Files
remote-rig/firmware/README.md
T
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

138 lines
6.0 KiB
Markdown

# RemoteRig — ESP32 / ESP8266 Camera Node Firmware
> **Platform:** PlatformIO (esp32dev + esp8266dev) | **Framework:** Arduino
> **MQTT Contract:** [docs/MQTT_CONTRACT.md](../docs/MQTT_CONTRACT.md)
> **Hardware:** [hardware/README.md](../hardware/README.md)
## Quick Start
```bash
# 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 |