ados-mavlink-router) that runs as the ados-mavlink systemd service. It opens the flight controller link, parses MAVLink frames and distributes them to the rest of the system: Mission Control over WebSocket, other services over IPC sockets, the cloud relay and the terminal status page.
Flight controller auto-detection
mavlink.source picks the transport: auto (the default), serial, udp or tcp. With source: auto and mavlink.serial_port empty, the proxy scans serial ports:
- Ports listed in the board profile’s
uart_paths(for example/dev/ttyS0,/dev/ttyAMA0) - USB serial devices at
/dev/ttyACM*and/dev/ttyUSB*
Network flight controllers
A flight controller or simulator reachable over the network uses audp: or tcp: form in mavlink.serial_port. Set mavlink.source to match, or leave it at auto and the prefix decides.
Configuration keys
What baud your drone is actually running
mavlink.baud_rate is only honoured when mavlink.serial_port names a port. With the port left empty, the configured baud is not used at all and the proxy sweeps this list in order:
mavlink.baud_rate on its own changes nothing, so pin mavlink.serial_port alongside it if you want a specific rate. And a fresh install behaves differently depending on what was plugged in at the time: the installer writes the detected port into the config when a flight controller is attached, which pins the link and skips the sweep, while a unit installed with no flight controller attached ships with an empty port and probes on every connect.
Read back what the link actually settled on with ados status, rather than reading it off the config file.
Multi-client distribution
The proxy is a one-to-many bridge. A single flight controller link feeds every consumer:IPC sockets
Two Unix domain sockets carry data between services:/run/ados/mavlink.sock (binary): raw MAVLink frames with a 4-byte big-endian length prefix before each frame. Any service or script can connect and receive the full MAVLink stream.
/run/ados/state.sock (msgpack): the vehicle state as length-prefixed msgpack (contract state.v2), a 4-byte big-endian length followed by a body {v, s}, published at about 10 Hz. s holds attitude, position, speed, battery, GPS and armed state. The health service, cloud relay and TUI read it. The frame format is documented in Agent services.
WebSocket endpoint
The proxy exposes a WebSocket endpoint on port 8765 (set inmavlink.endpoints). This is how Mission Control and other browser ground stations connect to the drone.
The credential for that endpoint is a short-lived HMAC ticket. A client asks the agent for one with POST /api/_ws/ticket, which is itself gated by the pairing key, and presents it on connect as a WebSocket subprotocol:
X-ADOS-Key header instead.
The WebSocket carries raw binary MAVLink frames, with no encoding changes and no JSON wrapping. Mission Control decodes MAVLink in the browser using the same CRC and payload tables as pymavlink.
Companion heartbeat
The proxy sends a 1 Hz companion heartbeat to the flight controller withMAV_TYPE_ONBOARD_CONTROLLER. This keeps ArduPilot’s GCS failsafe from triggering when the companion computer is the only MAVLink endpoint.
Without this heartbeat, ArduPilot assumes the GCS has disconnected after 5 seconds and can trigger RTL or land depending on failsafe settings.
Stream requests
On connection, the proxy asks the flight controller for these messages withSET_MESSAGE_INTERVAL:
The first request fires immediately on connection. The proxy then re-sends the requests on an adaptive cadence between 10 and 60 seconds (30 seconds by default), shortening it when the stream stalls, to cover flight controller reboots and buffer stalls.
iNav, Betaflight’s MAVLink telemetry and ArduPilot before 4.1 ignore
SET_MESSAGE_INTERVAL and honour only the legacy REQUEST_DATA_STREAM. For those, set mavlink.legacy_stream_request: true. It is off by default because firmware that honours both mechanisms streams roughly twice the requested telemetry.Reconnection
If the flight controller link drops (power cycle, USB disconnect, cable fault), the proxy:- Closes the old connection cleanly
- Retries every 3 seconds, indefinitely
- Runs auto-detection again when the source is
auto - Re-requests all message streams once reconnected
MAVLink signing
The agent is a transparent pipe for signed frames. The 32-byte key lives in the Mission Control browser as raw bytes in IndexedDB; the agent never persists it. The agent exposes/api/mavlink/signing/capability, /api/mavlink/signing/enroll-fc and related routes so Mission Control can push the key to the flight controller once via SETUP_SIGNING.
See MAVLink signing for the full guide.
The agent config file does not carry a signing key. Any legacy
mavlink.signing block in an old config is ignored with a deprecation warning on agent start.Checking MAVLink status
From the terminal:Next steps
MAVLink signing
Sign every command so only your browser can control the flight controller.
Video pipeline
How the camera feed is encoded and served.
Configuration
Where
mavlink.* lives in the agent config.Drone Agent troubleshooting
Fix a flight controller that does not connect.