Library / SDK
mavlink-router/mavlink-router avatar
mavlink-router/mavlink-router

mavlink-router forwards drone telemetry across UART, UDP and TCP

Route mavlink packets between endpoints

621 stars378 forksC++Apache-2.0

At a glance

What is it?
mavlink-router is a C++ daemon that distributes MAVLink messages between endpoints, bridging a serial flight controller to UDP or TCP ground stations and filtering by system and component ID. It suits a drone setup with more than one consumer of the telemetry link, and it is a router rather than a flight stack.
Who is it for?
Adopt mavlink-router when more than one process needs the MAVLink stream from a vehicle, whether that is a ground station, an offboard API or a companion computer, and configure it with a file rather than command line flags once the topology is settled. Skip it if you have a single consumer of the serial link, since a direct connection needs no router.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It solves the problem of one serial link with several consumers

MAVLink Router is an application to distribute MAVLink messages between multiple endpoints, and it distributes packets to a single port or multiple endpoints depending on the target address. The problem it addresses is concrete. A vehicle has a flight controller on a UART, and there may be a ground station, an offboard computer, a companion sensor process and a monitoring dashboard that all want the same telemetry stream. Without a router, each consumer needs its own radio link or a multiplexer, and the vehicle ends up with several serial ports. The router sits in the middle and fans the packets out. Connections can be made via UART, UDP or TCP, and the primary language is C++ with the MAVLink C library pulled in as a submodule, which is a sensible choice for a process whose job is to move bytes efficiently rather than to do anything clever with them.

The system and component ID pair decides where a packet goes

The forwarding logic rests on a property of the MAVLink message format, and the README explains it carefully because it is the part that surprises people. A MAVLink message has to have a sender address, but only some messages have a target address, for example parameter requests. The sender and target address consist of a system ID and a component ID each, so one UAV can have a flight controller as well as other services running on a companion computer with individual component IDs, but sharing the same system ID. In the target address a component ID of 0 is used to broadcast to all components on a system, and a system ID of 0 broadcasts to all systems. So the router is not a blind repeater. It understands addressing, which is what lets it serve several consumers without every consumer receiving every message and having to filter it out again.

UDP endpoints behave differently depending on client or server mode

The endpoint documentation is where the real behaviour lives, and the UDP case is the one to read carefully. A UDP endpoint is configured with a mode of either client or server, plus an IP address and port. In client mode the endpoint is configured with a target IP and port combination, so MAVLink messages can be sent directly after startup, but the endpoint will only receive after the first message was received by the remote side, because it does not know our IP and port otherwise. When a non-unicast address is used, such as an IPv4 broadcast or IPv6 local network multicast at ff02::1, messages are broadcast until somebody sends data back, and from that point UDP packets are only sent to the specific unicast IP of the answering device. When no MAVLink messages are received for 5 seconds, the endpoint switches back to broadcast mode using the configured non-unicast address. In server mode the endpoint is configured with a listening port and IP address. The practical consequence is that a UDP link in client mode has a dead period at startup, and one that goes quiet for five seconds reverts to broadcasting.

A command line gets a simple topology running in one line

The daemon can be configured entirely from the command line, and the README gives the canonical example. To route mavlink packets from UART ttyS1 to 2 other UDP endpoints:

bash
    $ mavlink-routerd -e 192.168.7.1:14550 -e 127.0.0.1:14550 /dev/ttyS1:1500000

The 1500000 after the colon in the device path sets the UART baudrate, so the serial rate is specified inline rather than in a separate option. The same shape works for an incoming UDP connection instead of a UART, with the last positional argument being the listening address and port:

bash
    $ mavlink-routerd -e 192.168.7.1:14550 -e 127.0.0.1:14550  0.0.0.0:24550

The important asymmetry in the flags, spelled out in the documentation, is that the TCP server is enabled by default, TCP and UDP endpoints can be added multiple times, and UDP endpoints added with the -e option are started in normal mode sending data to the specified address and port, while the last parameter without a key is a UART device or a UDP connection and that one starts in server mode waiting for an incoming connection. The daemon also listens on port 5760 for TCP by default, and any client connecting to that port can send and receive MAVLink data.

Configuration files and command line flags can be combined

The two configuration mechanisms are documented as alternatives that can also be mixed, and the CLI options are merged with the settings from the config file when both are used. The division of labour is described plainly. The configuration file gives more fine-grained control over the endpoints, while the CLI options enable a quick configuration, and when using the systemd unit added by the install step the recommendation is to use the configuration file rather than editing the systemd file. The daemon looks for the main configuration at /etc/mavlink-router/main.conf by default, overridable through the MAVLINK_ROUTERD_CONF_FILE environment variable or the -c switch. There is also a configuration directory, /etc/mavlink-router/config.d by default, overridable through MAVLINK_ROUTERD_CONF_DIR or the -d switch, where files are read in alphabetical order and can add to or override configurations found in previous files. The sample configuration in examples/config.sample is the reference for the file syntax and all parameters.

The build is Meson with cross compilation files for several architectures

The build requirements are a GCC or Clang compiler, meson 0.57 or later with ninja-build, and the C and C++ standard libraries. The dependency fetch is a submodule update, since the project depends on the MAVLink C library:

bash
    $ git submodule update --init --recursive

The per distribution package lists are short, for example on Debian and Ubuntu it is git, meson, ninja-build, pkg-config, gcc, g++, systemd. The configure and build cycle is the usual Meson one, with configuration needed only once:

bash
    $ meson setup build .

The build type defaults to debugoptimized and can be changed with --buildtype=release, the installation prefix is set with the -Dprefix option, and on a system without systemd you pass the service directory with -Dsystemdsystemunitdir=/usr/lib/systemd/system. The build itself is ninja -C build and the install is sudo ninja -C build install, with a DESTDIR variant for staged installs into another root. The repository carries four cross compilation files, meson-cross-aarch64.ini, meson-cross-armhf.ini, meson-cross-i686.ini and meson-cross-sysroot.ini.in, which tells you the embedded Linux targets this is expected to run on.

The examples show the router used as a ground station test bench

The examples directory is more informative than a typical one, because it covers the roles a router plays in practice. There are Python scripts for sending and receiving, a heartbeat printer in both Python and C++, and a heartbeat sender, which is the classic way to see packets flow without a flight controller attached. There is a px4-offboard-mode example in C++, which is the PX4 offboard API scenario where a companion computer sends commands back over the same link the telemetry arrives on. There is an arm-authorizer example in C++ and an arm.py, and Dockerfiles for Alpine and Ubuntu. The pairing of a C++ heartbeat printer with a Python one also tells you the tool is meant to be interoperable with the wider MAVLink ecosystem rather than to be a closed system, and the tests/ and tools/ directories at the top level suggest the routing logic itself is covered by an automated suite.

Where mavlink-router is the wrong choice

Three situations argue against it. The first is a vehicle with exactly one consumer of the telemetry stream. If a ground station connects directly to the flight controller over the radio link, a router adds a process, a configuration file and a failure mode for no gain. The second is a system that needs more than forwarding. The router understands addressing, so it can route by system and component ID, but it does not interpret the payloads, so anything that needs to react to a specific message type, trigger an action or arbitrate between two masters talking to the same flight controller is work you write yourself, and you would be writing it as a separate process that talks MAVLink rather than relying on the router. The third is an environment where the packaging model does not fit. The install step produces a systemd unit and the documentation assumes a Linux host with a root-owned configuration under /etc, so a container without systemd needs the explicit service directory override, and a heavily sandboxed platform needs another way to supply the configuration. The realistic alternative for the simple case is a direct link, and for the multi-consumer case a small script of your own that opens the serial port and fans out to subscribers, which is a page of code and gives you exactly the filtering semantics you need.

Editorial conclusion

Adopt mavlink-router when more than one process needs the MAVLink stream from a vehicle, whether that is a ground station, an offboard API or a companion computer, and configure it with a file rather than command line flags once the topology is settled. Skip it if you have a single consumer of the serial link, since a direct connection needs no router. Verify first which endpoint modes you need, because UDP client and server behave differently on startup, and note the release history, with the v4 tag from 2024-02-13 and the last push on 2026-07-13.

Frequently asked questions

What does mavlink-router do?

It is an application to distribute MAVLink messages between multiple endpoints, sending packets to a single port or to several endpoints depending on the target address. Connections can be made via UART, UDP or TCP, and the primary language is C++.

How do I run mavlink-router for the first time?

Install the build dependencies, fetch the MAVLink C library submodule with git submodule update --init --recursive, configure with meson setup build ., build with ninja -C build and install with sudo ninja -C build install. You can then start the daemon with a command such as mavlink-routerd -e 192.168.7.1:14550 -e 127.0.0.1:14550 /dev/ttyS1:1500000, where the number after the colon sets the UART baudrate.

Where does mavlink-router read its configuration from?

By default it looks for /etc/mavlink-router/main.conf, overridable with the MAVLINK_ROUTERD_CONF_FILE environment variable or the -c switch. Extra configuration files can go in the /etc/mavlink-router/config.d directory, overridable with MAVLINK_ROUTERD_CONF_DIR or -d, and they are read in alphabetical order and can override earlier files.

How does mavlink-router decide which endpoint receives a message?

It uses the MAVLink addressing fields. The sender and target address each consist of a system ID and a component ID, a component ID of 0 in the target address broadcasts to all components on a system, and a system ID of 0 broadcasts to all systems. Only some messages carry a target address, for example parameter requests.

Is mavlink-router still maintained?

The repository is not archived and the last push was on 2026-07-13. The recent tags are v4 from 2024-02-13, v3 from 2022-11-13 and v2 from 2020-03-26, the licence is Apache-2.0, and the daemon listens on port 5760 for TCP by default with IPv6 addresses written in square brackets.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. mavlink-router/mavlink-router on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mavlink-router-mavlink-router.svg)](https://hysenlabs.com/projects/mavlink-router-mavlink-router)

Was this useful?