Complete rewrite of firmware into two dedicated boards per camera node:
ESP8266 (Camera Bridge):
- Connects ONLY to GoPro AP — polls status, sends over UART
- Zero network switching, zero MQTT
- HTTP GET /bacpac/SH for status, start/stop
- JSON-per-line UART protocol to ESP32
ESP32 (MQTT Bridge):
- Connects ONLY to travel router — MQTT to Pi hub
- Reads status from ESP8266 over UART2 (RX16/TX17)
- Auto-registration, heartbeat, command forwarding
- Zero camera communication
UART Protocol: JSON-per-line at 115200 8N1
ESP8266→ESP32: status/ack/pong/error
ESP32→ESP8266: cmd (start_recording/stop_recording/ping)
Hardware updates:
- BOM now includes both boards (~4/node)
- 3D case has stacked dual-board compartment
- UART wire channel between board recesses
- Shared 3.3V power rail for both boards
- Unified firmware for ESP32 (dual-STA) and ESP8266 (time-shared STA)
- ESP8266: alternates between GoPro AP and travel router per poll cycle
- PlatformIO dual-target: esp32dev + esp8266dev (d1_mini)
- camera_ip config field for Akaso/non-GoPro cameras
- LittleFS support for ESP8266 (replaces SPIFFS)
- Camera compatibility table (GoPro H3/H4, Akaso)
- LED polarity handled per-platform (ESP8266 active-low)
ESP8266 time-sharing adds ~4s latency per 30s cycle — invisible at poll rate.
Conflicts in cmd/server/main.go, go.mod, internal/db/db.go
— main had older/simpler versions from PR #4 (README)
— dev has full codebase with MQTT, SSE, embed
- GoPro Hero 3 sleeve with lens/screen/USB cutouts
- ESP32 D1 Mini electronics compartment (vented)
- LiPo battery compartment with velcro strap slots
- Bill of materials (~0 per camera node)
- Wiring diagram (LiPo → dual buck converters → ESP32 + GoPro)
- Field deployment workflow
OpenSCAD model in hardware/case/remoterig-case.scad
Assembly guide in hardware/README.md
- Rename BINARY_NAME from openclaw to remoterig
- Cross-compile with GOOS=linux GOARCH=arm64 for Raspberry Pi Zero 2 W
- Build React frontend at repo root (npm ci && npm run build) producing dist/
- Remove old web/ working-directory and explicit embed step (handled by go:embed)
- Repository dispatch event_type unchanged: dev-build-success
- Added monotonic event ID (Last-Event-ID) for reconnection support
- Events now emit typed: 'event: camera_status' for client-side filtering
- Initial connection event sends 'event: connected' with ID
- Reconnection acknowledgment via Last-Event-ID header
- CORS preflight (OPTIONS) handler
- Access-Control-Allow-Headers: Last-Event-ID
- Initial heartbeat shortened to 15s for faster detect (30s for steady)
Closes CUB-233.
Implements MQTT subscriber (internal/mqtt/subscriber.go) that:
- Connects to Mosquitto broker with auto-reconnect
- Subscribes to remoterig/cameras/+/status, +/heartbeat, +/announce
- Parses and validates incoming messages per MQTT contract
- Inserts status_logs with duplicate prevention
- Auto-detects recording state changes and manages recording_events
- Broadcasts camera status changes via SSE hub
- Camera auto-registration via announce (MAC-based, sequential cam-NNN)
- Heartbeat watchdog marks cameras offline after 120s silence
- Wired into main.go with graceful degradation (warns if broker unreachable)
Dependency: github.com/eclipse/paho.mqtt.golang v1.5.0
Closes CUB-232.
Defines topic hierarchy, payload schemas, QoS levels, heartbeat
protocol, camera auto-discovery via announce topic, offline
buffering strategy, and command/response flow for start/stop.
Architecture: travel router subnet (192.168.4.x), Pi Zero 2 W
runs Mosquitto + Go backend, ESP32s dual-STA to GoPro AP +
travel router. No internet dependency.
Closes CUB-238.