Conversation
Add a second board target and a new camera backend, and fix the GoPro link being dropped right after it connects. ESP32-C3 Super Mini (new `esp32c3-supermini` env) - platformio.ini: shared settings moved to [env]; new env on the nologo_esp32c3_super_mini board (ESP32-C3FH4: 4 MB flash, no PSRAM, USB-Serial/JTAG). The esp32s3-zero env builds as before. - config.h: per-board pin maps selected by a BOARD_* define. C3: FC UART on GPIO20 (RX) / GPIO21 (TX), status LED on GPIO8. - StatusLed: support a plain single-colour LED (the C3 board has a blue active-low LED instead of a WS2812); states are told apart by blink pattern: slow blink = searching, double flash = config Wi-Fi client, solid = camera connected, fast blink = recording. - main.cpp: on single-core chips loop() blocks for a tick so the camera task and the idle task are not starved. - README: build, flash and wiring notes for the C3 board. GoPro - Fix the link being dropped as "stale" right after connecting: the check used a timestamp taken before the blocking status/setting writes, so a reply landing during them made `now - lastUpdateMs` underflow. - Log the NimBLE error code when a connect attempt fails. DJI Action 2 (new "DJI Action 2" camera type) - New DjiAction2Camera backend: DUML over BLE with the app-level pairing the Mimo app uses (01 00 arm write to FFF4, 0x07/0x45 pairing request, ACK of the on-screen approval), record start/stop via 0x02/0x02. Pairing flow ported from shutterlink (MIT). - The camera sends no telemetry once paired, so the BLE link itself is the liveness signal and record state/time come from our own commands. Photo capture, mode switch, SD free, time left and resolution are unavailable. - Web UI: new camera type in the bind wizard; every DJI camera is listed for each DJI type (the scan can't tell an Action 2 apart); OSD fields and channel functions the Action 2 can't provide are hidden. Tested on an ESP32-C3 Super Mini: - flashes and boots, Wi-Fi AP and Web UI work - GoPro HERO12 pairs and stays connected with live status - DJI Action 2 pairs; link stability after the liveness change and record start/stop not verified yet - not yet tested with a flight controller (MSP, OSD, record on arm) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YarosMallorca
left a comment
There was a problem hiding this comment.
Looks great overall, thanks! I added a few points to look into, and verify on a real Action 2 if you own one (I don't), as most code was copied from shutterlink as the source of truth.
I will receive my ESP-C3 next week and test on it, to confirm that everything works. I will try to get access to a GoPro as well; in the meantime, if you could look further into the Action 2, especially on the points I've mentioned below, I'd appreciate it!
| if (start && !status_.isRecording()) { | ||
| recStartMs_ = millis(); | ||
| status_.recElapsedS = 0; | ||
| } | ||
| status_.recState = start ? RecState::Recording : RecState::Idle; | ||
| return true; |
There was a problem hiding this comment.
recordCtrl sets status_.recState optimistically and returns true.
If the camera NAKs the command (handleFrame handles 0x02/0x02 ACK with p[0] != 0x00, e.g. 0xd9 wrong-state), it only logs; it never reverts recState.
The OSD then shows REC while the camera isn't recording. Please snapshot the prior recState/recElapsedS before the optimistic write and restore them on a non-zero reply.
| // Once paired the camera goes quiet - no status pushes, no reply to the keep-alive - | ||
| // so the BLE link itself (supervision timeout -> onDisconnect) is the liveness | ||
| // signal, and the record time runs from our own accepted start | ||
| status_.lastUpdateMs = t; | ||
| if (status_.isRecording()) | ||
| status_.recElapsedS = (t - recStartMs_) / 1000; | ||
| if (t - lastKeepMs_ >= kKeepAliveMs) { | ||
| lastKeepMs_ = t; | ||
| keepAlive(); | ||
| } |
There was a problem hiding this comment.
"Camera goes quiet after pairing" is likely incorrect, likely copied from shutterlink without questioning.
The design tracks its own elapsed time and relies purely on the BLE supervision timeout for liveness, on the premise that "the camera goes quiet - no status pushes, no reply to the keep-alive." Independent reverse engineering suggests otherwise for the Osmo DUML family:
- Osmosis MEDIA_PROTOCOL.md: 0x02/0x80 Camera Status is pushed ~10 Hz (recording bit at byte 0, 0x01→0x81); 0x0D/0x02 Battery ~1 Hz; 0x02/0xDC Storage push. "Status pushes arrive automatically; periodic polling not required."
- The dedicated FLORIANSV35/controle-dji-action2 project reports real C REC/C STP states, implying it parses returned state.
DjiOsmoCameraalready parses0x02/0x80for the Nano.
If the Action 2 does push these, this backend could report true recording state/elapsed, battery, and SD (parity with the other backends) and use a real telemetry-stale drop.
This backend skips the init/subscribe burst that DjiOsmoCamera sends, which may be what actually enables the pushes.
Since I don't have an Action 2, can't verify. If you do own one, please verify and report back.
| if (f.cmdSet == 0x02 && f.cmdId == 0x02 && f.flags == duml::FLAG_ACK && n >= 1) { | ||
| if (p[0] == 0x00) | ||
| status_.recState = lastCmdStart_ ? RecState::Recording : RecState::Idle; | ||
| else | ||
| Serial.printf("[a2] record command rejected (0x%02x)\n", p[0]); | ||
| return; |
Add a second board target and a new camera backend, and fix the GoPro link being dropped right after it connects.
ESP32-C3 Super Mini (new
esp32c3-superminienv)GoPro
now - lastUpdateMsunderflow.DJI Action 2 (new "DJI Action 2" camera type)
Tested on an ESP32-C3 Super Mini: