From 03af8e8adaa83566719afffb09e1f1e38fcff757 Mon Sep 17 00:00:00 2001 From: Hamish Willee Date: Thu, 16 Jul 2026 09:16:49 +1000 Subject: [PATCH 1/4] docs(docs): Add docs from C --- en/SUMMARY.md | 3 + en/mavgen_cpp/example_cpp_udp.md | 65 ++++++ en/mavgen_cpp/examples.md | 3 + en/mavgen_cpp/index.md | 345 +++++++++++++++++++++++++++++++ 4 files changed, 416 insertions(+) create mode 100644 en/mavgen_cpp/example_cpp_udp.md create mode 100644 en/mavgen_cpp/examples.md create mode 100644 en/mavgen_cpp/index.md diff --git a/en/SUMMARY.md b/en/SUMMARY.md index cc79ca262..a0dc172f1 100644 --- a/en/SUMMARY.md +++ b/en/SUMMARY.md @@ -12,6 +12,9 @@ - [Examples](mavgen_c/examples.md) - [UART Interface (C)](mavgen_c/example_c_uart.md) - [UDP Example (C)](mavgen_c/example_c_udp.md) + - [C++ 11 (mavgen)](mavgen_cpp/index.md) + - [Examples](mavgen_cpp/examples.md) + - [UART Interface (C)](mavgen_cpp/example_c_uart.md) - [Pymavlink (Python-mavgen)](mavgen_python/index.md) - [How to Request Messages/Set Message Rates](mavgen_python/howto_requestmessages.md) - [Message Signing](mavgen_python/message_signing.md) diff --git a/en/mavgen_cpp/example_cpp_udp.md b/en/mavgen_cpp/example_cpp_udp.md new file mode 100644 index 000000000..b29ae53ff --- /dev/null +++ b/en/mavgen_cpp/example_cpp_udp.md @@ -0,0 +1,65 @@ +# MAVLink C UDP Example + +The [MAVLink UDP Example](https://github.com/mavlink/mavlink/tree/master/examples/c) is a simple C example that sends and receives MAVLink HEARTBEATS over UDP. + +::: info +The example should work on any Unix-like system (Linux, MacOS, BSD, etc.). +These instructions were tested on a _Ubuntu LTS 22.04_ installation with either PX4 or ArduPilot dependencies installed (such as cmake). +::: + +## Building/Running the Example + +The following instructions show how to build and run the example. + +1. Clone the [mavlink/mavlink](https://github.com/mavlink/mavlink/) repository +2. Open a terminal in the repository root. +3. Use `cmake` to install MAVLink locally: + + ```sh + cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=install + cmake --build build --target install + ``` + +4. Navigate to [examples/c](https://github.com/mavlink/mavlink/tree/master/examples/c) + + ```sh + cd examples/c + ``` + +5. Use `cmake` to compile and build the example: + + ```sh + cmake -Bbuild -H. -DCMAKE_PREFIX_PATH=$(pwd)/../../install + cmake --build build + ``` + +6. Run the executable from the terminal: + + ```sh + ./build/udp_example + ``` + + By default, the example will listen for data on the localhost IP address, port 14550. + +7. Open another terminal on the same machine and start either PX4 or ArduPilot. + These publish to port 14550 on localhost by default. +8. The example should start displaying messages about sent and received HEARTBEAT messages in the terminal. + The following output is displayed if you connect to PX4: + + ```sh + ~/github/mavlink/mavlink/examples/c$ ./build/udp_example + + Sent heartbeat + Got heartbeat from PX4 autopilot + Sent heartbeat + Got heartbeat from PX4 autopilot + Sent heartbeat + Got heartbeat from PX4 autopilot + Sent heartbeat + Got heartbeat from PX4 autopilot + Sent heartbeat + Got heartbeat from PX4 autopilot + ... + ``` + +Note that the build and installation instructions are from [examples/c/README.md](https://github.com/mavlink/mavlink/blob/master/examples/c/README.md). diff --git a/en/mavgen_cpp/examples.md b/en/mavgen_cpp/examples.md new file mode 100644 index 000000000..ec283e667 --- /dev/null +++ b/en/mavgen_cpp/examples.md @@ -0,0 +1,3 @@ +# Examples + +- [UDP Example](../mavgen_cpp/example_cpp_udp.md): Simple C++ 11 example of a MAVLink UDP interface for Unix-like systems (Linux, MacOS, BSD, etc.). diff --git a/en/mavgen_cpp/index.md b/en/mavgen_cpp/index.md new file mode 100644 index 000000000..16739deab --- /dev/null +++ b/en/mavgen_cpp/index.md @@ -0,0 +1,345 @@ +# Using C++ 11 MAVLink Libraries (mavgen) + +The MAVLink C++ 11 library generated by _mavgen_ is a header-only implementation that is highly optimized for resource-constrained systems with limited RAM and flash memory. +It is field-proven and deployed in many products where it serves as interoperability interface between components of different manufacturers. + +This topic explains how to get and use the library. + +## Getting the C++ 11 MAVLink Library {#get_libraries} + +To get this library you will need to [install mavgen](../getting_started/installation.md) and [generate](../getting_started/generate_libraries.md) them yourself. +These can then be used in the same way as the pre-generated libraries. + +The libraries can be placed/generated anywhere in your project tree. + +## Adding Library to C++ Project + +This example below assumes the MAVLink headers to be located in: **generated/include/mavlink/**. + +To use MAVLink in your C project, include the **mavlink.h** header file for your dialect: + +```c +#include +``` + +This will automatically add the header files for all messages in your dialect, and for any dialect files that it includes. + +::: warning +Only include the header file for a single dialect. +If you need to support messages from a _number of dialects_ then create a new "parent" dialect XML file that includes them (and use its generated header as shown above). +::: + +::: tip +_Do not include the individual message files_. +If you generate your own headers, you will have to add their output location to your C compiler's search path. +::: + +When compiling the project, make sure to add the include directory: + +```sh +gcc ... -I generated/include ... +``` + +## Adding Library to Cmake Project + +To include the headers in cmake, install them locally, e.g. into the directory `install`: + +```sh +cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=install -DMAVLINK_DIALECT=common -DMAVLINK_VERSION=2.0 +cmake --build build --target install +``` + +Then use `find_package` to get the dependency in `CMakeLists.txt`: + +```cmake +find_package(MAVLink REQUIRED) + +add_executable(my_program my_program.c) + +target_link_libraries(my_program PRIVATE MAVLink::mavlink) +``` + +And pass the local install directory to cmake (adapt to your directory structure): + +```sh +cd ../my_program +cmake -Bbuild -H. -DCMAKE_PREFIX_PATH=../mavlink/install +``` + +### Build Warnings + +#### `-Waddress-of-packed-member` + +Building the headers may result in warnings like: + +``` +mavlink/common/../mavlink_helpers.h:86:24: warning: taking address of packed member of ‘__mavlink_message’ may result in an unaligned pointer value [-Waddress-of-packed-member] + 86 | crc_accumulate_buffer(&msg->checksum, _MAV_PAYLOAD(msg), msg->len); +``` + +The warning indicates the potential for hard faults caused by unaligned access to packed data. +This does not happen on most of the common architectures on which MAVLink is run, and generally the warning can be suppressed. + +You can suppress the warnings using `-Wno-address-of-packed-member`. + +::: info +The issue causes hard faults on [Cortex-M0](https://github.com/ArduPilot/pymavlink/issues/5) and other platforms [listed here](https://github.com/ArduPilot/pymavlink/issues/836#issue-1788623502). +Please raise issues in [ArduPilot/pymavlink](https://github.com/ArduPilot/pymavlink/) if you find other hardware that is affected. +::: + +## Upgrading Library from MAVLink 1 + +The _MAVLink 1_ pre-built library [mavlink/c_library_v1](https://github.com/mavlink/c_library_v1) can be upgraded by simply dropping in the _MAVLink 2_ library from Github: [mavlink/c_library_v2](https://github.com/mavlink/c_library_v2). + +The _MAVLink 2_ C library offers the same range of APIs as was offered by _MAVLink 1_. + +::: info +The major change from an API perspective is that you don't need to provide a message CRC table any more, or message length table. +These have been folded into a single packed table, replacing the old table which was indexed by `msgId`. +That was necessary to cope with the much larger 24 bit namespace of message IDs. +::: + +_MAVLink 2_ usage is covered in the following sections (this includes [Working with MAVLink 1](#mavlink_1) which explains how you can communicate with both _MAVLink 2_ and _MAVLink 1_ (only) libraries). + +## Multiple Streams ("channels") {#channels} + +The C MAVLink library utilizes a "channel" metaphor to allow for simultaneous processing of multiple, independent MAVLink streams in the same program. +All receiving and transmitting functions provided by this library require a channel, and it is important to use the correct channel for each operation. + +By default up to 16 channels may be defined on Windows, Linux and macOS, and up to 4 channels may be define on other systems. +Systems can specify a different maximum number of channels/comms buffers using `MAVLINK_COMM_NUM_BUFFERS` (for example, this might be reduced to 1 if running MAVLink on very memory constrained hardware). + +If only one MAVLink stream exists, channel 0 should be used by specifying the `MAVLINK_COMM_0` constant. + +## Receiving + +MAVLink reception/decoding is done in a number of phases: + +1. Parse the incoming stream into objects representing each packet (`mavlink_message_t`). +1. Decode the MAVLink message contained in the packet payload into a C struct (that has fields mapping the original XML definition). + +These steps are demonstrated below. + +### Parsing Packets + +The `mavlink_parse_char(...)` convenience function ([mavlink_helpers.h](https://github.com/mavlink/c_library_v2/blob/master/mavlink_helpers.h)) is used to parse incoming MAVLink data. +The function parses the data one byte at a time, returning 0 (`MAVLINK_FRAMING_INCOMPLETE`) as it progresses, and 1 (`MAVLINK_FRAMING_OK`) on successful decoding of a packet. +The `r_mavlink_status` parameter is updated with the channel status/errors as decoding progresses (you can check `mavlink_status_t.msg_received` to get the current byte's decode status/error and `mavlink_status_t.parse_state` for the current parse state). +On successful decoding of a packet, the `r_message` argument is populated with an object representing the decoded packet. + +The function prototype and parameters are shown below: + +```c +MAVLINK_HELPER uint8_t mavlink_parse_char(uint8_t chan, uint8_t c, mavlink_message_t* r_message, mavlink_status_t* r_mavlink_status) +``` + +Parameters: + +- `uint8_t chan`: ID of the current channel. +- `uint8_t c`: The char to parse. +- [`mavlink_message_t*`](https://github.com/ArduPilot/pymavlink/blob/c0f5bb2695a677721aa7bb8f20be40ba8274c3d4/generator/C/include_v2.0/mavlink_types.h#L108) `r_message`: On success, the decoded message. NULL if the message couldn't be decoded. +- [`mavlink_status_t*`](https://github.com/ArduPilot/pymavlink/blob/c0f5bb2695a677721aa7bb8f20be40ba8274c3d4/generator/C/include_v2.0/mavlink_types.h#L217) `r_mavlink_status`: The channel statistics, including information about the current parse state. + +Returns: `0` if the packet decoding is incomplete. `1` if the packet successfully decoded. + +The code fragment below shows the typical use of this function when reading data from a serial port (`serial`): + +```cpp +#include + +mavlink_status_t status; +mavlink_message_t msg; +int chan = MAVLINK_COMM_0; + +while(serial.bytesAvailable > 0) +{ + uint8_t byte = serial.getNextByte(); + if (mavlink_parse_char(chan, byte, &msg, &status)) + { + printf("Received message with ID %d, sequence: %d from component %d of system %d\n", msg.msgid, msg.seq, msg.compid, msg.sysid); + // ... DECODE THE MESSAGE PAYLOAD HERE ... + } +} +``` + +::: tip +The [mavlink_helpers.h](https://github.com/mavlink/c_library_v2/blob/master/mavlink_helpers.h) include other parser functions: `mavlink_frame_char()` and `mavlink_frame_char_buffer()`. +Generally you will want to use `mavlink_parse_char()` (which calls those functions internally), but reviewing the other methods can give you a better understanding of the parsing process. +::: + +### Decoding the Payload {#decode_payload} + +The message/packet object retrieved in the previous section(`mavlink_message_t`) contains fields in the [MAVLink packet/serialization](../guide/serialization.md) format - including the message id (`msgid`) and the payload (`payload64`). + +To get the fields of the _specific message_ in the packet you need to further decode the payload. +This is typically done by providing a `case` statement that maps the _ids_ of the messages you wish to decode to appropriate decoder functions. +The code fragment below shows this for two messages (the first decodes a whole message into a C struct, while the second gets just a single field): + +```c +if (mavlink_parse_char(chan, byte, &msg, &status)) { +... +``` + +```c + switch(msg.msgid) { + case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: // ID for GLOBAL_POSITION_INT + { + // Get all fields in payload (into global_position) + mavlink_msg_global_position_int_decode(&msg, &global_position); + + } + break; + case MAVLINK_MSG_ID_GPS_STATUS: + { + // Get just one field from payload + visible_sats = mavlink_msg_gps_status_get_satellites_visible(&msg); + } + break; + default: + break; + } +``` + +```c +... +} +``` + +The decoder/encoder functions and ids for each message in a dialect can be found in separate header files under the dialect folder. +The headers are named with a format including the message name (**mavlink_msg\__message_name_.h**) + +::: tip +Individual message definitions for the dialect are pulled in when you include **mavlink.h** for your dialect, so you don't need to include these separately. +::: + +The most useful decoding function is named with the pattern **mavlink_msg\__message_name_\_decode()**, and extracts the whole payload into a C struct (with fields mapping to the original XML message definition). +There are also separate decoder functions to just get the values of individual fields. + +For example, the common message [GLOBAL_POSITION_INT](../messages/common.md#GLOBAL_POSITION_INT) is generated to [common/mavlink_msg_global_position_int.h](https://github.com/mavlink/c_library_v2/blob/master/common/mavlink_msg_global_position_int.h), +and contains the following definitions: + +```c +// Message ID number definition +#define MAVLINK_MSG_ID_GLOBAL_POSITION_INT 33 + +// Function to decode whole message into C struct +static inline void mavlink_msg_global_position_int_decode(const mavlink_message_t* msg, mavlink_global_position_int_t* global_position_int) + +// C-struct with fields mapping to original message +MAVPACKED( +typedef struct __mavlink_global_position_int_t { + uint32_t time_boot_ms; /*< [ms] Timestamp (time since system boot).*/ + int32_t lat; /*< [degE7] Latitude, expressed*/ + int32_t lon; /*< [degE7] Longitude, expressed*/ + int32_t alt; /*< [mm] Altitude (MSL). Note that virtually all GPS modules provide both WGS84 and MSL.*/ + int32_t relative_alt; /*< [mm] Altitude above ground*/ + int16_t vx; /*< [cm/s] Ground X Speed (Latitude, positive north)*/ + int16_t vy; /*< [cm/s] Ground Y Speed (Longitude, positive east)*/ + int16_t vz; /*< [cm/s] Ground Z Speed (Altitude, positive down)*/ + uint16_t hdg; /*< [cdeg] Vehicle heading (yaw angle), 0.0..359.99 degrees. If unknown, set to: UINT16_MAX*/ +}) mavlink_global_position_int_t; + +// Function to get just a single field from the payload (in this case, the altitude). +static inline int32_t mavlink_msg_global_position_int_get_alt(const mavlink_message_t* msg) +``` + +### Command Decoding {#decode_command} + +A [MAVLink Command](../services/command.md) encodes a command defined in a [MAV_CMD](../messages/common.md#mav_commands) enum value into a [COMMAND_INT](../messages/common.md#COMMAND_INT) or [COMMAND_LONG](../messages/common.md#COMMAND_LONG) message. + +Command packets are parsed and decoded in the same way as [any other payload](#decode_payload) - i.e. you switch on message id of `MAVLINK_MSG_ID_COMMAND_INT`/`MAVLINK_MSG_ID_COMMAND_LONG` and call the decoder functions `mavlink_msg_command_int_decode()`/`mavlink_msg_command_long_decode()` (respectively) to get a C struct mapping the original message. + +::: info +The message types differ in that `COMMAND_INT` has `int32` types for parameter fields 6 and 7 (instead of `float`) and also includes a field for the geometric frame of reference of any positional information in the command. +::: + +To decode the specific command you then switch on the value of the `mavlink_command_int_t.command` or `mavlink_command_long_t.command` field, which contains the particular `MAV_CMD` id. + +Further interpretation and handling of the message then has to be done manually (there are no convenience functions). + +### Additional Checks + +The library have a number of `#define` values that you can set to enable various features: + +- `MAVLINK_CHECK_MESSAGE_LENGTH`: + Enable this option to check the length of each message. + This allows invalid messages to be caught much sooner. + Use if the transmission medium is prone to missing (or extra) characters (e.g. a radio that fades in and out). + Only use if the channel will only contain messages types listed in the headers. + +## Transmitting + +Transmitting messages can be done by using the `mavlink_msg_*_pack()` function, where one is defined for every message. +The packed message can then be serialized with `mavlink_helpers.h:mavlink_msg_to_send_buffer()` and then writing the resultant byte array out over the appropriate serial interface. + +It is possible to simplify the above by writing wrappers around the transmitting/receiving code. +A multi-byte writing macro can be defined, `MAVLINK_SEND_UART_BYTES()`, or a single-byte function can be defined, `comm_send_ch()`, that wrap the low-level driver for transmitting the data. +If this is done, `MAVLINK_USE_CONVENIENCE_FUNCTIONS` must be defined. + +## Message Signing + +[Message Signing (authentication)](../guide/message_signing.md) allows a _MAVLink 2_ system to verify that messages originate from a trusted source. + +The C libraries generated by _mavgen_ provide _almost everything_ needed to support message signing in your MAVLink system. +The topic [C Message Signing](../mavgen_c/message_signing_c.md) explains the remaining code that a system must implement to enable signing using the C library. + +## Connection/Heartbeat + +The sections above explain how you can send and receive messages. +What messages are sent/received depends on the systems that you're working with. +The set of messages that most systems can send are documented in [common.xml](../messages/common.md) and there are various microservices [microservices](../services/index.md) that you may want to use. + +Minimally MAVLink components should implement the [HEARTBEAT/Connection protocol](../services/heartbeat.md) as this is used by other systems as proof-of-life for the component, and also for [routing](../guide/routing.md). + +## Working with MAVLink 1 {#mavlink_1} + +This section explains how to use the MAVLink 2 C library to work with MAVLink 1 systems. + +### Version Handshaking/Negotiation + +[MAVLink Versions](../guide/mavlink_version.md) explains the [handshaking](../guide/mavlink_version.md#version_handshaking) used to determine the supported MAVLink version of either end of the channel, and how to [negotiate the version to use](../guide/mavlink_version.md#negotiating_versions). + +### Sending and Receiving MAVLink 1 Packets + +The _MAVLink 2_ library will send packets in _MAVLink 2_ framing by default. +To force sending _MAVLink 1_ packets on a particular channel you change the flags field of the status object. + +For example, the following code causes subsequent packets on the given channel to be sent as _MAVLink 1_: + +```cpp +mavlink_status_t* chan_state = mavlink_get_channel_status(MAVLINK_COMM_0); +chan_state->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1; +``` + +Incoming _MAVLink 1_ packets will be automatically handled as _MAVLink 1_. If you need to determine if a particular message was received as _MAVLink 1_ or _MAVLink 2_ then you can use the `magic` field of the message. In c programming, this is done like this: + +```c +if (msg->magic == MAVLINK_STX_MAVLINK1) { + printf("This is a MAVLink 1 message\n"); +} +``` + +In most cases this should not be necessary as the XML message definition files for _MAVLink 1_ and _MAVLink 2_ are the same, so you can treat incoming _MAVLink 1_ messages the same as _MAVLink 2_ messages. + +::: info +_MAVLink 1_ is restricted to message IDs less than 256, so any messages with a higher message ID won't be received as _MAVLink 1_. +::: + +It is advisable to switch to _MAVLink 2_ when the communication partner sends _MAVLink 2_ (see [Version Handshaking](../guide/mavlink_version.md#version_handshaking)). The minimal solution is to watch incoming packets using code similar to this: + +```cpp +if (mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status)) { + + // check if we received version 2 and request a switch. + if (!(mavlink_get_channel_status(MAVLINK_COMM_0)->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1)) { + // this will only switch to proto version 2 + chan_state->flags &= ~(MAVLINK_STATUS_FLAG_OUT_MAVLINK1); + } +} +``` + +## Examples + +The following examples show the use of the API. + +- [UDP Example](../mavgen_cpp/example_cpp_udp.md): Simple C++ example of a MAVLink UDP interface for Unix-like systems (Linux, MacOS, BSD, etc.). From 4518e79c52cc090cb207c1b035ebe7e557ed54b3 Mon Sep 17 00:00:00 2001 From: Hamish Willee Date: Thu, 16 Jul 2026 09:47:39 +1000 Subject: [PATCH 2/4] Add nullprt, consptr to cspell --- cspell.json | 2 + en/SUMMARY.md | 2 +- en/mavgen_cpp/example_cpp_udp.md | 21 ++- en/mavgen_cpp/index.md | 280 +++++++++++++++++-------------- 4 files changed, 172 insertions(+), 133 deletions(-) diff --git a/cspell.json b/cspell.json index ea40d19ec..3f57c3596 100644 --- a/cspell.json +++ b/cspell.json @@ -31,6 +31,7 @@ "COLORMODE", "compid", "configurators", + "constexpr", "CRCS", "CREAT", "cubepilot", @@ -100,6 +101,7 @@ "multicopter", "multicopters", "mydescriptivebranchname", + "nullptr", "Nutt", "nuttx", "OCTOROTOR", diff --git a/en/SUMMARY.md b/en/SUMMARY.md index a0dc172f1..30f6df18d 100644 --- a/en/SUMMARY.md +++ b/en/SUMMARY.md @@ -14,7 +14,7 @@ - [UDP Example (C)](mavgen_c/example_c_udp.md) - [C++ 11 (mavgen)](mavgen_cpp/index.md) - [Examples](mavgen_cpp/examples.md) - - [UART Interface (C)](mavgen_cpp/example_c_uart.md) + - [UDP Example (C++)](mavgen_cpp/example_cpp_udp.md) - [Pymavlink (Python-mavgen)](mavgen_python/index.md) - [How to Request Messages/Set Message Rates](mavgen_python/howto_requestmessages.md) - [Message Signing](mavgen_python/message_signing.md) diff --git a/en/mavgen_cpp/example_cpp_udp.md b/en/mavgen_cpp/example_cpp_udp.md index b29ae53ff..a885bdaac 100644 --- a/en/mavgen_cpp/example_cpp_udp.md +++ b/en/mavgen_cpp/example_cpp_udp.md @@ -1,12 +1,19 @@ -# MAVLink C UDP Example +# MAVLink C++ UDP Example -The [MAVLink UDP Example](https://github.com/mavlink/mavlink/tree/master/examples/c) is a simple C example that sends and receives MAVLink HEARTBEATS over UDP. +The [MAVLink C++ UDP Example](https://github.com/mavlink/mavlink/tree/master/examples/cpp) is a simple C++ 11 example that sends and receives MAVLink HEARTBEATs over UDP. + +It is the C++ 11 counterpart to the [C UDP example](../mavgen_c/example_c_udp.md): instead of the C pack/decode functions, it uses the generated message structs (e.g. `mavlink::minimal::msg::HEARTBEAT`) together with `mavlink::MsgMap` to serialize and deserialize to and from a `mavlink_message_t`. ::: info The example should work on any Unix-like system (Linux, MacOS, BSD, etc.). These instructions were tested on a _Ubuntu LTS 22.04_ installation with either PX4 or ArduPilot dependencies installed (such as cmake). ::: +::: warning +The C++ 11 library requires you to provide a `mavlink::mavlink_get_msg_entry()` function so that `mavlink_parse_char()` can look up the length and CRC extra of incoming messages. +This example builds it from the dialect's `MESSAGE_ENTRIES` table (see the top of `udp_example.cpp`, and the [Parsing Packets](index.md#parsing-packets) section for more detail). +::: + ## Building/Running the Example The following instructions show how to build and run the example. @@ -20,10 +27,12 @@ The following instructions show how to build and run the example. cmake --build build --target install ``` -4. Navigate to [examples/c](https://github.com/mavlink/mavlink/tree/master/examples/c) + This installs both the C (`*.h`) and C++ 11 (`*.hpp`) headers. + +4. Navigate to [examples/cpp](https://github.com/mavlink/mavlink/tree/master/examples/cpp) ```sh - cd examples/c + cd examples/cpp ``` 5. Use `cmake` to compile and build the example: @@ -47,7 +56,7 @@ The following instructions show how to build and run the example. The following output is displayed if you connect to PX4: ```sh - ~/github/mavlink/mavlink/examples/c$ ./build/udp_example + ~/github/mavlink/mavlink/examples/cpp$ ./build/udp_example Sent heartbeat Got heartbeat from PX4 autopilot @@ -62,4 +71,4 @@ The following instructions show how to build and run the example. ... ``` -Note that the build and installation instructions are from [examples/c/README.md](https://github.com/mavlink/mavlink/blob/master/examples/c/README.md). +Note that the build and installation instructions are from [examples/cpp/README.md](https://github.com/mavlink/mavlink/blob/master/examples/cpp/README.md). diff --git a/en/mavgen_cpp/index.md b/en/mavgen_cpp/index.md index 16739deab..9d1dcb182 100644 --- a/en/mavgen_cpp/index.md +++ b/en/mavgen_cpp/index.md @@ -3,12 +3,14 @@ The MAVLink C++ 11 library generated by _mavgen_ is a header-only implementation that is highly optimized for resource-constrained systems with limited RAM and flash memory. It is field-proven and deployed in many products where it serves as interoperability interface between components of different manufacturers. +The C++ 11 library is a thin wrapper around the [C library](../mavgen_c/index.md): parsing/framing (`mavlink_parse_char()`, channels, MAVLink 1 interop) is unchanged, but messages are represented as C++ structs with `serialize()`/`deserialize()` methods instead of being packed/decoded via free functions, and everything is scoped under the `mavlink` namespace. + This topic explains how to get and use the library. ## Getting the C++ 11 MAVLink Library {#get_libraries} -To get this library you will need to [install mavgen](../getting_started/installation.md) and [generate](../getting_started/generate_libraries.md) them yourself. -These can then be used in the same way as the pre-generated libraries. +Unlike the C library, there is no pre-built C++ 11 library repository to download. +You will always need to [install mavgen](../getting_started/installation.md) and [generate](../getting_started/generate_libraries.md) the headers yourself (using `--lang=C++11`), for whichever dialect(s) you need. The libraries can be placed/generated anywhere in your project tree. @@ -16,13 +18,14 @@ The libraries can be placed/generated anywhere in your project tree. This example below assumes the MAVLink headers to be located in: **generated/include/mavlink/**. -To use MAVLink in your C project, include the **mavlink.h** header file for your dialect: +To use MAVLink in your C++ project, include the **common.hpp** header file for your dialect: -```c -#include +```cpp +#include ``` This will automatically add the header files for all messages in your dialect, and for any dialect files that it includes. +Everything the library provides — message structs, enums, and the framing functions inherited from the C layer (e.g. `mavlink_parse_char()`) — is scoped under the `mavlink` namespace, with each dialect getting its own nested namespace (e.g. `mavlink::common`). ::: warning Only include the header file for a single dialect. @@ -31,13 +34,13 @@ If you need to support messages from a _number of dialects_ then create a new "p ::: tip _Do not include the individual message files_. -If you generate your own headers, you will have to add their output location to your C compiler's search path. +If you generate your own headers, you will have to add their output location to your C++ compiler's search path. ::: -When compiling the project, make sure to add the include directory: +When compiling the project, make sure to add the include directory and enable C++11 (or later): ```sh -gcc ... -I generated/include ... +g++ -std=c++11 ... -I generated/include ... ``` ## Adding Library to Cmake Project @@ -49,12 +52,15 @@ cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=install -DMAVLINK_DIALECT=common -DMAVL cmake --build build --target install ``` +This single install step generates and installs _both_ the C (`*.h`) and C++ 11 (`*.hpp`) headers (controlled by the `MAVLINK_ENABLE_CXX` cmake option, which defaults to `ON`) — there's no separate install command for the C++ headers. + Then use `find_package` to get the dependency in `CMakeLists.txt`: ```cmake find_package(MAVLink REQUIRED) -add_executable(my_program my_program.c) +add_executable(my_program my_program.cpp) +target_compile_features(my_program PRIVATE cxx_std_11) target_link_libraries(my_program PRIVATE MAVLink::mavlink) ``` @@ -87,49 +93,42 @@ The issue causes hard faults on [Cortex-M0](https://github.com/ArduPilot/pymavli Please raise issues in [ArduPilot/pymavlink](https://github.com/ArduPilot/pymavlink/) if you find other hardware that is affected. ::: -## Upgrading Library from MAVLink 1 +## MAVLink 1 -The _MAVLink 1_ pre-built library [mavlink/c_library_v1](https://github.com/mavlink/c_library_v1) can be upgraded by simply dropping in the _MAVLink 2_ library from Github: [mavlink/c_library_v2](https://github.com/mavlink/c_library_v2). +The C++ 11 library only ever generates _MAVLink 2_ headers — there is no C++ 11 equivalent of the old MAVLink-1-only [c_library_v1](https://github.com/mavlink/c_library_v1) to upgrade from. -The _MAVLink 2_ C library offers the same range of APIs as was offered by _MAVLink 1_. - -::: info -The major change from an API perspective is that you don't need to provide a message CRC table any more, or message length table. -These have been folded into a single packed table, replacing the old table which was indexed by `msgId`. -That was necessary to cope with the much larger 24 bit namespace of message IDs. -::: - -_MAVLink 2_ usage is covered in the following sections (this includes [Working with MAVLink 1](#mavlink_1) which explains how you can communicate with both _MAVLink 2_ and _MAVLink 1_ (only) libraries). +This doesn't stop you interoperating with MAVLink-1-only systems: the generated _MAVLink 2_ headers can send/receive _MAVLink 1_ framed packets on a channel-by-channel basis, in exactly the same way as the C library. +See [Working with MAVLink 1](#mavlink_1) below. ## Multiple Streams ("channels") {#channels} -The C MAVLink library utilizes a "channel" metaphor to allow for simultaneous processing of multiple, independent MAVLink streams in the same program. +The MAVLink library utilizes a "channel" metaphor to allow for simultaneous processing of multiple, independent MAVLink streams in the same program. All receiving and transmitting functions provided by this library require a channel, and it is important to use the correct channel for each operation. By default up to 16 channels may be defined on Windows, Linux and macOS, and up to 4 channels may be define on other systems. Systems can specify a different maximum number of channels/comms buffers using `MAVLINK_COMM_NUM_BUFFERS` (for example, this might be reduced to 1 if running MAVLink on very memory constrained hardware). -If only one MAVLink stream exists, channel 0 should be used by specifying the `MAVLINK_COMM_0` constant. +If only one MAVLink stream exists, channel 0 should be used by specifying the `mavlink::MAVLINK_COMM_0` constant. ## Receiving MAVLink reception/decoding is done in a number of phases: -1. Parse the incoming stream into objects representing each packet (`mavlink_message_t`). -1. Decode the MAVLink message contained in the packet payload into a C struct (that has fields mapping the original XML definition). +1. Parse the incoming stream into objects representing each packet (`mavlink::mavlink_message_t`). +2. Deserialize the MAVLink message contained in the packet payload into a generated C++ struct (that has fields mapping the original XML definition). These steps are demonstrated below. ### Parsing Packets -The `mavlink_parse_char(...)` convenience function ([mavlink_helpers.h](https://github.com/mavlink/c_library_v2/blob/master/mavlink_helpers.h)) is used to parse incoming MAVLink data. +The `mavlink::mavlink_parse_char(...)` convenience function (inherited from [mavlink_helpers.h](https://github.com/mavlink/c_library_v2/blob/master/mavlink_helpers.h)) is used to parse incoming MAVLink data. The function parses the data one byte at a time, returning 0 (`MAVLINK_FRAMING_INCOMPLETE`) as it progresses, and 1 (`MAVLINK_FRAMING_OK`) on successful decoding of a packet. The `r_mavlink_status` parameter is updated with the channel status/errors as decoding progresses (you can check `mavlink_status_t.msg_received` to get the current byte's decode status/error and `mavlink_status_t.parse_state` for the current parse state). On successful decoding of a packet, the `r_message` argument is populated with an object representing the decoded packet. The function prototype and parameters are shown below: -```c +```cpp MAVLINK_HELPER uint8_t mavlink_parse_char(uint8_t chan, uint8_t c, mavlink_message_t* r_message, mavlink_status_t* r_mavlink_status) ``` @@ -137,123 +136,123 @@ Parameters: - `uint8_t chan`: ID of the current channel. - `uint8_t c`: The char to parse. -- [`mavlink_message_t*`](https://github.com/ArduPilot/pymavlink/blob/c0f5bb2695a677721aa7bb8f20be40ba8274c3d4/generator/C/include_v2.0/mavlink_types.h#L108) `r_message`: On success, the decoded message. NULL if the message couldn't be decoded. -- [`mavlink_status_t*`](https://github.com/ArduPilot/pymavlink/blob/c0f5bb2695a677721aa7bb8f20be40ba8274c3d4/generator/C/include_v2.0/mavlink_types.h#L217) `r_mavlink_status`: The channel statistics, including information about the current parse state. +- `mavlink_message_t* r_message`: On success, the decoded message. NULL if the message couldn't be decoded. +- `mavlink_status_t* r_mavlink_status`: The channel statistics, including information about the current parse state. Returns: `0` if the packet decoding is incomplete. `1` if the packet successfully decoded. -The code fragment below shows the typical use of this function when reading data from a serial port (`serial`): +The code fragment below shows the typical use of this function when reading data from a socket/serial port (`socket_fd`): ```cpp -#include +#include -mavlink_status_t status; -mavlink_message_t msg; -int chan = MAVLINK_COMM_0; +mavlink::mavlink_status_t status; +mavlink::mavlink_message_t message; +int chan = mavlink::MAVLINK_COMM_0; -while(serial.bytesAvailable > 0) +for (int i = 0; i < num_bytes_read; ++i) { + if (mavlink::mavlink_parse_char(chan, buffer[i], &message, &status) == 1) { + printf("Received message with ID %d, sequence: %d from component %d of system %d\n", + message.msgid, message.seq, message.compid, message.sysid); + // ... DECODE THE MESSAGE PAYLOAD HERE ... + } +} +``` + +::: warning +Unlike the C library, the C++ 11 generator does **not** emit a `mavlink::mavlink_get_msg_entry()` lookup function. +`mavlink_parse_char()` needs this internally to find the length and CRC extra of an incoming message id, so you must provide it yourself before parsing will work. + +The simplest approach is to build it from the dialect's `MESSAGE_ENTRIES` table (which is sorted by message id), as shown in the [UDP example](example_cpp_udp.md): + +```cpp +namespace mavlink { + +const mavlink_msg_entry_t* mavlink_get_msg_entry(uint32_t msgid) { - uint8_t byte = serial.getNextByte(); - if (mavlink_parse_char(chan, byte, &msg, &status)) - { - printf("Received message with ID %d, sequence: %d from component %d of system %d\n", msg.msgid, msg.seq, msg.compid, msg.sysid); - // ... DECODE THE MESSAGE PAYLOAD HERE ... + for (const auto& entry : common::MESSAGE_ENTRIES) { + if (entry.msgid == msgid) { + return &entry; + } } + return nullptr; } + +} // namespace mavlink ``` -::: tip -The [mavlink_helpers.h](https://github.com/mavlink/c_library_v2/blob/master/mavlink_helpers.h) include other parser functions: `mavlink_frame_char()` and `mavlink_frame_char_buffer()`. -Generally you will want to use `mavlink_parse_char()` (which calls those functions internally), but reviewing the other methods can give you a better understanding of the parsing process. +Omitting this results in a linker error, since `mavlink_parse_char()` declares (but doesn't define) `mavlink_get_msg_entry()`. ::: ### Decoding the Payload {#decode_payload} -The message/packet object retrieved in the previous section(`mavlink_message_t`) contains fields in the [MAVLink packet/serialization](../guide/serialization.md) format - including the message id (`msgid`) and the payload (`payload64`). +The message/packet object retrieved in the previous section (`mavlink::mavlink_message_t`) contains fields in the [MAVLink packet/serialization](../guide/serialization.md) format - including the message id (`msgid`) and the payload (`payload64`). To get the fields of the _specific message_ in the packet you need to further decode the payload. -This is typically done by providing a `case` statement that maps the _ids_ of the messages you wish to decode to appropriate decoder functions. -The code fragment below shows this for two messages (the first decodes a whole message into a C struct, while the second gets just a single field): +This is typically done by providing a `switch` statement that maps the _ids_ of the messages you wish to decode to a generated message struct, then calling `.deserialize()` on it via a `mavlink::MsgMap`: -```c -if (mavlink_parse_char(chan, byte, &msg, &status)) { -... -``` - -```c - switch(msg.msgid) { - case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: // ID for GLOBAL_POSITION_INT - { - // Get all fields in payload (into global_position) - mavlink_msg_global_position_int_decode(&msg, &global_position); - - } - break; - case MAVLINK_MSG_ID_GPS_STATUS: +```cpp +if (mavlink::mavlink_parse_char(chan, byte, &message, &status) == 1) { + switch (message.msgid) { + case mavlink::common::msg::GLOBAL_POSITION_INT::MSG_ID: { - // Get just one field from payload - visible_sats = mavlink_msg_gps_status_get_satellites_visible(&msg); + // Deserialize the whole payload into the generated struct. + mavlink::common::msg::GLOBAL_POSITION_INT global_position; + mavlink::MsgMap map(&message); + global_position.deserialize(map); } break; - default: + default: break; } -``` - -```c -... } ``` -The decoder/encoder functions and ids for each message in a dialect can be found in separate header files under the dialect folder. -The headers are named with a format including the message name (**mavlink_msg\__message_name_.h**) - -::: tip -Individual message definitions for the dialect are pulled in when you include **mavlink.h** for your dialect, so you don't need to include these separately. -::: +Each generated message struct is a plain C++ class with fields mapping to the original XML message definition, plus a handful of static members used by the framing layer: -The most useful decoding function is named with the pattern **mavlink_msg\__message_name_\_decode()**, and extracts the whole payload into a C struct (with fields mapping to the original XML message definition). -There are also separate decoder functions to just get the values of individual fields. - -For example, the common message [GLOBAL_POSITION_INT](../messages/common.md#GLOBAL_POSITION_INT) is generated to [common/mavlink_msg_global_position_int.h](https://github.com/mavlink/c_library_v2/blob/master/common/mavlink_msg_global_position_int.h), -and contains the following definitions: - -```c -// Message ID number definition -#define MAVLINK_MSG_ID_GLOBAL_POSITION_INT 33 - -// Function to decode whole message into C struct -static inline void mavlink_msg_global_position_int_decode(const mavlink_message_t* msg, mavlink_global_position_int_t* global_position_int) - -// C-struct with fields mapping to original message -MAVPACKED( -typedef struct __mavlink_global_position_int_t { - uint32_t time_boot_ms; /*< [ms] Timestamp (time since system boot).*/ - int32_t lat; /*< [degE7] Latitude, expressed*/ - int32_t lon; /*< [degE7] Longitude, expressed*/ - int32_t alt; /*< [mm] Altitude (MSL). Note that virtually all GPS modules provide both WGS84 and MSL.*/ - int32_t relative_alt; /*< [mm] Altitude above ground*/ - int16_t vx; /*< [cm/s] Ground X Speed (Latitude, positive north)*/ - int16_t vy; /*< [cm/s] Ground Y Speed (Longitude, positive east)*/ - int16_t vz; /*< [cm/s] Ground Z Speed (Altitude, positive down)*/ - uint16_t hdg; /*< [cdeg] Vehicle heading (yaw angle), 0.0..359.99 degrees. If unknown, set to: UINT16_MAX*/ -}) mavlink_global_position_int_t; - -// Function to get just a single field from the payload (in this case, the altitude). -static inline int32_t mavlink_msg_global_position_int_get_alt(const mavlink_message_t* msg) +```cpp +namespace mavlink { +namespace common { +namespace msg { + +struct GLOBAL_POSITION_INT : mavlink::Message { + static constexpr msgid_t MSG_ID = 33; + static constexpr size_t LENGTH = 28; + static constexpr size_t MIN_LENGTH = 28; + static constexpr uint8_t CRC_EXTRA = 104; + + uint32_t time_boot_ms; /*< [ms] Timestamp (time since system boot). */ + int32_t lat; /*< [degE7] Latitude, expressed */ + int32_t lon; /*< [degE7] Longitude, expressed */ + int32_t alt; /*< [mm] Altitude (MSL). */ + int32_t relative_alt; /*< [mm] Altitude above ground */ + int16_t vx; /*< [cm/s] Ground X Speed (Latitude, positive north) */ + int16_t vy; /*< [cm/s] Ground Y Speed (Longitude, positive east) */ + int16_t vz; /*< [cm/s] Ground Z Speed (Altitude, positive down) */ + uint16_t hdg; /*< [cdeg] Vehicle heading (yaw angle) */ + + void serialize(MsgMap& map) const override { ... } + void deserialize(MsgMap& map) override { ... } +}; + +} // namespace msg +} // namespace common +} // namespace mavlink ``` +There are no separate "get a single field" decoder functions as there are in C — deserialize the whole struct and read the field you need. + ### Command Decoding {#decode_command} A [MAVLink Command](../services/command.md) encodes a command defined in a [MAV_CMD](../messages/common.md#mav_commands) enum value into a [COMMAND_INT](../messages/common.md#COMMAND_INT) or [COMMAND_LONG](../messages/common.md#COMMAND_LONG) message. -Command packets are parsed and decoded in the same way as [any other payload](#decode_payload) - i.e. you switch on message id of `MAVLINK_MSG_ID_COMMAND_INT`/`MAVLINK_MSG_ID_COMMAND_LONG` and call the decoder functions `mavlink_msg_command_int_decode()`/`mavlink_msg_command_long_decode()` (respectively) to get a C struct mapping the original message. +Command packets are parsed and decoded in the same way as [any other payload](#decode_payload) - i.e. you switch on `mavlink::common::msg::COMMAND_INT::MSG_ID`/`mavlink::common::msg::COMMAND_LONG::MSG_ID` and deserialize into the corresponding generated struct. ::: info The message types differ in that `COMMAND_INT` has `int32` types for parameter fields 6 and 7 (instead of `float`) and also includes a field for the geometric frame of reference of any positional information in the command. ::: -To decode the specific command you then switch on the value of the `mavlink_command_int_t.command` or `mavlink_command_long_t.command` field, which contains the particular `MAV_CMD` id. +To decode the specific command you then switch on the value of the deserialized struct's `command` field, cast to `mavlink::common::MAV_CMD`. Further interpretation and handling of the message then has to be done manually (there are no convenience functions). @@ -269,19 +268,49 @@ The library have a number of `#define` values that you can set to enable various ## Transmitting -Transmitting messages can be done by using the `mavlink_msg_*_pack()` function, where one is defined for every message. -The packed message can then be serialized with `mavlink_helpers.h:mavlink_msg_to_send_buffer()` and then writing the resultant byte array out over the appropriate serial interface. +Transmitting messages means filling in a generated message struct, serializing it, then finalizing and sending the resulting packet: + +```cpp +// Fill in the generated HEARTBEAT struct. +mavlink::minimal::msg::HEARTBEAT heartbeat; +heartbeat.type = static_cast(mavlink::minimal::MAV_TYPE::GENERIC); +heartbeat.autopilot = static_cast(mavlink::minimal::MAV_AUTOPILOT::GENERIC); +heartbeat.base_mode = 0; +heartbeat.custom_mode = 0; +heartbeat.system_status = static_cast(mavlink::minimal::MAV_STATE::STANDBY); + +// Serialize it into a mavlink_message_t ... +mavlink::mavlink_message_t message; +mavlink::MsgMap map(message); +heartbeat.serialize(map); + +// ... and finalize it by adding the header and CRC. This is where the +// system and component id as well as the sequence number get filled in. +mavlink::mavlink_finalize_message_chan( + &message, + system_id, + static_cast(mavlink::minimal::MAV_COMPONENT::COMP_ID_PERIPHERAL), + mavlink::MAVLINK_COMM_0, + heartbeat.MIN_LENGTH, + heartbeat.LENGTH, + heartbeat.CRC_EXTRA); + +// Now convert the message into a byte buffer to send it out. +uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; +const int len = mavlink::mavlink_msg_to_send_buffer(buffer, &message); +// ... write buffer[0..len) to your transport (UART, UDP socket, etc.) +``` + +Note that the message struct's own `MIN_LENGTH`, `LENGTH` and `CRC_EXTRA` static members are passed to `mavlink_finalize_message_chan()` — there's no separate table to look these up from, unlike raw use of the C API. -It is possible to simplify the above by writing wrappers around the transmitting/receiving code. -A multi-byte writing macro can be defined, `MAVLINK_SEND_UART_BYTES()`, or a single-byte function can be defined, `comm_send_ch()`, that wrap the low-level driver for transmitting the data. -If this is done, `MAVLINK_USE_CONVENIENCE_FUNCTIONS` must be defined. +Enum fields (e.g. `MAV_TYPE`, `MAV_AUTOPILOT`) are C++ 11 scoped enums (`enum class`), so they need an explicit `static_cast` when writing them into a struct field, and a `static_cast` back to the enum type when reading a field for comparison (see [Decoding the Payload](#decode_payload)). ## Message Signing [Message Signing (authentication)](../guide/message_signing.md) allows a _MAVLink 2_ system to verify that messages originate from a trusted source. -The C libraries generated by _mavgen_ provide _almost everything_ needed to support message signing in your MAVLink system. -The topic [C Message Signing](../mavgen_c/message_signing_c.md) explains the remaining code that a system must implement to enable signing using the C library. +The signing mechanism is implemented in the shared C layer that the C++ 11 library builds on, so it works the same way as for the C library. +The topic [C Message Signing](../mavgen_c/message_signing_c.md) explains the remaining code that a system must implement to enable signing (the API used is namespaced under `mavlink::` as elsewhere in this topic). ## Connection/Heartbeat @@ -293,7 +322,7 @@ Minimally MAVLink components should implement the [HEARTBEAT/Connection protocol ## Working with MAVLink 1 {#mavlink_1} -This section explains how to use the MAVLink 2 C library to work with MAVLink 1 systems. +This section explains how to use the MAVLink 2 C++ 11 library to work with MAVLink 1 systems. ### Version Handshaking/Negotiation @@ -301,21 +330,21 @@ This section explains how to use the MAVLink 2 C library to work with MAVLink 1 ### Sending and Receiving MAVLink 1 Packets -The _MAVLink 2_ library will send packets in _MAVLink 2_ framing by default. +The library will send packets in _MAVLink 2_ framing by default. To force sending _MAVLink 1_ packets on a particular channel you change the flags field of the status object. For example, the following code causes subsequent packets on the given channel to be sent as _MAVLink 1_: ```cpp -mavlink_status_t* chan_state = mavlink_get_channel_status(MAVLINK_COMM_0); -chan_state->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1; +mavlink::mavlink_status_t* chan_state = mavlink::mavlink_get_channel_status(mavlink::MAVLINK_COMM_0); +chan_state->flags |= mavlink::MAVLINK_STATUS_FLAG_OUT_MAVLINK1; ``` -Incoming _MAVLink 1_ packets will be automatically handled as _MAVLink 1_. If you need to determine if a particular message was received as _MAVLink 1_ or _MAVLink 2_ then you can use the `magic` field of the message. In c programming, this is done like this: +Incoming _MAVLink 1_ packets will be automatically handled as _MAVLink 1_. If you need to determine if a particular message was received as _MAVLink 1_ or _MAVLink 2_ then you can use the `magic` field of the message: -```c -if (msg->magic == MAVLINK_STX_MAVLINK1) { - printf("This is a MAVLink 1 message\n"); +```cpp +if (message.magic == mavlink::MAVLINK_STX_MAVLINK1) { + printf("This is a MAVLink 1 message\n"); } ``` @@ -328,13 +357,12 @@ _MAVLink 1_ is restricted to message IDs less than 256, so any messages with a h It is advisable to switch to _MAVLink 2_ when the communication partner sends _MAVLink 2_ (see [Version Handshaking](../guide/mavlink_version.md#version_handshaking)). The minimal solution is to watch incoming packets using code similar to this: ```cpp -if (mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status)) { - - // check if we received version 2 and request a switch. - if (!(mavlink_get_channel_status(MAVLINK_COMM_0)->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1)) { - // this will only switch to proto version 2 - chan_state->flags &= ~(MAVLINK_STATUS_FLAG_OUT_MAVLINK1); - } +if (mavlink::mavlink_parse_char(mavlink::MAVLINK_COMM_0, buf[i], &message, &status)) { + // check if we received version 2 and request a switch. + if (!(mavlink::mavlink_get_channel_status(mavlink::MAVLINK_COMM_0)->flags & mavlink::MAVLINK_STATUS_FLAG_IN_MAVLINK1)) { + // this will only switch to proto version 2 + chan_state->flags &= ~(mavlink::MAVLINK_STATUS_FLAG_OUT_MAVLINK1); + } } ``` From 195f15ef4085a3d78b606df2bf06da41331536a3 Mon Sep 17 00:00:00 2001 From: Hamish Willee Date: Thu, 16 Jul 2026 09:55:13 +1000 Subject: [PATCH 3/4] Update via claude --- en/mavgen_cpp/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/en/mavgen_cpp/index.md b/en/mavgen_cpp/index.md index 9d1dcb182..cb6f60891 100644 --- a/en/mavgen_cpp/index.md +++ b/en/mavgen_cpp/index.md @@ -1,7 +1,6 @@ # Using C++ 11 MAVLink Libraries (mavgen) The MAVLink C++ 11 library generated by _mavgen_ is a header-only implementation that is highly optimized for resource-constrained systems with limited RAM and flash memory. -It is field-proven and deployed in many products where it serves as interoperability interface between components of different manufacturers. The C++ 11 library is a thin wrapper around the [C library](../mavgen_c/index.md): parsing/framing (`mavlink_parse_char()`, channels, MAVLink 1 interop) is unchanged, but messages are represented as C++ structs with `serialize()`/`deserialize()` methods instead of being packed/decoded via free functions, and everything is scoped under the `mavlink` namespace. From 94b30709790fb472c6b2d9301235424de7f26754 Mon Sep 17 00:00:00 2001 From: Hamish Willee Date: Thu, 16 Jul 2026 13:16:33 +1000 Subject: [PATCH 4/4] Update en/mavgen_cpp/index.md --- en/mavgen_cpp/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/mavgen_cpp/index.md b/en/mavgen_cpp/index.md index cb6f60891..ed5aeeda2 100644 --- a/en/mavgen_cpp/index.md +++ b/en/mavgen_cpp/index.md @@ -1,4 +1,4 @@ -# Using C++ 11 MAVLink Libraries (mavgen) +# Using C++ 11 (mavgen) The MAVLink C++ 11 library generated by _mavgen_ is a header-only implementation that is highly optimized for resource-constrained systems with limited RAM and flash memory.