Victoria's Code STA / 01
GCS — build log

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 this post is and isn't. This is the adapter and the protocol library. It runs against a mini-simulator I wrote — a stand-in that speaks valid MAVLink over UDP. It has not been flown against PX4 software-in-the-loop, and it has not touched physical hardware. Both are on the list; neither has happened.

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.

The operator console showing the Common Operating Picture: a map with the fleet across four domains, and a detail panel for FALCON-1 labelled PX4, MAVLINK, AIR DOMAIN — in flight, with phase, mode, battery, altitude in feet MSL, heading, position and armed state, and movement, return-to-launch and land controls below.
FALCON-1 on the console, labelled 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 next real milestone is SITL: run the actual autopilot in software and find out whether the four-verb contract holds up, or whether MAVLink's heartbeats, acknowledgements, modes, parameters and mission protocol force the abstraction to grow. That test hasn't happened, so I don't know yet.
Restricted section

The four verbs in MAVLink, and the handshakes that bite

Same key as the other GCS posts — or request access below.

Request access

Build log entry. Apache-2.0. Part of the multi-domain GCS build log.
Comments

Thoughts?

Comments are threads on GitHub, so a GitHub account is needed to post. No account? Email me instead — I read everything.