Skip to content

ESP32 decision firmware 0.1.0 — all ESP32 chips, safe serial OTA (preview)

Pre-release
Pre-release

Choose a tag to compare

@ruvnet ruvnet released this 26 Sep 14:24
· 504 commits to main since this release

Preview firmware from PR #1018, which is not merged yet. It runs RuVector decision models on the ESP32 itself, with no cloud or Wi-Fi needed. You can update it over the USB serial port, and it rolls back automatically if an update fails its self-test.

Plain-language guide: https://gist.github.com/ruvnet/c994f474e94ce2efbf088b9b1406488d

Which file do I need?

Your chip First install (flash at 0x0) Later updates (OTA)
ESP32 (original) ruvector-decision-esp32-full-factory.bin ruvector-decision-esp32-full-ota.bin
ESP32-S2 …-esp32s2-full-factory.bin …-esp32s2-full-ota.bin
ESP32-S3 …-esp32s3-full-factory.bin …-esp32s3-full-ota.bin
ESP32-C2 (2 MB flash) …-esp32c2-full-factory.bin …-esp32c2-full-ota.bin
ESP32-C3 …-esp32c3-full-factory.bin …-esp32c3-full-ota.bin
ESP32-C6 …-esp32c6-full-factory.bin …-esp32c6-full-ota.bin
ESP32-H2 …-esp32h2-full-factory.bin …-esp32h2-full-ota.bin
ESP32-P4 …-esp32p4-full-factory.bin …-esp32p4-full-ota.bin

Each chip also has a production variant. It removes the profiling buffers, which frees 16 KB of RAM; inference, selftest, bench and energy all still work.

Install

pip install esptool
esptool.py --chip auto write_flash 0x0 ruvector-decision-esp32c6-full-factory.bin

You can also use the browser flasher at https://espressif.github.io/esptool-js/: pick the factory file and set the address to 0x0. Then open a serial terminal at 115200 baud and send meta. The reply should include "selftest_pass":true.

Update over serial (OTA)

python3 examples/esp32-decision/tools/ota.py --port /dev/ttyUSB0 ruvector-decision-esp32c6-production-ota.bin

This works on every chip, including the H2 and P4, which have no Wi-Fi. The board has two app slots. A new image is only kept if its model self-test passes after reboot; otherwise the board switches back to the previous image. The board rejects an image with the wrong SHA-256, an image built for another chip, and a transfer that stops partway.

What was verified (measured)

On a physical ESP32-C6 (CP210x, 160 MHz), using these exact images:

  • factory boot, self-test and bench: 174.5 µs per decision
  • OTA to the production image: committed, 21.6 s transfer
  • OTA of a deliberately broken image: rolled back automatically

Earlier on the same board, with images built from identical sources:

  • 1000/1000 decisions matched the Rust reference (INT16); INT8 matched 99.8%
  • An 11-round paired rig with the real occupancy model gave exact decisions in every round, and the PR kernel was 1.056× faster than baseline. That is below the ADR's 1.10× bar for claiming a speedup.
  • Six OTA rejection cases were each refused, with the board still running its previous image.
  • The production image has 16,384 more bytes of free heap.

All 16 images were built with espressif/idf:v5.4.4 from commit 88e5b13. The C6 is the only chip that has run on silicon. The ESP32, S2, S3, C2, C3, H2 and P4 images are cross-compiled only and have not been run on hardware. Evidence is in examples/esp32-decision/tests/evidence/ (ota-esp32c6-physical.json, rig-esp32c6-physical.json, release-builds.json) and in the ADR-007 follow-up section.

Limits

  • The bundled default model is the synthetic demo model. The occupancy model was trained on one office and is a benchmark, not a deployable occupancy detector.
  • Boards need a USB-UART bridge on UART0; most DevKitC boards have one. Boards that only have the chip's built-in USB-Serial/JTAG port are not supported yet.
  • OTA runs over the serial port only: there is no network OTA and no image signing. Anyone who can reach the port can already reflash the board through the ESP32's built-in serial bootloader.
  • Not measured yet: energy, S3 physical rounds, and real sensor capture.

Verify downloads with sha256sum -c SHA256SUMS.