fliegendewurst.eu/content/blog/raspberry-pi-temperature-monitoring.md
2023-11-11 19:23:56 +01:00

141 lines
7.7 KiB
Markdown

+++
title = "Raspberry Pi 0 W + sensors + OLED display = temperature monitoring and calendar"
date = 2022-10-05
updated = 2023-11-11
+++
<img id="hero-img" alt="OLED display showing temperature and upcoming appointments, connected to a Raspberry Pi" src="/assets/raspberry-pi-temperature-monitoring.jpg">
With this project, I monitor the temperature and humidity in my room.
Measurements are taken every 10 minutes and saved in an SQLite database.
Additionally, the display shows the upcoming appointments of the next five days: each of the 24 rows represents one hour and each pixel in a row represents 5 minutes.
In the middle, the time until the next appointment, the relative humidity and the current temperature are shown.
Below that, a graph shows the temperature of the last two days.
When activating another display mode (not active in the picture), this area of the display instead shows the next seven appointments.
### Hardware
<ul>
<li>computer: <a href="https://www.raspberrypi.com/products/raspberry-pi-zero-w/">Raspberry Pi 0 W</a></li>
<li>temperature and humidity sensor: AM2302 (wired DHT22)</li>
<li>OLED display: 1.5 inch SSD1351 (<a href="https://www.waveshare.com/wiki/1.5inch_RGB_OLED_Module">Waveshare</a>)</li>
<li>yellow push switch: TRU Components PBS-18B 701912</li>
</ul>
The components are connected to the Pi as indicated in the wiring diagram below (made using [Circuit Diagram](https://www.circuit-diagram.org/)).
<embed
class="white-svg"
type="image/svg+xml"
src="/assets/circuit.svg"
title="Wiring diagram">
Wiring of the SSD1351 to the Pi (<a href="https://pinout.xyz/pinout/spi">pinout</a>):
1. VCC ↦ 3.3V
1. GND ↦ GND
1. DIN ↦ SPI0 MOSI (GPIO 10)
1. CLK ↦ SPI0 SCLK (GPIO 11)
1. CS ↦ SPI0 CE0 (GPIO 8)
1. DC ↦ GPIO 25
1. RST ↦ GPIO 27
Wiring of the AM2302 to the RPi:
1. VCC ↦ 5V
1. second pin ↦ GPIO 26
1. third pin ↦ (unused)
1. GND ↦ GND
Wiring of the push switch to the Pi: one pin to GPIO 19, the other to 3.3V.
### Software
The Raspberry Pi 0 W is running <a href="https://www.raspberrypi.com/software/">Raspberry Pi OS</a> 11 (bullseye).
It is advisable to enable SSH and configure a WLAN network before booting the RPi for the first time.
As explained in many tutorials online, this boils down to modifying the SD card image.
Create an empty file <span class="code">/boot/ssh</span> to enable the SSH daemon, and configure the credentials of your WLAN network in <span class="code">/etc/wpa_supplicant/wpa_supplicant.conf</span>.
<a href="https://valh.io/p/raspberry-pi-configure-wlan/wifi--ssh-before-first-boot/">This blog post</a> describes how to set up the initial user and password.
Make sure to set <span class="code">dtparam=spi=on</span> in your <span class="code">/boot/config.txt</span>, otherwise the SPI connection to the OLED display will not work.
All of the following software is written in <a href="https://www.rust-lang.org/">Rust</a> and available <a href="https://github.com/FliegendeWurst/raspi-oled/">on GitHub</a>.
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/take_measurement.rs">take_measurements</a> requests the current temperature and humidity from the sensor.
To mitigate transmission errors, the median of five readings is used.
The final reading is saved in the specified SQLite database (mine is named sensors.db) along with the current time.
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/refresh_json.rs">refresh_json</a> downloads the latest calendar entries from my <a href="https://github.com/zadam/trilium/"> Trilium Notes</a> installation and saves them in a JSON file (events.json).
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/status_check_example.rs">status_check</a> gathers some miscellaneus status information on my other computer systems. The provided example just checks whether GitHub and GitLab are up.
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/display_all.rs">display_all</a> processes all of this data and displays it on the OLED.
It is run by another Python script (<a href="https://gist.github.com/FliegendeWurst/087916a7635c2690de609366517a4ae7">turn_on.py</a>) whenever the push switch is pressed.
Below is the crontab of the pi user. status_check is run every five minutes. take_measurement is run every ten minutes. Fifteen minutes into the hour, any new calendar entries are synced using refresh_json. To avoid OLED burn-in, the display is turned off every minute (if it was on previously).
```
*/5 * * * * /home/pi/status_check /home/pi/sensors.db 2>/dev/null >/dev/null
*/10 * * * * /home/pi/take_measurement /home/pi/sensors.db
15 * * * * /home/pi/refresh_json --no-weekly
* * * * * /home/pi/display_off off
```
For performance reasons (and because the Pi doesn't have enough RAM), I cross-compile these programs using <a href="https://nixos.org/">Nix</a>.
I'm certain there's a better way to do it, but the following method works for my purposes:
in my <a href="https://github.com/FliegendeWurst/nur-packages/">NUR packages</a>, run the following command to cross-compile all binaries: <span class="code">NIXPKGS_ALLOW_BROKEN=1 nix-build -A raspi-oled-cross</span>.
(Btw, this will first compile LLVM and rustc for some reason. There must be a way to avoid that...)
As always, <span class="code">./result</span> is a symlink to the build artifacts.
The binaries (and the libraries they depend on) may be copied to the Pi like this:
```
mkdir /tmp/nixstore
nix copy --extra-experimental-features nix-command flakes --no-check-sigs --to /tmp/nixstore $(readlink -f result)
rsync -r --links --info=progress /tmp/nixstore/nix pi@himbeere-null:~/
```
On the RPi itself, the binary needs to be adapted to Raspbian and placed in the proper position:
```
patchelf --set-interpreter /lib/ld-musl-armhf.so.1 nix/store/*-raspi-oled-*/bin/display_all
sudo mv nix /
```
### Enclosure
<img class="content-img-tall" alt="3D model of the case" src="/assets/raspberry-pi-case.png">
To make the whole setup look a bit nicer, I designed a casing using [OpenSCAD](https://openscad.org/).
The image on the right shows the final result as rendered by OpenSCAD ([.scad](/assets/RPi-Zero.scad)).
[Raspberry Pi 0 W - Slim Case](https://www.thingiverse.com/thing:4199739) (by OhSnap) proved to be a great base to build on.
I added some supports around it to ensure it won't topple over as easily.
Since I was too lazy to measure the dimensions of my OLED display, I extracted the relevant part of [Zoid, the Trapezoidal Under Cabinet 1.5" OLED Display](https://www.thingiverse.com/thing:3397089) (by rhattie).
Feeling a little bit fancy, I decided to add some inset icons underneath.
The pin holes will be useful to add some status LEDs later.
Because one button will soon not be enough control, I added three button holders on the top.
A library near me offers a 3D printing service so it was easy to get the case printed.
The new buttons and LEDs will need to be wired up to some unused GPIO pins.
All LEDs are connected using a single resistor since these will only turn on rarely and even more rarely at the same time.
<embed
class="white-svg"
type="image/svg+xml"
src="/assets/circuit_new.svg"
title="Wiring diagram">
To make full use of the three buttons, I came up with a simple one/two-step selection menu to get to the most important functionality.
1. Show time table and large clock
1. (unused)
2. Show temperature chart
3. Show upcoming events
2. Dismiss active action
3. Show TOTP codes
1. Next page
2. (unused)
3. Show RPi screensaver
This post will be updated soon™ to show to the final 3D print (which required some adjustments not yet mentioned).