Skip to content

Programming Guide

Daniel Frenkel edited this page Jun 22, 2026 · 2 revisions

Programming Guide

This page covers building the firmware from source and flashing it over a wire.

💡 Most people don't need this. Day-to-day firmware updates happen over the air from the web portal — see Firmware Update. Reach for wired flashing only for development, for the one-time OTA partition migration, or to recover a device that won't boot.

MiniSpeedCam has two microcontrollers, each with its own firmware project:

MCU Part Role Project
ESP32 ESP32-S3-WROOM-1 (8 MB flash, 8 MB PSRAM) Camera, WiFi, web portal, uploads firmware/platformio/esp32_firmware_platformio
STM32 STM32F301K8U6 (Cortex-M4F) Radar DSP — samples the CDM324 and reports a speed firmware/platformio/stm32_firmware_platformio

Both are built with PlatformIO (the VS Code extension, or the pio CLI). Install it first.


ESP32 firmware

Set the build token

The ESP firmware needs a bootstrap token supplied as an environment variable — the build fails on purpose if it's missing, so the secret is never committed to the repo. Set it before building:

# PowerShell
$env:MSC_BOOTSTRAP_TOKEN = "your-token-here"
# bash
export MSC_BOOTSTRAP_TOKEN=your-token-here

Build, flash, monitor

From the esp32_firmware_platformio folder:

pio run                 # compile
pio run -t upload       # flash over USB
pio device monitor      # serial console @ 115200 baud

The ESP32-S3 uses its on-chip native USB for the serial console, so you don't need a separate USB-to-serial adapter — the same USB cable used to flash carries the console.

Board / version notes

  • Defaults target an ESP32-S3-WROOM-1-N8R8 (8 MB flash, 8 MB OPI PSRAM). For a different module, adjust board_build.arduino.memory_type, board_upload.flash_size, etc. in platformio.ini.
  • FW_VERSION (a build flag in platformio.ini) is the version this image reports to the cloud and the OTA check. Bump it on every release so devices can tell a new build apart.
  • The API base URL defaults to the test environment; uncomment the live URL build flag in platformio.ini for production builds.

⚠️ One-time OTA partition migration

Over-the-air updates need a dual-app flash layout (two firmware slots, so a new image can be written to the inactive slot and switched on reboot). A unit flashed before OTA support used a single-slot layout and must be wired-flashed once to migrate onto the new table (partitions_8mb_ota.csv).

  • Survives the migration: WiFi credentials and saved settings (they live in NVS, which keeps its address).
  • Wiped by the migration: the SPIFFS data partition.

After this one wired flash, all future updates can be done over the air.


STM32 firmware

The STM32 is flashed over a USB-to-UART (CH340) adapter using the chip's built-in ROM serial bootloader (via stm32flash). The PlatformIO project is already configured for this (upload_protocol = serial).

Entering the bootloader

The STM32 only listens for a serial flash when it boots with BOOT0 held high. The exact procedure depends on your harness, but the pattern is:

  1. Connect the CH340 adapter to the STM32's debug UART.
  2. Hold BOOT0 high, then reset/power-cycle the STM32 so it boots into the ROM bootloader. (Unplug, hold BOOT0, replug.)
  3. Set upload_port in platformio.ini to your adapter's COM port (it varies by machine — check Device Manager / pio device list).

Build and flash

From the stm32_firmware_platformio folder:

pio run             # build -> .pio/build/genericSTM32F301K8/firmware.{elf,bin,hex}
pio run -t upload   # flash over the CH340 serial bootloader
pio run -t size     # report flash/RAM usage

After flashing, return BOOT0 to its normal (low) state and reset so the STM32 runs the application instead of the bootloader.

ST-Link / SWD is only needed for debugging; normal flashing uses the serial bootloader above.

Footprint

This firmware is tight on RAM — roughly ~45 KB / 64 KB flash and ~16 KB / 16 KB RAM. Keep that in mind before adding buffers.


How the two talk to each other

The ESP32 is the master: it resets the STM32, polls it for speed over a shared UART, and acts on the results (camera, uploads, sleep). The full electrical + protocol contract between the two — pinout, UART command set, checksummed reply format, the WiFi-TX radar-blanking handshake — is documented in INTERFACE.md in the firmware folder (firmware/platformio/INTERFACE.md). Read it before changing anything that crosses between the two chips.

Clone this wiki locally