Thermometer adventure continuous with some progress over the last weekend. Little bit of refactoring, rewiring, X.org, matchbox, Piston, … and some ugly (but working!) UI 🙂
My inside DS18B20 thermometers have three wires. But I managed to buy the outside one with two wires only. Had to rewire everything to parasite power mode …
… and add pullup=1 to RESIN_HOST_CONFIG_dtoverlay (w1-gpio,pullup=1). As a bonus, it’s much simpler and nicer.
BTW I’m not very good at these things, maybe, it can be wired without parasite power mode somehow, don’t know.
Here’s the short list of important changes I made. Not all of them are covered.
Hard coded thermometer device identifiers removed and config module introduced. Also decided to use two thermometers instead of three - one for inside temperature and second one for outside temperature. Thermometer can be configured via environment variables or command line arguments. Issue following command to see the complete list of them:
cargo run -- --help
Version 0.0.2 gives you following output:
thermometer 0.0.2 Raspberry Pi Thermometer USAGE: thermometer --inside-thermometer <INSIDE_THERMOMETER> --max-fps <MAX_FPS> --outside-thermometer <OUTSIDE_THERMOMETER> --temperature-interval <TEMPERATURE_INTERVAL> --temperature-units <TEMPERATURE_UNITS> FLAGS: -h, --help Prints help information -V, --version Prints version information OPTIONS: --inside-thermometer <INSIDE_THERMOMETER> Inside W1 thermometer device ID [env: INSIDE_THERMOMETER=] [default: 28-000009e8f6e7] --max-fps <MAX_FPS> Max frames per second [env: MAX_FPS=] [default: 2] --outside-thermometer <OUTSIDE_THERMOMETER> Outside W1 thermometer device ID [env: OUTSIDE_THERMOMETER=] [default: 28-000009d4dffc] --temperature-interval <TEMPERATURE_INTERVAL> Interval in which temperatures are read from sensors (ms) [env: TEMPERATURE_INTERVAL=] [default: 500] --temperature-units <TEMPERATURE_UNITS> Temperature units [env: TEMPERATURE_UNITS=] [default: celsius] [possible values: celsius, fahrenheit]
Each device in the fleet can have different configuration now:
TIMEZONE environment variable is not related to the Rust thermometer application. But it’s important and will be explained later in this post.
export RESIN_MACHINE_NAME=raspberrypi3 export RESIN_MACHINE_TARGET=armv7-unknown-linux-gnueabihf
Do you have different device? Just modify these values and local-push.sh should work for you. Other changes:
- removed resin-sync.yml and moved the before part to the local-push.sh (-b argument) script,
- removed hard coded DEVICE,
- run local-push.sh without arguments to discover devices to deploy to,
- run local-push.sh with device name (first argument) to deploy to specific device.
Everything should be covered in the documentation.
Temperature parsing was moved to the ds18b20.rs module. It’s DS18B20 specific. Couple of other changes like temperature units added, from / to string, etc.
Exhaustive documentation is in the code itself. Frankly, it’s over documented, but should help people new to the Rust language and Piston.
Kiosk mode and launch scripts
Decided to go with X.org kiosk mode and took inspiration from the electron-rpi-quick-start repository. It means couple of new packages in the Dockerfile and new launch script. Launch script configures time zone, disables DPMS, screen blanking, launches matchbox window manager and our thermometer.
Remember TIMEZONE from the device configuration section? We want our application to display correct date & time (local one, not the UTC).
My Raspberry Pi 3 B+ is standing on the desk, case is upside down and the only thing I had to do was to set RESIN_HOST_CONFIG_lcd_rotate variable to 2 (to rotate it upside down).
Couple of graphics libraries do exist for the Rust language. I prefer piston_window for simple applications. If you’re new to the Piston, don’t look to other crates, just stick with the piston_window. It’s more than enough and you’ll not be puzzled. Trust me 🙂
As I already said, code is extensively documented, so, I’m not going to cover it in detail. Just some organization notes.
- src/w1 - W1 related stuff (devices - thermometers)
- src/app - UI application based on the piston_window crate
- src/state.rs - global application state (just temperatures for now)
- src/config.rs - application configuration
- src/temperature.rs - application temperature readers threads
- src/main.rs - entry point which spawns temperature readers threads and launches thermometer UI
You can try to run the thermometer application on your computer. It works, but you’ll see N/A instead of temperature values. Sensors are connected to the Raspberry Pi, right? Rust thermometer contains feature simulate-temperature, which can be enabled in this way:
cargo run --features simulate-temperature
It simulates temperatures on local machine, so, we can play with the thermometer on computer as well. Here’re the differences:
- Temperature reader for Raspberry Pi
#[cfg(not(feature = "simulate-temperature"))]
- Temperature reader for local development
#[cfg(feature = "simulate-temperature")]
Read The Manifest Format to learn more about features.
Honestly, I thought it will be much harder task to make it working on Raspberry Pi & balenaOS. But it was piece of cake. Little bit of Googling, checking sample projects and connecting dots together.
Thermometer on Raspberry Pi. Yeah, it’s a mug with coffee. Was testing if the sensor works 🙂 Don’t worry, it’s the outside sensor = waterproof.
And thermometer running on macOS with simulate-temperature feature enabled.
Pretty ugly UI, but good enough for now. We’ve just tested that it works and we can work on some nice UI later.
What’s next? Open / closed terrace door check and I’ll probably replace synchronous temperature readers with some asynchronous ones. We don’t need separate thread per sensor.