Sourdough monitoring system with an ESP32, a time-of-flight distance sensor and temperature/humidity sensors.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
We use the ESP32-S2 with BME280.
Attached to it are the following devices:
- TMF8821 TimeOfFlight distance sensor on i2c address
0x41
. Link - ThinkInk 2.9" grayscale e-Ink display with 296x128 pixels and four gray scales . Product , Pinouts , Button Pinout , FeatherWing Pinout
- BME280 on-board temperature, humidity, pressure and altitude sensor on i2c address
0x77
. Manual , Product - AM2320 external temperature and humidity sensor on i2c address
0x5c
. Datasheet , Product - Zio OLED [only for debugging] 1.5" display with 128x128 pixel for debugging on i2c address
0x3c
. Link - U132 [only for debugging] passive buzzer for signalling on a GPIO pin . Schematic , Product
- LC709203 on-board LiPo battery monitor on i2c address
0x0b
. Datasheet , Product
For the other on-board peripheries, the guide here provides tutorials. Also, the schematic can be downloaded here
We work with Python 3.9, specifically CircuitPython 7.3.3.
We work with PyCharm CircuitPython, a guide to setting it up to work with circuitpythyon can be found [here]
(https://learn.adafruit.com/welcome-to-circuitpython/pycharm-and-circuitpython).
Since CircuitPython 7.3.3 is forked from the latest MicroPython repository which
uses [Python <= 3.9 features] (https://docs.micropython.org/en/latest/genrst/index.html), we create a virtual
environment with Python 3.9 and install the circuitpython-stubs
as well as the
Serial Port Monitor plugin.
The CircuitPython libraries we use are the following (can be installed on the local venv
as well to enhance code
completion):
adafruit-circuitpython-am2320
for AM2320 (temp sensor) . Exampleadafruit-circuitpython-bme280
for BME280 (environment sensor) . Exampleadafruit-circuitpython-lc709203f
for LC709203 (battery monitor) . Exampleadafruit-circuitpython-il0373
for IL0373 (e-Ink display) . Example- `` for TMF8821 (ToF sensor). Example
adafruit-circuitpython-ssd1327
for SSD1327. Exampleadafruit-circuitpython-simpleio
for PWMadafruit-circuitpython-ticks
for time measurements in TMF882X driveradafruit-circuitpython-typing
for datatype description in TMF882X drivercircuitpython-displayio-cartesian
for plotting on the display
We work with a bunch of pre-existing libraries for use of the periphery devices, most of them from here). They are copied on the CIRCUITPYTHON volume and reside in the CIRCUITPYTHON/lib
folder.
Note that there was no Python library for the TMF882X device family. Thus, we developed our own library, accessible in CIRCUITPYTHON/lib/tmf8821
.
The sensors and display updates happen every 4 minutes and the monitor goes to deep sleep mode between the updates. Whenever a button is pressed, the next sleep time is 1.5x the usual interval to account for interruption of an average of 0.5x the sleep time.
The battery lasts about 2-3 weeks. If the monitor is in the refrigerator (the external temperature sensor reads less than 10°C), the monitor is in power-safe mode and the sensors and display updates only happen every 9 minutes.
Holding a button means pressing it for at least 3.5 seconds, or until the blue LED on the ESP32 PCB light up.
The recorded curves on the MicroSD card can be plotted using the scripts in release_tests/v1.1
.
Some of the growth curves look like this:
Upon connecting an ESP32 with native USB to the computer, it's flash memory will be mounted as a thumb drive. Now the content of the folder CIRCUITPYTHON
has to be copied to this thumb drive.
For a new hardware setup, the cross talk of the TMF8821 should be calibrated to guarantee the best possible accuracy. This can be done using the script in experiments/distance/code4.py
(uncomment line 44). The calibration data must then be written in byte format to a file named "<config_spad_map>_<active_range>" (e.g. "3x3_normal_mode_short") in CIRCUITPYTHON/calibration
.
If there is always a container with the same height being used, it makes sense to only calibrate the floor once and write the distance in millimeters (just the number, no unit) to a file "floor.txt" in CIRCUITPYTHON/calibration
.
The ESP32 tries to access the Wi-Fi and push relevant metrics to an InfluxDB bucket every time it wakes up. The following metrics are sent:
height
: Growth percentage (relative to calibrated start position)height_std
: Standard deviation in mm of last measured distance (not relative to percentage yet)floor_calib
: calibrated floor distance in mmstart_calib
: calibrated start distance in mmtemp_in
: Temperature in rise chamber in °Ctemp_out
: Temperature in environment air in °Chum_in
: Humidity in rise chamberhum_out
: Humidity in environment airwifi_rssi
: RSSI of Wi-Fiwake_reason
: Reason for wakeup after deep-sleep: "unknown", "reset", "left" (button) or "middle" (button)battery_level
: Battery charge percentage
A list of Wi-Fi configurations can be stored in CIRCUITPYTHON/metric_telemetry/secrets.py
as a tuple of (SSID, Password)
. The Wi-Fi configurations are tried in the given order and the first working one is tried first the next time the ESP32 wakes up (the according access point channel is also cached to persistent memory and tried first next time).
Note that the CircuitPython 7.3.3 requests
module throws a Runtime error during the SSL handshake for some pages (see the issue here), so the complete CA chain of the POST request target URL must be downloaded:
- Find out the cluster URL of the targets InfluxDB cloud instance (see below)
- Download all certificates in the CA chain using this bash command using the above URL
- Open all the certificates and concatenate them in one file
- Place the file in
CIRCUITPYTHON/metric_telemetry/all_certs.pem
.
In the same file as the Wi-Fi configurations, CIRCUITPYTHON/metric_telemetry/secrets.py
, the information about the InfluxDB are stored. To this end, create a new bucket on InfluxDB Cloud and fill in following values in the secrets file:
INFLUXDB_URL_WRITE
: Using as prefix the Cluster URL (Host Name) from the Organization SettingsINFLUXDB_ORG
: The Organization's nameINFLUXDB_BUCKET
: The Bucket's nameINFLUXDB_API_TOKEN
: A token with write access to the bucket
A template file can be found at CIRCUITPYTHON/metric_telemetry/secrets_template.py
.
Then also adjust the measurement's name (INFLUXDB_MEASUREMENT
) and the device's name (DEVICE_NAME
) in CIRCUITPYTHON/code.py
.
Note that the free InfluxDB Cloud tier is enough to get started, the only relevant limitation is a data retention period of maximum 30 days.
The metrics can be visualized by e.g. Grafana. An example dashboard is visualized above and the according JSON model can be found in telemetry/grafana_dashboard_model.json
using a free Grafana cloud setup.
If an exception occurred, the CircuitPython OS will flash the red LED on the ESP32 PCB 2x every 5 second. In this case, the error message and traceback has been stored to the SD card in a file called "exception_traceback.txt". Moreover, if the USB-C cable is connected, debug messages are printed over the virtual serial port.
We don't want to use Button C on the eInk display since pulling it high would result in a constant current through the onboard LED.
- https://grafana.com/blog/2020/06/17/how-to-monitor-a-sourdough-starter-with-grafana/
- https://www.theverge.com/2021/3/19/22340817/breadwinner-smart-sourdough-starter-tracking-wifi-gadget-bread-yeast
- https://www.justinmklam.com/posts/2021/02/levain-monitor/
- https://www.twilio.com/blog/how-to-data-tracker-sourdough-starter?utm_campaign=sourdio-site&utm_medium=site&utm_source=sourdio
- https://github.com/Fsned/sourSmart
- https://hi-hi-hi.com/food/projects/2021/06/18/jar.html