Files
Hermes d419dfe519
CI/CD / lint-and-typecheck (pull_request) Failing after 2s
CI/CD / test (pull_request) Has been skipped
CI/CD / build (pull_request) Has been skipped
CI/CD / deploy (pull_request) Failing after 12m6s
CI/CD / test (push) Has been skipped
CI/CD / build (push) Has been skipped
CI/CD / deploy (push) Has been skipped
Build (Dev) / build (push) Failing after 14s
CI/CD / lint-and-typecheck (push) Failing after 0s
feat: add PlatformIO ESP32 firmware with dual-STA + MQTT + GoPro control
firmware/
├── platformio.ini     — ESP32 (esp32dev), PubSubClient + ArduinoJson
├── src/main.cpp       — Full camera node firmware (360 lines)
│   ├── SPIFFS config persistence
│   ├── Dual Wi-Fi STA (travel router + GoPro AP)
│   ├── GoPro Hero 3 HTTP API (start/stop/status)
│   ├── 60-byte binary status blob parser
│   ├── MQTT per contract (status QoS1, heartbeat QoS1, announce QoS2)
│   ├── Command subscription (start/stop/reboot)
│   ├── Auto-registration (announce → hub assigns cam-NNN)
│   ├── Heartbeat every 60s, status every 30s
│   ├── LED status indicator
│   └── Exponential backoff reconnection
├── data/config.json   — Default SPIFFS config template
└── README.md          — Quick start, config reference, troubleshooting
2026-05-21 21:54:05 +00:00

4.8 KiB

RemoteRig — ESP32 Camera Node Firmware

Platform: PlatformIO (esp32dev) | Framework: Arduino MQTT Contract: docs/MQTT_CONTRACT.md Hardware: hardware/README.md

Quick Start

# Install PlatformIO (if not already)
pip install platformio

# Build
cd firmware
pio run

# Upload to ESP32 (USB connected)
pio run --target upload

# Upload SPIFFS config (first time only, or after config changes)
pio run --target uploadfs

# Serial monitor
pio device monitor

Configuration

The ESP32 stores configuration in SPIFFS (data/config.json):

Key Default Description
wifi_ssid "RemoteRig" Travel router SSID
wifi_password "" Travel router password
camera_ssid "GOPRO-BP-" GoPro Wi-Fi AP prefix (auto-discovered)
camera_password "goprohero" GoPro Wi-Fi password
mqtt_broker "192.168.4.10" Pi Zero 2 W static IP
mqtt_port 1883 Mosquitto port
camera_id "" Assigned by hub on first announce (leave empty)
poll_interval_sec 30 GoPro status poll frequency
heartbeat_interval_sec 60 MQTT heartbeat frequency

First boot: Leave camera_id empty. The ESP32 will auto-announce to the hub, which assigns a cam-NNN ID. The assigned ID is saved to SPIFFS automatically.

LED Status Codes

Pattern Meaning
Slow blink (1s) Connected to router + MQTT, normal operation
Fast blink (200ms) No Wi-Fi connection — reconnecting
Solid on Connected but GoPro unreachable
Off Boot/shutdown

Architecture

┌──────────────────────────────────────────┐
│              ESP32 (Arduino)             │
│                                          │
│  ┌──────────┐  ┌──────────┐  ┌────────┐  │
│  │ WiFi STA │  │ WiFi STA │  │  MQTT  │  │
│  │ (Router) │  │ (GoPro)  │  │ Client │  │
│  └────┬─────┘  └────┬─────┘  └───┬────┘  │
│       │             │            │       │
│       │    ┌────────┘            │       │
│       ▼    ▼                     ▼       │
│  ┌─────────────────────────────────┐     │
│  │         Main Loop               │     │
│  │  Every 30s:                    │     │
│  │    HTTP GET GoPro status       │     │
│  │    Parse 60-byte blob          │     │
│  │    MQTT publish status         │     │
│  │  Every 60s:                    │     │
│  │    MQTT publish heartbeat      │     │
│  └─────────────────────────────────┘     │
│                                          │
│  SPIFFS: /config.json (persistent)       │
└──────────────────────────────────────────┘

Boot Sequence

  1. Load config from SPIFFS
  2. Connect to travel router Wi-Fi (STA mode)
  3. Connect to GoPro AP Wi-Fi (STA mode — simultaneous)
  4. Connect to MQTT broker (192.168.4.10)
  5. If no camera_id → publish announce → hub registers us
  6. Subscribe to remoterig/cameras/{camera_id}/command
  7. Enter main loop

GoPro API Notes (Hero 3 Black/Silver)

  • IP: Always 10.5.5.1 (GoPro's own AP)
  • Status endpoint: GET /bacpac/SH?t={password}&p=%01
  • Start recording: GET /bacpac/SH?t={password}&p=%01 (mode byte = 1)
  • Stop recording: GET /bacpac/SH?t={password}&p=%00 (mode byte = 0)
  • Get password: GET /bacpac/sd (no auth, returns plain text)
  • Status blob: 60 bytes binary — see parseStatus() in main.cpp for field offsets

ESP8266 Compatibility

To target ESP8266 instead:

  1. Change platformio.ini: board = d1_mini under [env:d1_mini]
  2. Change WiFi.hESP8266WiFi.h
  3. ESP8266 doesn't do true simultaneous STA — use single STA to travel router, HTTP to GoPro via router bridge
  4. SPIFFS → LittleFS on some boards

ESP32 is recommended for dual-STA capability.

Troubleshooting

Symptom Check
No serial output Baud rate: 115200. Hold BOOT, press EN, release BOOT for flash mode
Can't connect to router Verify SSID/password in SPIFFS config, check router DHCP range
GoPro unreachable GoPro must be ON and Wi-Fi enabled. Password defaults to "goprohero"
MQTT connect fails Verify Mosquitto running on Pi: systemctl status mosquitto
Camera never registers Watch serial for "announce" message, check hub logs for registration