Files
remote-rig/docs/design/esp01s-uart-ota.md
T
Joshua King 403e1d9edd
Build (Dev) / build (push) Failing after 9s
CI/CD / lint-and-typecheck (push) Successful in 9m28s
CI/CD / test (push) Successful in 9m27s
CI/CD / build (push) Failing after 4m53s
CI/CD / deploy (push) Has been skipped
firmware: no-reflash config updates for ESP-01S + UART-OTA groundwork
Updating the buried ESP-01S currently means a USB-UART adapter and a
GPIO0 jumper. Add a path to change its settings without reflashing, and
lay the groundwork for full firmware updates over the existing UART.

set_config (no reflash for settings):
- ESP-01S: add saveConfig() + a set_config command — updates GoPro
  SSID/password/IP and poll interval, persists to LittleFS, acks, and
  re-associates Wi-Fi if creds changed
- XIAO: forward an MQTT set_camera_config down to the ESP-01S over UART
  (hub -> MQTT -> XIAO -> UART -> ESP-01S/LittleFS)

UART-OTA groundwork ("XIAO as flasher"):
- reserve XIAO GPIOs ESP01_RST_PIN=D8, ESP01_PGM_PIN=D10 for driving the
  ESP-01S serial bootloader (not driven yet)
- docs/design/esp01s-uart-ota.md: full design (why Wi-Fi OTA doesn't fit
  the 1MB ESP-01S on the GoPro AP, bootloader entry, ROM flash protocol,
  HTTP-pull delivery, scope)
- hardware/README.md: fix stale ESP32-C3 -> XIAO ESP32-C6 wiring, add the
  two control lines (Notion wiring diagram updated to match)

Both firmwares build clean and are flashed; set_config round-trip needs
the broker to exercise end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 19:11:34 -04:00

4.2 KiB
Raw Blame History

ESP-01S firmware updates without a USB-UART adapter

Status: design / not yet implemented. Interim mitigations (config-over-UART, GPIO reservation) are shipped; see "Shipped now" below.

Problem

The ESP-01S camera bridge has no native USB. Today it is flashed with an external 3.3 V USB-UART adapter and a GPIO0 → GND jumper held during reset. On an assembled field node that is impractical — we want to update it over the network.

Why not Wi-Fi OTA on the ESP-01S itself

  1. Network topology. The ESP-01S joins the GoPro AP (10.5.5.1), not the hub / travel-router network. The hub cannot reach it to push an OTA.
  2. 1 MB flash. Standard ESP8266 OTA stages a second copy of the sketch alongside the running one. Our sketch is ~333 KB; a 1 MB module has no room for two copies plus FS and reserved areas.

So updates must arrive through the XIAO, which is already UART-connected to the ESP-01S and sits on the hub network.

Approach: XIAO ESP32-C6 as the flasher (UART OTA)

The XIAO plays the role the USB-UART adapter plays today, driving the ESP-01S's ROM serial bootloader over the existing UART.

Hardware — two added control lines

XIAO pin → ESP-01S purpose
D8 (ESP01_RST_PIN) RST pulse low to reset the ESP-01S
D10 (ESP01_PGM_PIN) GPIO0 hold low across reset → enter bootloader
D6 (TX) / D7 (RX) RX / TX existing Serial1 link (crossed)
GND GND common ground

Confirm before committing the PCB/wiring: verify D8/D10 on the actual XIAO ESP32-C6 variant do not map to ESP32-C6 strapping pins (GPIO8, GPIO9, GPIO15) or the USB-JTAG pins. Pins are reserved in firmware (ESP01_RST_PIN, ESP01_PGM_PIN) but not yet driven.

Bootloader entry

GPIO0 = LOW, pulse RST low→high → ESP-01S enters the serial bootloader on the UART. After writing: GPIO0 = HIGH, pulse RST → run the new firmware. Always restore GPIO0 = HIGH on give-up so the ESP can boot normally.

Flash protocol

Implement enough of the ESP8266 ROM bootloader / esptool SLIP protocol on the XIAO over Serial1:

  • SYNC, then FLASH_BEGIN / FLASH_DATA (≈1 KB blocks) / FLASH_END to write the app at offset 0x0.
  • Start at 115200 baud; optionally raise after sync.
  • Verify with the ROM SPI_FLASH_MD5 against the expected MD5.

Firmware delivery (hub → XIAO)

Greenfield on the Go hub (only a firmware_version field exists today). Recommended:

  • HTTP pull. Hub exposes GET /firmware/esp01s/<version>.bin (+ MD5). XIAO is triggered by an MQTT command, e.g. {"command":"update_esp01s","url":"http://<hub>/firmware/esp01s/0.4.0.bin","md5":"…"}, fetches the .bin in chunks, and streams each chunk straight into a FLASH_DATA block.
  • Avoid buffering the whole image in RAM — stream HTTP chunk → flash block → repeat.
  • MQTT chunked transfer is possible but heavier on the broker; prefer HTTP.

Sequencing / safety

  • Pause the UART JSON status/command protocol while flashing (the link is busy with the bootloader protocol).
  • On failure leave the ESP recoverable and retry; report progress/result to the hub over MQTT.

XIAO self-update (separate, easy)

The XIAO (4 MB flash, on the hub network) can use standard ESP32 OTA (ArduinoOTA or httpUpdate). No gymnastics required. Split: XIAO = native OTA; ESP-01S = flashed by the XIAO over UART.

Scope estimate

  • XIAO firmware: ESP8266 ROM-loader client over Serial1 + GPIO0/RST control + HTTP fetch + MQTT trigger. Mediumlarge.
  • Hub (Go): firmware store + HTTP endpoint + MQTT trigger command. Smallmedium.
  • Hardware: two control wires + confirm non-strapping pins. Small.

Shipped now (interim)

  • Config-over-UART (set_config) — change GoPro SSID/password/IP and poll interval with no reflash. Hub → set_camera_config (MQTT) → XIAO → set_config (UART) → ESP-01S persists to LittleFS.
  • GPIO reservationESP01_RST_PIN = D8, ESP01_PGM_PIN = D10 reserved in the XIAO firmware for the flasher.

References

  • ESP8266 ROM serial bootloader protocol (esptool).
  • Wiring: Notion "XIAO ESP32-C6 Pin-to-Pin Wiring Diagram".