DJI Osmo  ·  Betaflight  ·  v1.0.0-beta1

The Slate

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

The camera reports to your goggles

Four rows of your Betaflight OSD, rewritten once a second. Flip both switches and watch what the module actually sends.

Your OSD4 × custom message

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.

Why it reads the way it does

A field you cannot trust is left blank

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.

Sixteen characters, and not one more

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.

You choose what goes in the rows

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

Four ways to roll

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

Arm

Arm starts the clip, disarm stops it. Nothing to remember and nothing to press.

AUX CHANNEL

Switch

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 + AUX · DEFAULT

Cut

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 + AUX

Both

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.

Why the window runs to 2100 µs

900 to 2100 µs, not 1000 to 2000

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 / ELRS98815002012
SBUS98715002011

Pinned by a host test, so it cannot quietly regress: test_switch_window.c

At the bench — what you need

Requirements

Four things, and if you already fly Betaflight you have two of them.

Board

A compatible ESP32

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.

Camera

A DJI Osmo

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.

Flight controller

Betaflight 2025.12 or newer

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.

Tools

A soldering iron

Four wires — 5 V, ground and a UART pair. Nothing else to buy, and nothing to buy from us.

INSTALL · 1

Flash the board

One file at offset 0x0 — bootloader, partition table and app already merged. No toolchain, and none to roll it back either.

INSTALL · 2

Solder it to a spare UART

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

Set it up from your phone

A module is flashed once. Everything you change after that is a browser away — no toolchain, no serial console, no laptop at the field.

STEP 1

Hold the button ten seconds

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.

STEP 2

Join SLATE-XXXX

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.

STEP 3

Pick your camera and channel

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 settings page on a phone: the camera picked from a live list, the recording trigger set to Both, the control type set to Button, and Save and Done & restart at the bottom.
The whole of it is this one page, served by the module itself.
Choosing a channel, and the OSD layout

Choosing a channel used to need a lookup table

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.

AUX 3 — live
90015002100

1842 µs — inside the window, so this switch position records.

At the bench — what it talks to

Cameras

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.

CameraStatus
Osmo NanoVerified
Osmo 360Verified
Osmo Action 5 ProExpected
Osmo Action 4Expected
Osmo Action 3Expected
Osmo Action 6Expected

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.

What Verified and Expected mean

What the two words mean

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.

Flight controller

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

Roadmap

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

One board, four wires, one file

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.

Wiring

ESP32-C3 SuperminiFlight controller
5V5V
GNDGND
GPIO4 — UART1 TXspare UART RX
GPIO5 — UART1 RXspare UART TX

TX goes to RX and vice versa; common ground required. Budget around 80 mA average with short peaks while the radio transmits.

Flash it

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.

  1. Connect, and pick the port that appears.
  2. Choose slate-v1.0.0-beta1-full.bin.
  3. Set Flash Address to 0x0. The box is pre-filled with 0x1000, and at that address the image has no bootloader in front of it and will not start.
  4. Program.

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.

Three ways to kill it

  • Never plug in USB with the flight battery in. The shared 5 V wire ties the two together, so USB back-feeds the FC and everything else on that rail, including the VTX. Two 5 V sources in parallel through one wire is how BECs die.
  • Do not use GPIO2, GPIO8 or GPIO9. They are strapping pins, and a pull on them at boot stops the chip from starting.
  • The trace antenna must overhang the frame. Carbon fibre is conductive; laying the board flat on it will detune and shadow the antenna badly. Heatshrink is fine — anything containing metal or carbon is not.

What you need

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.

Full build notes, wiring and test results on GitHub →

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.