Files
remote-rig/firmware
Hermes 324402f268
CI/CD / lint-and-typecheck (push) Failing after 0s
CI/CD / test (push) Has been skipped
CI/CD / build (push) Has been skipped
Build (Dev) / build (push) Failing after 8s
CI/CD / deploy (push) Has been skipped
feat: add ESP8266 support + Akaso camera compatibility config
- 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.
2026-05-22 00:28:48 +00:00
..

RemoteRig — ESP32 / ESP8266 Camera Node Firmware

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

Quick Start

# Install PlatformIO
pip install platformio

# Build for ESP32 (recommended — dual-STA)
cd firmware
pio run -e esp32dev

# Build for ESP8266 D1 Mini (time-shared STA)
pio run -e esp8266dev

# Upload to board
pio run -e esp32dev --target upload
pio run -e esp8266dev --target upload

# Upload SPIFFS/LittleFS config (first time only)
pio run -e esp32dev --target uploadfs
pio run -e esp8266dev --target uploadfs

# Serial monitor
pio device monitor

Platform Differences

Feature ESP32 ESP8266
Wi-Fi Dual-STA (simultaneous) Single STA (time-shared)
Poll latency 0ms (always connected to both) ~4s per cycle (switch + poll + switch)
Power draw ~80mA active ~70mA active
Cost ~$5 ~$3
Build target esp32dev esp8266dev
Filesystem SPIFFS LittleFS
LED pin GPIO 2 (active high) GPIO 2 / LED_BUILTIN (active low)

ESP8266 workflow: Every 30s, the ESP8266 switches from the travel router to the GoPro AP (~1s), polls the camera (~2s), switches back to the travel router (~1s), then publishes MQTT. This adds ~4s of latency per cycle but is invisible at 30s poll intervals.

Camera Compatibility

Camera IP Protocol Status
GoPro Hero 3 10.5.5.1 HTTP GET /bacpac/SH Full support
GoPro Hero 4 10.5.5.1 HTTP GET /gp/gpControl ⚠️ Different API — needs adaptation
Akaso Brave 4/7/V50 192.168.1.1 or 192.168.42.1 Varies (HTTP or TCP:7878) 🔬 Needs testing — config camera_ip

For Akaso or other cameras: set camera_ip in config.json. The firmware will attempt the GoPro-style HTTP API at that IP. If the camera uses a different protocol, the fetchCameraStatus() and sendCameraCommand() functions in main.cpp need to be adapted.

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

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