Firmware Architecture & Algorithms¶
The firmware implementations in 48broadbent/broadbent incorporate sophisticated on-device processing, sensor compensation algorithms, and local graphics routines. This page breaks down the core patterns used across the fleet.
1. MQTT Weather Forecast JSON Parsing¶
Nodes with e-paper screens (pluto, venus, alichino, etc.) subscribe to an MQTT topic sensor/weather/forecast that broadcasts 3-day weather forecasts generated by Home Assistant.
An inline C++ lambda parses the JSON array and stores the days, temperatures, rain probabilities, and Unicode weather icons directly into ESPHome vector globals:
mqtt:
broker: !secret mqtt_broker
discovery: false
on_json_message:
- topic: sensor/weather/forecast
then:
- lambda: |-
int i = 0;
for (auto k: id(keys)) {
if (!x[k].isNull()) {
id(days)[i] = x[k]["day"].as<const char *>();
id(Ts)[i] = x[k]["nT"]; // Next-day temperature
id(pp)[i] = x[k]["pP"]; // Precipitation probability
id(wind)[i] = x[k]["wind"]; // Wind speed
id(code)[i] = x[k]["condition"]; // Weather condition index
i++;
}
}
2. Hypsometric Sea-Level Pressure Compensation¶
Barometric pressure measured by the BME280 or BME680 reflects the raw station pressure at the local elevation (~35 meters above sea level). To display standard meteorologic Mean Sea Level Pressure (MSLP), the firmware calculates:
[ P_{\text{sea}} = \frac{P_{\text{station}}}{\left(1 - \frac{L \cdot h}{T_0}\right)^{\frac{g \cdot M}{R_0 \cdot L}}} ]
For an altitude of exactly 35 meters, this yields a pre-calculated standard divisor of 0.995858 (or 0.996212 for 32 meters):
sensor:
- platform: template
name: "${sensor} ${devicename} Sea Level Pressure"
id: sea_level_pressure
unit_of_measurement: "hPa"
accuracy_decimals: 1
icon: "mdi:weather-partly-cloudy"
lambda: |-
if (id(my_pres).has_state()) {
// Pre-calculated standard hypsometric divisor for exactly 35 meters
return id(my_pres).state / 0.995858;
} else {
return 0.0;
}
3. Sensirion SGP40 Dynamic Compensation¶
The Sensirion SGP40 metal-oxide gas sensor calculates the VOC Index (a standardized 1 to 500 scale where 100 represents typical indoor clean air). The raw sensor resistance is sensitive to ambient temperature and absolute humidity.
The firmware routes live readings from the adjacent BME280 sensor directly into the SGP40 compensation engine:
sensor:
- platform: sgp4x
address: 0x59
i2c_id: bus_b
update_interval: 300s
voc_index:
name: "${sensor_airq2} ${devicename} VOC Index"
id: my_voc
accuracy_decimals: 1
filters:
- or:
- throttle: 300s
- delta: 2
compensation:
temperature_source: my_temp
humidity_source: my_hum
4. Optical Dust Sensor UART Processing¶
In dust-front.yaml, dust-kitchen.yaml, and dust-up.yaml, fine particulate matter (PM2.5) is measured via optical light scattering using the PM1006 sensor communicating over UART (9600 baud):
uart:
rx_pin: D2
baud_rate: 9600
sensor:
- platform: pm1006
pm_2_5:
id: my_pm25
name: ${sensor} ${devicename} concentration
internal: false
filters:
# Throttle telemetry to reduce Wi-Fi radio wakeups unless values shift
- or:
- throttle: 600s
- delta: 1
5. M5Stack Atom Matrix 5×5 LED Scrolling Display¶
In atom_matrix.yaml (named gutenberg), the firmware controls a miniature 5×5 RGB addressable LED array running on an ESP32-PICO:
- Reads temperature and humidity from an onboard Sensirion SHT30 sensor (
I2C address: 0x44). - Includes custom C++ driver logic (
include/my_matrixled_scrolling_display.h) to scroll multi-digit temperature values across the 5×5 matrix with smooth color gradients. - Automatically flips display brightness down during evening hours to maintain subtle room ambient lighting.