Published on

Building DeviceOS firmware for a Photon to make a MQTT Home Assistant compatible BME680 sensor

Authors

As I was workign on my home assistant setup, I wanted to install some sensors to get some temperature sensor data around the house. I remembered I still have a couple of Particle Photons lying around from the days I built a wireless outside weather station. This is some old hardware, based on the STM32F2 series combined with a BCM43362 for wifi connections.

Combined with an Adafruit BME680 sensor that gives you temperature, humidity, pressure and a gas resistance reading in one I2C device, that should work fine with Home Assistant. The challenge was to have it be completely local without depending on the particle cloud.

As ESPHome doesnt run on the STM32 platform, and programming the firmware in Rust would get stuck on the missing drivers for the BCM chip (something with loading a licensed proprietary blob image, amongst other challenges), Claude and I decided we'd stick with the original DeviceOS and push the sensor data to a local MQTT broker before consuming that in Home Assistant.

wiring

BME680Photon
3V33V3
GNDGND
SDID0 (SDA)
SCKD1 (SCL)

The default I2C address for the BME680 is 0x77

building firmware without the web IDE

The Photon is no longer supported by the current Particle toolchain, but the docker buildpack images are still hosted on docker hub. Device OS 2.3.1 is the last LTS line that supports the old Gen 2 hardware, so 2.3.1-photon is the tag we used.

docker run --rm --platform linux/amd64 \
  -v "$PWD":/input \
  -v "$PWD/target":/output \
  -e PLATFORM_ID=6 \
  particle/buildpack-particle-firmware:2.3.1-photon

This builds the firmware to target/firmware.bin. The MQTT client and the official Bosch BME68x driver were vendored into lib/ so the buildpack picks them up without a library manager.

flashing an old photon

The firmware needs Device OS 2.3.x flashed to the device. You can update it once over USB, then flash locally over DFU. Put the device in DFU mode by holding SETUP, tapping RESET and releasing SETUP when the LED blinks yellow.

npm install -g particle-cli
particle update
particle flash --local target/firmware.bin

home assistant discovery

Rather than defining the sensors in configuration.yaml, the firmware publishes retained MQTT discovery configs on every connect, so home assistant can detect the sensors automatically

homeassistant/sensor/photon_bme680_<deviceid>/<sensor>/config

The state is sent as JSON to particle/photon_bme680_<deviceid>/state every 30 seconds, availability to .../status

debugging mosquitto

We used the following command on the mosquitto broker to see if messages were arriving

mosquitto_sub -h <broker> -v -t 'particle/#' -t 'homeassistant/#'

adding robustness

After witnessing a couple of times that the temperature sensor data was n't submitted anymore, claude helped built some extra robustness into the readouts.

We first made the bus force a re-init if data wasn't read correctly:

if (!sensorRead(&data)) {
    Log.warn("BME680 read failed; resetting I2C");
    Wire.reset();   // Device OS I2C bus recovery
    bmeOk = false;  // force sensorInit() next loop
    return;
}

... and added ApplicationWatchdog that reboots the device if the loop stops checking in for 60 seconds.

void onWatchdogTimeout() {
    System.reset();
}

ApplicationWatchdog watchdog(WATCHDOG_TIMEOUT_MS, onWatchdogTimeout, 1536);

// ... and at the end of loop():
ApplicationWatchdog::checkin();

The sensor has been chugging along happily for weeks!

The resulting project can be found on peterpeerdeman/deviceos-bme680.

Support Hashbang, keep in touch 💌