DJI Osmo · Betaflight · v1.0.0-beta1
Arm the quad. The camera rolls.
Out now in beta.
An ESP32-C3 on a spare UART of your flight controller. It starts your DJI Osmo recording when you arm, stops when you disarm, and puts the camera's own state on your Betaflight OSD — so you know it is rolling before you leave the ground.
In the air — what you see
Four rows of your Betaflight OSD, rewritten once a second. Flip both switches and watch what the module actually sends.
No camera bound
Flip both. A field goes blank whenever the camera has not confirmed that value — the firmware never infers a reading from what it commanded, because a wrong number on your OSD is worse than no number.
Every value carries its own confirmation flag and ages out after three seconds of silence. Nothing on that screen is a guess, and nothing is what the module last asked for rather than what the camera reported back.
NO CAM rather than IDLE, because idle is a camera that is present and not recording — a completely different thing from a camera that is not there.
Betaflight draws a custom message at a fixed position and never clears what was there before, so IDLE after CAM LOST reads IDLEOST. Every row is padded to the longest thing it can ever produce, which makes the limit a budget rather than a guideline.
State, clip time, battery, card time remaining, the liveness dot and which camera is bound, dragged into the four rows from the settings page.
In the air — what starts the recording
Set once, from your phone. The mode decides what owns the record button while you are flying; the module's own button always overrides it on the bench.
Arm starts the clip, disarm stops it. Nothing to remember and nothing to press.
An AUX channel controls recording. Level-driven rather than edge-toggled, so the state is still predictable after an FC reboot or a brownout.
Arm starts the clip; a press on the switch cuts a bad take and immediately starts a fresh one. It never stops recording — it only breaks the footage into separate files.
Arm starts and disarm stops, and the switch works independently on top. Arming or disarming takes control back, so you always land on a stopped camera.
A CRSF switch at +100% sits at 2012 µs. A window that stopped at 2000 could not contain it — so the setting looked right on screen and the camera never rolled. The scale runs the full range a receiver actually produces, and a limit dragged to either end means and beyond, so a radio with extended endpoints still triggers.
| Receiver | −100% | Centre | +100% |
|---|---|---|---|
| CRSF / ELRS | 988 | 1500 | 2012 |
| SBUS | 987 | 1500 | 2011 |
Pinned by a host test, so it cannot quietly regress: test_switch_window.c
At the bench — what you need
Four things, and if you already fly Betaflight you have two of them.
It ships and flies on an ESP32-C3 Supermini, which costs a few pounds. The firmware is not tied to that board — it needs Bluetooth LE, Wi-Fi, a spare UART and room for two app slots.
Osmo Nano and Osmo 360 are verified. The Action series is recognised and expected to work — the table below says which is which, and how sure that answer is.
A hard requirement: that is the release which added the four Custom Message OSD elements this writes into. You need one spare UART, with MSP enabled on it at 115200.
Four wires — 5 V, ground and a UART pair. Nothing else to buy, and nothing to buy from us.
One file at offset 0x0 — bootloader, partition table and app already merged. No toolchain, and none to roll it back either.
Four wires to any free UART on your flight controller: 5 V, ground, and the pair crossed TX to RX. The wiring is below.
At the bench — setup
A module is flashed once. Everything you change after that is a browser away — no toolchain, no serial console, no laptop at the field.
Or flick the AUX channel you bound to setup, for a module buried in the frame. Both routes are refused while armed, checked continuously — arming during the hold abandons it.
The module reboots into an open Wi-Fi access point and the settings page opens by itself. Those four characters come from the board's own MAC, so two modules at the same field are never confused for each other.
The camera list is live — switch a camera on and it appears. The module never picks one for you: with nothing selected it connects to nothing at all.
The page draws the live value of the channel you selected. Flick the switch, watch the marker move, and drag the window around where it lands. It needs the flight controller powered — which is a wire, not the radio — so it works in setup mode with the camera link down.
The OSD layout is set the same way: drag the fields you want into the four rows, and the page budgets each row against the same 16-character limit the renderer uses.
1842 µs — inside the window, so this switch position records.
At the bench — what it talks to
The module recognises your camera when you bind it and speaks to it correctly from the first connection. Two are verified end to end. The rest are expected to work, and say so plainly rather than claiming more.
| Camera | Status |
|---|---|
| Osmo Nano | Verified |
| Osmo 360 | Verified |
| Osmo Action 5 Pro | Expected |
| Osmo Action 4 | Expected |
| Osmo Action 3 | Expected |
| Osmo Action 6 | Expected |
A camera that is not on this list is not blocked — it is unknown. It may work and it may simply not, and there is no way to find that out from here without one on the bench.
Verified — that exact camera has been paired, driven and read back on hardware here. Recording starts and stops on command, and every value on your OSD came from the camera rather than from what the module last asked it to do.
Expected — the camera is recognised and handled correctly, and a camera in the same family is verified. That exact model has never been run.
Expected is not a soft yes. It is the most that can honestly be said about hardware nobody here has held, and it stays that way until one is on the bench.
Betaflight 2025.12 or newer — a hard requirement, because that is the release that added the four Custom Message OSD elements this writes into. Enable MSP on the chosen UART at 115200 in the Ports tab, and place Custom Message 1–4 in the OSD tab. If those elements are absent, the OSD half cannot work.
In the air — what comes next
Five milestones, in the order they will be taken. Intent, not a promise: nothing here has a date, and nothing here is finished.
At the bench — build one
The firmware is published under a noncommercial licence and the releases are single images flashable at offset zero — bootloader, partition table and app already merged. No toolchain needed to install it, and none to roll it back.
| ESP32-C3 Supermini | Flight controller |
|---|---|
| 5V | 5V |
| GND | GND |
| GPIO4 — UART1 TX | spare UART RX |
| GPIO5 — UART1 RX | spare UART TX |
TX goes to RX and vice versa; common ground required. Budget around 80 mA average with short peaks while the radio transmits.
Once, over USB, with the flight battery out — everything after that happens over Wi-Fi from your phone. Espressif's flasher runs in the browser, so there is nothing to install: esptool-js, in Chrome or Edge.
Prefer a terminal? Espressif's esptool writes the same file to the same offset.
NVS sits at 0x9000 and is not covered by these images, so a rollback keeps your bound camera and your settings. A rollback is for recovering from bad firmware; it should not also cost you your setup.
An ESP32-C3 Supermini. A compatible DJI camera — the table above says which, and how sure that answer is. A flight controller running Betaflight 2025.12+ with one spare UART. Nothing else is sold, and nothing else is needed.
The Slate is the first thing under this name. Frames come next, on the same terms: the source and the measurements published, and nothing claimed that has not been on hardware.