- 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.
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
- Load config from SPIFFS
- Connect to travel router Wi-Fi (STA mode)
- Connect to GoPro AP Wi-Fi (STA mode — simultaneous)
- Connect to MQTT broker (192.168.4.10)
- If no
camera_id→ publish announce → hub registers us - Subscribe to
remoterig/cameras/{camera_id}/command - 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 |