Skip to main content
Mission Control talks to flight controllers in MAVLink v2 (ArduPilot and PX4) and MSP (Betaflight and iNav). One TypeScript interface, DroneProtocol, hides the difference so the rest of the app does not care which firmware is on the other end. MAVLink v2 is a binary protocol. Every frame has the same layout:
  • STX is 0xFD for v2.
  • LEN is the payload length. v2 trims trailing zero bytes from the payload, so the receiver restores the full length before decoding.
  • MSGID is 3 bytes.
  • CRC is CRC-16/MCRF4XX over the frame, seeded with a per-message CRC_EXTRA byte.
  • SIGNATURE is present when bit 0 of INC_FLAGS is set: link id, a 48-bit timestamp and a 6-byte signature. src/lib/protocol/mavlink-signer.ts builds and checks it.

CRC_EXTRA

Every MAVLink message definition has a CRC_EXTRA constant derived from its field layout. If the sender and the receiver disagree on a message’s layout, the CRC fails and the frame is dropped. Mission Control keeps the CRC_EXTRA value and the full payload length for every message it decodes in src/lib/protocol/mavlink-crc-extra.ts (CRC_EXTRA and PAYLOAD_LENGTHS).

Parser

src/lib/protocol/mavlink-parser.ts is a streaming parser. It accumulates bytes from the transport in whatever chunks they arrive, scans for 0xFD, reads the header and payload, restores zero-trimmed payloads, checks the CRC and emits a typed MAVLinkFrame.

The DroneProtocol interface

DroneProtocol is defined in src/lib/protocol/types/protocol.ts. Every adapter implements it: MAVLinkAdapter, MSPAdapter, and the demo-mode MockProtocol. Its methods fall into groups:
The real interface has many more members, including optional ones (marked ?) that only some firmwares implement, such as iNav safehomes and geozones or fence and rally upload. ProtocolCapabilities (in src/lib/protocol/types/core.ts) is a set of booleans such as supportsParameters, supportsMissionUpload, supportsCalibration, supportsFailsafe, supportsOsd and supportsFirmwareFlash. The UI reads it before showing a feature. A flight controller configuration panel that depends on one names it as its requiredCapability, so the panel does not appear for a firmware that lacks it. MAVLinkAdapter (src/lib/protocol/mavlink-adapter.ts) implements DroneProtocol for ArduPilot and PX4. Its work is split across sibling files: ArduPilot and PX4 share MAVLink but differ in parameter names, mode numbers and some command meanings. The adapter learns the firmware from the heartbeat and from AUTOPILOT_VERSION, then applies the matching mode map and parameter metadata.

MSP adapter

MSPAdapter (src/lib/protocol/msp-adapter.ts) implements DroneProtocol for Betaflight and iNav. MSP is a different kind of protocol:
  • Request and response, with one request outstanding at a time. msp/msp-serial-queue.ts serializes requests and matches each reply.
  • MSPv1 framing with an XOR checksum, and MSPv2 framing with a CRC-8 DVB-S2 checksum. The parser in msp/msp-parser.ts also handles jumbo frames.
  • No named parameters. Configuration lives in binary blocks (PID profile, rate profile, OSD layout, serial ports and so on).
The adapter delegates to msp-adapter-telemetry.ts (polled telemetry), msp-adapter-commands.ts (commands) and msp-adapter-params.ts (virtual parameters).

Virtual parameters

Because Betaflight and iNav have no parameter store, the MSP adapter maps fields of the configuration blocks to virtual parameter names such as BF_RC_RATE, BF_RC_EXPO and BF_ROLL_RATE. The definitions live in src/lib/protocol/msp/virtual-params/. This lets the usePanelParams hook, which every configuration panel uses, read and write Betaflight settings the same way it handles MAVLink parameters.

Encoders

MAVLink encoding lives in src/lib/protocol/encoders/. index.ts re-exports every module, and src/lib/protocol/mavlink-encoder.ts re-exports the barrel.

Transports

The adapters do not care how bytes arrive. Each transport implements the Transport interface in src/lib/protocol/types/transport.ts; the implementations are in src/lib/protocol/transport/. Demo mode does not use a transport to a real device. MockProtocol implements DroneProtocol directly and feeds the same stores a real connection would. The demo fleet is described on Dashboard.

The agent-side router

On a node, MAVLink does not go from the flight controller to the browser directly. The agent’s native router (ados-mavlink-router, run by the ados-mavlink unit) owns the flight controller’s serial link, fans frames out on /run/ados/mavlink.sock for other agent services, and serves the stream to Mission Control over the WebSocket on :8765. A client needs a short-lived HMAC ticket before the router bridges it to the flight controller. See MAVLink proxy.

Message flow example

Next steps

State management

How decoded telemetry reaches the stores and the UI.

Code recipes

Add a MAVLink decoder or a configuration panel step by step.
Last modified on October 11, 2026