implement 32 bit system IDs - #1229
Open
tridge wants to merge 10 commits into
Open
Conversation
This was referenced Jul 15, 2026
tridge
force-pushed
the
pr-sysid-32
branch
2 times, most recently
from
July 15, 2026 23:40
e946cd6 to
9e2ef3a
Compare
Implements the MAVLink2.1 extended headers from mavlink/rfcs#20, amended to a 32 bit system ID so IPv4 addresses can be used directly as system IDs: - MAVLINK_IFLAG_SYSID32: set only when the sender sysid is over 255, the header sysid field widens in place from 1 to 4 bytes - MAVLINK_IFLAG_TARGETTED: set only when the target sysid is over 255, target_sysid[4] and target_compid[1] are appended to the header and the payload target byte is zeroed - MAVLINK_CFLAG_SYSID32 is always set in compat_flags to advertise capability. Old parsers ignore compat_flags, so frames with small IDs remain compatible with existing MAVLink2.0 parsers The wire header now varies in length (10 to 18 bytes), so header serialisation is centralised in mavlink_header_to_send_buffer() and used by finalize, mavlink_msg_to_send_buffer, _mavlink_resend_uart and signature check. The signature sha256 previously hashed the message struct as if it were the wire header; it now hashes the re-serialised header, which is also required by the wider struct sysid field. New finalize/send entry points carry an explicit target so the convenience send path (which never builds a mavlink_message_t) can emit the extended header. MAVLink1 output with a 32 bit ID is an error, never a truncation, as a masked sysid would alias another system and a zeroed target byte would become a broadcast. mavlink_msg_get_target_sysid()/mavlink_msg_get_target_compid() give routers a single call for the effective target of any message. On receive TARGETTED is honoured for any message, including ones with no payload target fields, for forward compatibility with full-RFC senders.
Tag target_system/target_component fields in mavparse (keyed on the routing semantics, so MANUAL_CONTROL.target is included) and use the tags in the C generator: - pack/send/encode system_id and target_system arguments widen to uint32_t. The payload struct stays wire-layout so its target byte is written as zero when the target is over 255 and the target is passed through to the _target finalize/send entry points - per-message get_target_system()/get_target_component() return the extended header target when MAVLINK_IFLAG_TARGETTED is set - decode() reads the raw payload byte for target fields so the wire struct is bit-exact in both the aligned and byte-swap builds; code needing the full 32 bit target must use the getters MAVLink1 generation is unchanged.
Round trips for all four sysid/target width combinations, golden legacy-compat check (frames with small IDs differ from MAVLink2.0 output only in compat_flags and CRC, and compat_flags 0 frames still parse), convenience-send and resend_uart byte-exact checks, signing over the variable-length header including corruption cases, MAVLink1 truncation guards and parser recovery from a truncated extended header.
Mirrors the C implementation: SYSID32 set only when srcSystem is over 255, TARGETTED only when a message's target_system field is over 255 (the payload byte is then zeroed), MAVLINK_CFLAG_SYSID32 always advertised, variable header length handled in framing and decode, and MAVLink1 packing raises MAVError for 32 bit IDs rather than truncating. On decode the extended header target is overlaid onto the decoded message fields, so existing code reading msg.target_system keeps working for targets over 255. New get_target_system() and get_target_component() return the effective target of any message. This also fixes get_payload() for MAVLink2: the payload slice previously started at offset 6 (the MAVLink1 header length) and so included 4 header bytes.
Round trips for all sysid/target width combinations, signing over extended headers including corruption cases, MAVLink1 guards, legacy compat framing, and golden frames produced by the C implementation to pin C/Python cross-language wire compatibility.
The CS and JavaScript runtimes read a fixed MAVLink2 header length and would consume the wrong byte count on MAVLink2.1 extended-header frames, mis-framing the stream instead of cleanly dropping them. Drop frames with incompatible flags the parser does not understand, and consume the extended header bytes so the stream stays in sync. The Java parser already rejects unknown incompat flags.
The Lua module parses the in-memory mavlink_message_t structure, not the wire format. The sysid field widened to 32 bits, moving the compid, msgid and payload offsets, and the CRC check must reconstruct the wire header (which depends on the SYSID32/TARGETTED flags) rather than assuming the structure matches the wire bytes. The extended header target is exposed as target_sysid/target_compid. Users often copy current scripts onto older firmware, so the layout is detected at runtime from the structure size: 291 bytes for the older 1 byte sysid layout, 299 bytes for the 32 bit sysid layout. decode_header additionally returns the payload offset as a second return value so callers do not need layout knowledge of their own.
Full MAVLink2.1 support for the NextGen JavaScript generator, mirroring the C and Python implementations: SYSID32 set only when the source system is over 255, TARGETTED only when a message's target_system field is over 255 (the payload byte is then zeroed), MAVLINK_CFLAG_SYSID32 always advertised, variable header length in framing and decode, extended header targets overlaid onto decoded fields, and MAVLink1 packing throws for 32 bit IDs. The static test fixtures gain the compat_flags advertisement and make_tests.py expects it for MAVLink2 frames, keeping the byte-level cross-check against the C implementation exact. Adds round trip tests for 32 bit source and target IDs.
…ages Negative array indexing returns undefined in JavaScript, so _link_id was never set from the signature block. Use slice instead, matching check_signature().
the MAVLink1 path skips the GOT_LENGTH state, which is where target_sysid and target_compid are cleared, so a re-used receive buffer kept the target from a previous MAVLink2 frame. Forwarding that MAVLink1 frame then silently failed, as _mav_header_pack() treats a target over 255 as unrepresentable in MAVLink1 and returns 0. This broke forwarding on a mixed-version link. The existing test could not catch this as it resets the parser buffer for every frame; the new test re-uses one parser across both frames.
| buf[ofs++] = (target_sysid >> 16) & 0xFF; | ||
| buf[ofs++] = (target_sysid >> 24) & 0xFF; | ||
| buf[ofs++] = target_compid; | ||
| } |
Contributor
There was a problem hiding this comment.
Does MAVLINK_IFLAG_TARGETTED require MAVLINK_IFLAG_SYSID32?
Suggested change
| } | |
| if (incompat_flags & MAVLINK_IFLAG_TARGETTED) { | |
| buf[ofs++] = target_sysid & 0xFF; | |
| if (incompat_flags & MAVLINK_IFLAG_SYSID32) | |
| buf[ofs++] = (target_sysid >> 8) & 0xFF; | |
| buf[ofs++] = (target_sysid >> 16) & 0xFF; | |
| buf[ofs++] = (target_sysid >> 24) & 0xFF; | |
| } | |
| buf[ofs++] = target_compid; | |
| } |
Comment on lines
+335
to
+339
| if (target_sysid > 255) { | ||
| msg->incompat_flags |= MAVLINK_IFLAG_TARGETTED; | ||
| msg->target_sysid = target_sysid; | ||
| msg->target_compid = target_compid; | ||
| } |
Contributor
There was a problem hiding this comment.
Suggested change
| if (target_sysid > 255) { | |
| msg->incompat_flags |= MAVLINK_IFLAG_TARGETTED; | |
| msg->target_sysid = target_sysid; | |
| msg->target_compid = target_compid; | |
| } | |
| if (target_sysid > 0) { | |
| msg->incompat_flags |= MAVLINK_IFLAG_TARGETTED; | |
| msg->target_sysid = target_sysid; | |
| msg->target_compid = target_compid; | |
| if (target_sysid > 255) { | |
| msg->incompat_flags |= MAVLINK_IFLAG_SYSID32; | |
| } | |
| } |
| buf[0] = magic; | ||
| buf[1] = len; | ||
| if (magic == MAVLINK_STX_MAVLINK1) { | ||
| if (sysid > 255 || target_sysid > 255) { |
Contributor
There was a problem hiding this comment.
Elsewhere, equivalent checks also check if msgid > 255.
| /* | ||
| compat_flags bits | ||
| */ | ||
| #define MAVLINK_CFLAG_SYSID32 0x01 // system is capable of understanding 32 bit system ID |
Contributor
There was a problem hiding this comment.
Would this be better as a new flag in MAV_PROTOCOL_CAPABILITY_MAVLINK2?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a WIP effort to implement 32 bit system IDs in MAVLink2. It is based on mavlink/rfcs#20 with a change to use 32 bit instead of 16 bit wide system ID.
Only C and python generation so far, plus addition of CS and JavaScript dropping of unknown incompatible flags
I am also planning on resolving the 32 bit uint32_t issue with ArduPilot parameters as part of this effort which will make using IPv4 addresses more practical