firmware: no-reflash config updates for ESP-01S + UART-OTA groundwork
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

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>
This commit is contained in:
Joshua King
2026-06-04 19:11:34 -04:00
parent cefb7ef52c
commit 403e1d9edd
4 changed files with 175 additions and 42 deletions
+21
View File
@@ -113,6 +113,13 @@ int batteryPct(int raw) {
#define UART_RX_PIN D7
#define UART_TX_PIN D6
// Reserved for future ESP-01S UART OTA ("XIAO as flasher"): two control
// lines let the XIAO drive the ESP-01S into its serial bootloader and
// reflash it over Serial1 — no USB-UART adapter or GPIO0 jumper needed.
// Not driven yet; see docs/design/esp01s-uart-ota.md.
#define ESP01_RST_PIN D8 // → ESP-01S RST (pulse low to reset)
#define ESP01_PGM_PIN D10 // → ESP-01S GPIO0 (low at reset = bootloader)
// ────────────────────────────────────────────
// RGB STAT LED — D0/D1/D2 (red/green/blue) via 220Ω each
// ────────────────────────────────────────────
@@ -263,6 +270,20 @@ void mqttCallback(char* topic, byte* payload, unsigned int len) {
saveConfig();
Serial.printf("[BAT] Calibration set: raw_min=%d raw_max=%d\n",
cfg.bat_raw_min, cfg.bat_raw_max);
} else if (cmd == "set_camera_config") {
// Forward camera-bridge config to the ESP-01S over UART so the
// GoPro creds / poll rate can change without reflashing it.
JsonDocument out;
out["type"] = "cmd";
out["command"] = "set_config";
if (!doc["camera_ssid"].isNull()) out["camera_ssid"] = doc["camera_ssid"];
if (!doc["camera_password"].isNull()) out["camera_password"] = doc["camera_password"];
if (!doc["camera_ip"].isNull()) out["camera_ip"] = doc["camera_ip"];
if (!doc["poll_interval_sec"].isNull()) out["poll_interval_sec"] = doc["poll_interval_sec"];
String line; serializeJson(out, line);
UART_ESP8266.println(line);
UART_ESP8266.flush();
Serial.println("[MQTT] Forwarded set_config → ESP-01S");
} else if (cmd == "registered") {
String id = doc["camera_id"] | "";
if (id.length() > 0 && id != cfg.camera_id) {