I had a few goals with this project:
- Have a basic monitor with temperature, humidity, CO2 etc. where data could easily be read by other devices on my network
- Experiment with TDD on embedded platforms.
- Build good habits for TDDing CircuitPython projects for later.
- Data exported over the network. Easily used by Prometheus to populate a Grafana dashboard, and also have a JSON (or similar) endpoint that could be used by other devices.
- root contains readme, scripts and other common things
- code: contains the CircuitPython code as well as unit tests
To be able to design the logic before I got the sensors, I decided to dual-target the unit tests. The two targets was my computer, as well as the device itself. This means abstracting away hardware dependencies to make developing the logic locally possible. The local logic included the report formatters, as well as a wrapper for getting sensor information. To run this, you can navigate to the code
directory and run test_main.py
. To people unfamiliar with test driven development, this might seem overkill. On the other hand, it makes changing sensors etc. easy without affecting the underlying logic.
The following sensors are used in my implementation:
- BME688 (Adafruit)
- SGP30 (Adafruit)
See them used in the drawing below:
(the picture is of a regular Pico with headers, as I couldn’t find a Pico W Fritzing part. Just imagine that it is the Pico W. They are pin compatible after all. Also the BME680 is pin compatible with the BME688 which I used, and they also use the same libraries)
NOTE: The gray wire is partially occluded behind the purple and white ones! It goes from the SCL on the SGP30 to the GP3 on the Pico (the one right below the purple wire on GP2. See the Pico W data sheet if you are unsure about the pin layout.
My setup looks like this (don’t judge my lack of soldering skills too hard, it works):
To run Python code on the Raspberry Pi Pico W, CircuitPython is used. This should be downloaded and installed on your Pico W before any of the below code can be run.
To run the unit tests on the device, a simple “polyfill” for unit test functionality is needed. Fortunately, the user mytechnotalent made an awesome polyfill for Pythons built-unit unittest, which makes it easy to run the same tests on device and on a development system. Simply copy unittest.py to the device, and we are ready to go!
While working on the unit tests, I really just copied over the related files and the tests (including the testmain test_main.py
). This is automated in the script called deploy_test.sh
, with the path to the CircuitPython volume as input (e.g, /Volumes/CIRCUITPY
on my Mac).
Remember that the necessary libraries need to be in place for the sensors and web server functionality to work. I use CircUp to handle this. The following libraries is needed:
- adafruit_httpserver
- adafruit_sgp30
- adafruit_bme680
To run the main program, you should set up a settings.toml
file with network information and copy to the Pico. This file will look like this:
WIFI_SSID = "myssid"
WIFI_PASSWORD = "mypass"
(replace myssid with your routers SSID and mypass with your routers WPA/WPA2 password)
After this file is transferred, the main code files from the code
directory should be transferred. This is done through the script deploy_main.sh
, with the path to the CircuitPython volume as input (e.g, /Volumes/CIRCUITPY
on my Mac).
The code provides the following endpoints:
- /environment : Prometheus endpoint for metrics.
- /environment.json : The data in json-format.
Temperature, humidity and eCO2 is the data provided.
The Prometheus endpoint can easily be read by Prometheus to get the temperature, humidity and eCO2 data. These can then be read to create dashboards for something like Grafana. A simple Prometheus scrape config I have used is the following:
- job_name: 'env_monitor'
scrape_interval: 5s
metrics_path: "/environment"
static_configs:
- targets: ["10.0.0.7"]
(you might have to change the ip of your Raspberry Pi Pico W on your local network. Also, feel free to change the scrape interval. I think I will use something like 30s once I get a more permanent Kubernetes cluster up and running in my home-lab)