This is another version of the Heating Brain hobby project - an Elixir application for controlling the floor heating system in the house. It utilizes a Raspberry PI Zero, a relay controller, and a few 1-wire temperature sensors. The original Elixir application ran for over three years without any issues (except for CPU and memory utilization when rendering data). It provided around 20-30% savings in gas consumption compared to the previous "industry standard" devices (mainly due to optimized logic for enabling and disabling heating in the context of the required heat area).
Disclaimer: This is a hobby project, and I do not take any responsibility for any issues or damages that may arise from running this software. Use it at your own risk.
Install tools required by the .tool-versions
file and ensure you have the following system packages installed on your macOS:
brew install arm-unknown-linux-musleabihf
rustup target add arm-unknown-linux-musleabihf
See src/controller.rs
and src/scheduler.rs
for the main logic of the heating system. The controller is responsible for turning the heating on and off based on the temperature readings from the sensors and the expected temperature (according to the heating_config.json
file). The scheduler is a cron-like worker that reads temperatures and calls the controller for valves and the stove. You may want to adjust constants in those files (e.g., the minimal floor heating area that is open before turning on the stove).
There is a bin/deploy
script that builds the binary file and performs actions on the remote server (e.g., backing up the database, updating the systemd service, etc.). Make sure to review the heating configuration (e.g., GPIO pins, sensor identifiers, etc.).
If deploying for the first time, you will have to set up the server (see Raspberry Pi Zero Setup section) and create heating_config.json
(see heating_config.json.sample
).
You can access all data by hitting the API endpoints with cURL. The host name depends on your server configuration. Also, keep in mind that the scheduler needs to populate some data before you will see any results (prior to that, it will return HTTP 404).
neuroheat λ curl neuroheat.local:3030/api/temperatures/office | jq
{
"timestamp": "2024-10-06 10:32:03",
"temperature": "21.937",
"key": "office",
"label": "Office",
"expected_temperature": "21"
}
neuroheat λ curl neuroheat.local:3030/api/state | jq
{
"living_room": {
"label": "Living Room",
"timestamp": "2024-10-06 11:16:03",
"heating_enabled": "false",
"temperature": "22.5",
"expected_temperature": "21"
},
"pipe": {
"label": "Heating Pipe",
"timestamp": "2024-10-06 11:16:01",
"temperature": "30.375"
},
"bathroom": {
"label": "Bathroom",
"timestamp": "2024-10-06 11:16:02",
"temperature": "21.625",
"expected_temperature": "21",
"heating_enabled": "false"
},
"office": {
"temperature": "22.312",
"timestamp": "2024-10-06 11:16:03",
"label": "Office",
"expected_temperature": "21",
"heating_enabled": "false"
},
"bedroom": {
"label": "Bedroom",
"expected_temperature": "18.5",
"timestamp": "2024-10-06 11:16:04",
"temperature": "20",
"heating_enabled": "false"
},
"guest_room": {
"label": "Guest Room",
"timestamp": "2024-10-06 11:16:05",
"temperature": "19.625",
"expected_temperature": "18.5",
"heating_enabled": "false"
},
"stove": {
"label": "Stove",
"heating_enabled": "false",
"timestamp": "2024-10-06 11:16:05"
}
}
Install sudo apt install -y sqlite
and run:
sqlite3 /srv/neuroheat/neuroheat.db
journalctl --unit neuroheat.service --lines=50 --follow
This is a hobby project, so it does not implement any migration mechanism (yet?). If there are changes in the schema (i.e., src/repo.rs
's init function), you may need to re-initialize the database:
-
Stop the service on the server:
sudo systemctl stop neuroheat.service
-
Remove the database on the server:
sudo rm /srv/neuroheat/neuroheat.db
-
Let the application initialize it from scratch on startup:
./bin/deploy
-
Install Raspbian Buster Lite on the memory card. See official documentation for details.
-
Copy
server/config.txt
onto the card (asconfig.txt
). -
Configure the network connection by creating a
wpa_supplicant.conf
file on the card with the following content (adjust the credentials):country=PL ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 network={ ssid="[wifi-name]" psk="[wifi-password]" key_mgmt=WPA-PSK }
-
Enable SSH by creating an
ssh
file.
-
Allow password-less SSH connections:
ssh-copy-id pi@[ip]
-
sudo sed -i 's/raspberrypi/[neuroheat or any other hostname]/g' /etc/hostname sudo sed -i 's/raspberrypi/[neuroheat or any other hostname]/g' /etc/hosts
-
Fix the
cannot change locale (en_US.UTF-8)
issue:sudo sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen sudo locale-gen en_US.UTF-8 sudo update-locale en_US.UTF-8
-
Set the new password:
sudo passwd pi
-
Upgrade the distro:
sudo apt-get update sudo apt-get dist-upgrade sudo apt-get autoclean
-
Set the local timezone:
sudo timedatectl set-timezone Europe/Warsaw
-
Configure rsyslog (optional):
# /etc/rsyslog.d/01-ignore-rngd.conf if $programname == 'rngd' then /var/log/rngd.log & stop if $programname == 'rng-tools' then /var/log/rngd.log & stop # /etc/rsyslog.d/02-cron.conf if $programname == 'cron' then /var/log/cron.log & stop # /etc/rsyslog.d/99-nas.conf use local address like "rsyslog.local" *.* @[ip address of log server]:514 $ActionQueueFileName queue $ActionQueueMaxDiskSpace 1g $ActionQueueSaveOnShutdown on $ActionQueueType LinkedList $ActionResumeRetryCount -1 # /etc/logrotate.d/rsyslog /var/log/rngd.log { rotate 4 weekly missingok notifempty compress delaycompress sharedscripts postrotate /usr/lib/rsyslog/rsyslog-rotate endscript
-
Create application directories:
mkdir -p /srv/backups /srv/neuroheat /opt/neuroheat/bin chown -R pi:pi /opt/neuroheat/ /srv/backups/
-
Configure backups:
# Add in crontab: 0 * * * * cp /srv/neuroheat/neuroheat.db /srv/backups/neuroheat.db.$(date +'%Y.%m.%d.%H.%M').bak 15 10 * * * find /srv/backups -type f -mtime +14 -ls -exec rm -f -- {} \;
-
Install
jq
forserver/bin
helper scripts:sudo apt-get install jq
-
Deploy the code:
./bin/deploy