What problems do you have with MavLink? I will say I went on a (thankfully successful) goose chase this morning to figure out how to calculate the "CRC extra byte". I found the answer 2 ways: (1) by asking ChatGPT how to do it using the Python lib, and (2) by compiling ArduPilot and diving 10 folders deep into its build folder.
The main issue other than this I've found is it requires 12 bytes of overhead; could be shortened to half this. What problems have you found?
DroneCAN... now that's a hot mess. I think you will be pleasantly surprised with MavLINK after trying to implement that. I can go into details, but it's a more exciting experience if the jump scares aren't ruined!
The C library has many problems. First, many of the constants are provided as macros (completely valid for C) but this can become a problem in C++ which is generally migrating towards constexpr functions or objects instead of macros. Second, many of the macros specify de/serialization to bitfields and the bitfields raise compiler warnings about safety when compiled in C++ with `-Wall -Wextra`. That's on top of the library being far too complex. I understand the need for an XML generator, but as far as I understand, the C library and C++ library do not provide an easy way to dynamically specify message types at runtime instead of at compile time (contrast with other message protocols). The library's headers are sensitive to the order they're included and they provide configuration via declarations. One of the configuration items is "how many" global "channels" to allocate. These global channels are not thread safe and are used by, eg, QGroundControl.
The mavsdk library for C++ is a wrapper around the C library (for the most part) and it brings additional problems. Using the C library means that things aren't very type-safe under the hood. Mavsdk's use of C++ wants to be modern but makes several design choices that I disagree with, particularly around threading (instantiating the C++ library creates a thread to handle its own event queue and creating sockets via mavsdk C++ library creates additional one thread per socket) and serialization (the C++ code does not usefully provide type safe serialization. It's a common pattern but definitely not helpful on power-limited devices: power consumption and latency is higher compared to asynchronous socket programming, both of which are important for flight duration and control feedback. These design choices also make it difficult to unit test with Googletest's EXPECT_DEATH tests which uses `fork()` and can be sensitive to thread problems.
Auterion is writing a Mavlink library, libmav. I haven't yet looked at its internals, but I understand they want to address a lot of the shortcomings of the official mavsdk C++ library. So I have high hopes for that ... but alas already have things written to mavsdk's API.
Speaking of threading problems, I've encountered problems with QGroundControl's use of threads. A pattern common among these libraries is poor use of lifetime management, and poor use of shared_ptr and/or mutex to guard against races. It's the typical kind of thing that even experienced engineers make ... when they don't use tooling such as Thread Sanitizer or Address Sanitizer to warn (and raise the warning to an error) and/or insufficient test coverage.
Past the libraries, Mavlink protocol itself also leaves a lot to desire. It's a protocol that wants to be at nearly every layer in the OSI model. It smells of reinventing the wheel at all of those layers. At layer 2, there's the fact that mavlink is designed to be transmitted directly over a radio, telephone modem, a serial bus such as RS232, or even a multi-component bus such as I2C. At layer 3 we have device and component addressing, and forwarding. At layer 4 we have message sequences, checksums, and retransmissions. Then layer 5 is a little fuzzy. Even at layer 6 there is a custom file transfer protocol. It has custom implementations of a terminal session, !
It stinks to the core of a hobby protocol which "matured" by reinventing every wheel there could be. That's just my observation as a 20-year software engineer who entered the hardware/embedded field a few years ago.
Some components or implementations/firmwares have different interpretations of the meaning of data fields. If a coordinate is XYZ then what is the frame of reference for the XYZ? If you've got an orientation with yaw/pitch/roll, then what is the frame of reference? Is altitude based on AMSL or AGL or pressure instrumentation? Are we using WGS84 or something else? Even some messages themselves are deprecated, and there are some custom messages that vendors will use (remember: using custom messages requires a recompile of the library, with a customization to add the message definitions).
That's just what I have time to write off the top of my head. There's a ton more problems with mavlink, from an experienced software engineer's perspective.
Hey - Thank you for the detailed insights! I appreciate all the info. For some context, I am writing bare-metal in Rust. I've been using MavLINK for some basic stuff that is self-contained; trying to use a MAVLink Gimbal this weekend. The naive approach didn't work, but we'll see. I will hint that DroneoCAN: #1: Uses bit-alignment. Even if that means throwing off the alignment on an 80 byte message to save 3 bits. #2: Uses something similar and as poorly-document as the CRC_EXTRA byte based on message format. #3: Uses inscrutable ID assignment and Get/Set APIs. I have posted on various Githubs and discords, and the verdict is I'm the only out of touch. Ok. Fine. I will and have complied with the standards, but I am not obliged to like them, and I think they have severe engineering problems. Thank you for your insight on MavLINK. I am tolerating it for now for gimbal operations, but see the writing on the wall for the custom messages I'm using it for. Like, I need 1 byte for payload size, 1 byte for message type, maybe 1-2 for CRC... the rest of the header is a liability for OTA transmission time.
I propose we don't even talk about MSP; Holy fuck.
Don't misunderstand me: mavlink gets the job done which is better than nothing. But its use requires careful consideration about scope and error handling -- more careful than should be necessary if COTS were used instead. I seriously wish I could have a few years to rewrite the whole stack.
> I am tolerating it for now for gimbal operations
Make sure you're using latest (there was a recent fix to a crash in the gimbal manager and reconnect code recently). The mavlink guys are usually pretty responsive to mavlink support questions on Github issues and discord. Many of the vendors are also on discord, useful if you're integrating with components.
> I need 1 byte for payload size, 1 byte for message type, maybe 1-2 for CRC... the rest of the header is a liability for OTA transmission time.
Yup, exactly. If the component and you are both already connected to an IP network then there are more industry standard protocols to use which perform better (battery, bandwidth, latency, the whole shebang) perform in a more expected behavior (a single message to a single IP destination is has well-defined routing rules, but a single mavlink message is likely broadcasted and possibly broadcasted multiple times (even when deployed to a single system), and can be secured better because security tools talk IP.
You mention DroneoCAN - I assume that's a typo for DroneCAN [0]. Can you use ROS2 [1]?
Contrast ROS2 with mavlink. The communication layer between different ROS2 components (nodes) can be changed for different implementations of transport and I've seen things like shared-memory (same-host, saw it at a drone conference) or replacing with another COTS (I don't remember which one though, but I think it was ZeroMQ or based on it).
If you can provide a ros service then that's a step in the right direction as far as I'm concerned. ROS already has existing services to interface with CAN [2]. Even if you have some specific reason to need DroneCAN, there appears to be a an example for using ROS2 to talk to DroneCAN [3].
Interesting in regards to ROS2; I'd not heard of it. It sounds like a type of RTOS or library? I'm not sure if I can use it; my devices are bare metal currently. I am using DroneCAN because it is #1: A bus. #2: Differential signaling. #3: plug+play with an ecosystem. It's a great way to set up decoupled systems, where the flight controller isn't doing all the IO.
I should note that my perspective subtly clashes with the designers of both MavLINK and DroneCAN: think these protocols should operate as you'd read a hardware datasheet: It should be easy to implement in any language by referencing a byte-aligned table, and have explicit instructions. Because, it's just bytes down the wire. The DroneCAN creators, and it sounds like MavLINK as well consider it something where you should rely on an official library. The Get/Set API is a hot mess? It doesn't matter if you expect no one to implement it because the expectation is to use the official library. You will also note that DroneCAN (and maybe mavlink? Not sure) devices have poor or no protocol documentation, which is astounding to me.
ROS2 is not a RTOS library. It's a set of libraries and tools. Though it can be installed in a real-time OS, it can also be installed in eg a Dockerfile.
> You will also note that DroneCAN (and maybe mavlink? Not sure) devices have poor or no protocol documentation, which is astounding to me.