One Port, Many Drones: Teaching the Console to Speak MAVLink
The console could already fly my own simulator. Teaching it MAVLink — the protocol industry autopilots speak — was supposed to be the big job of the week. It was a small one, and that's the whole point: a new kind of vehicle is a new adapter behind one unchanged seam.
The air-gap post introduced the seam. Every vehicle the console talks to sits behind a single vehicle adapter. Above that seam nothing knows a protocol — the safety checks, the recorder, the map and the screens all deal in one normalized command type and one normalized telemetry type. Below it, each adapter translates for one kind of device.
So the plan was simple: don't touch anything above the seam. Add one more adapter. Isang kontrata, maraming eroplano (one contract, many aircraft).
What MAVLink is, briefly
MAVLink is the open messaging standard flight controllers use. It's a compact binary protocol for telemetry and commands, spoken by PX4 and ArduPilot — the two dominant open autopilot stacks. It's widely used across open-source, research, commercial and some defense unmanned-system work.
It's a different world from a toy drone's little text protocol. Heartbeats. A parameter system. Mission uploads. Arm and disarm handshakes. Mode changes that come back with acknowledgements.
What landed
Three small packages, deliberately split so the useful parts are reusable:
- A dependency-free MAVLink library — the pure protocol. The checksum, the frame format, and a parser that reads a byte stream and hands back messages. It knows nothing about this project, so a companion computer or a bridge could use it too.
- A shared adapter base — the plumbing every device adapter repeats: opening a link, streaming telemetry, gating commands, connecting and disconnecting. A new device fills in three small methods, not a whole lifecycle.
- The MAVLink adapter itself — binds the library to the seam. It streams a heartbeat so the vehicle recognises the ground station, folds position and battery messages into normalized telemetry, and turns arm, take-off, land and return-to-launch into MAVLink commands, waiting for the acknowledgement.
Then one line of wiring registered a new adapter kind, and two roster entries added the vehicles. The screens, the safety pipeline, the recorder — untouched. That's the payoff of putting the seam in early: a new class of aircraft cost an adapter, not a rewrite.
PX4 · MAVLINK · AIR DOMAIN. The panel, the movement controls and the pipeline stamps across the top are the same ones every other vehicle gets — that's the seam doing its job. The vehicle on the other end of the link is my simulator, not an autopilot.Three decisions worth defending
Re-author the protocol; don't borrow it. There are excellent MAVLink libraries. I didn't use one. The most common is under a copyleft licence that would reach into this project, and the best-known ground stations are under a stronger copyleft still. This project's whole posture is that it can be deployed without inheriting anyone's licence obligations. So the codec is re-authored from the public MAVLink specification — the message definitions, the frame layout and the published checksum algorithm are public facts, and facts aren't copyrightable. Same clean-room approach the toy-drone adapter used.
TypeScript, not C++. A fair question, since PX4 and the best-known ground station are both C++. But the wire is just bytes, so the language on each end is independent. The ground-station adapter is light work — it reads small datagrams and parses tiny frames a few hundred times a second. Reaching for C++ there would mean a compiled native module to rebuild for every desktop-runtime version, which is exactly the packaging pain this project avoids everywhere else. C++ is the right tool for firmware, on the aircraft, in hard real time. It's the wrong tool for the ground.
MAVLink, not uORB. Another fair question if you've read PX4 internals. uORB is PX4's internal message bus — it wires the flight stack's own modules together and never leaves the aircraft. MAVLink is the external link a ground station speaks. You can't "connect to a drone over uORB" any more than you can plug into another computer's memory bus.
What I learned
The interesting bug wasn't in the protocol. It was in the seams around it, and it's the kind a control station must not ship: a disconnected vehicle kept showing its last reading. Disconnect one and its battery and altitude sat frozen on screen, reading as live for something no longer connected. The data was stale rather than wrong, and the adapter really did close its link — but presenting stale numbers as current is its own hazard. A disconnected vehicle greys out now.
The rest was concurrency, and it had to be boring. Send one command at a time, so two commands can't get each other's acknowledgement. Cap the already-sent memory, so a long mission can't grow it without bound. Make every wait end on either a reply or a timeout, so nothing can hang. A parser reading a hostile byte stream re-synchronises instead of stalling.
None of that is exciting. On a thing that commands aircraft, "not exciting" is the goal.
Flying it today, with nothing plugged in
You don't need a drone to see this work — or even the full PX4 simulator. Two options exist:
- PX4 SITL — the actual flight stack running in software, speaking MAVLink exactly like real hardware. This is the honest way to validate the adapter properly. I've written the guide for it. I haven't run it yet.
- My mini-simulator — a small stand-in that streams valid MAVLink and serves a web dashboard, so you can watch the vehicle from both sides. Not a flight stack, not physics. Just enough protocol to exercise the whole ground-side path. This is what everything above was tested against.
The four verbs in MAVLink, and the handshakes that bite
Same key as the other GCS posts — or request access below.
Thoughts?
Comments are threads on GitHub, so a GitHub account is needed to post. No account? Email me instead — I read everything.