Skip to content

Use standard NMEA2000 3/5 fast-packet split + re-enable sequence check - #3

Open
kaanow wants to merge 3 commits into
extrafu:mainfrom
kaanow:fix/fastpacket-standard-split
Open

Use standard NMEA2000 3/5 fast-packet split + re-enable sequence check#3
kaanow wants to merge 3 commits into
extrafu:mainfrom
kaanow:fix/fastpacket-standard-split

Conversation

@kaanow

@kaanow kaanow commented Jul 28, 2026

Copy link
Copy Markdown

What

Switch the fast-packet control-byte split from 4/4 (nibble) to the standard
NMEA2000 3-bit sequence counter + 5-bit frame index, and re-enable the
sequence-integrity check that was disabled because of the old split.

Why

XanbusMessage.append_bytes currently reads sequence_id = b0 >> 4,
frame_id = b0 & 0x0F. The NMEA2000 fast-packet spec is seq = b0 >> 5,
frame = b0 & 0x1F.

The two are indistinguishable for messages of ≤16 frames (the frame index's
high bit stays 0), which is why most PGNs decode fine. But PGN 0x1F0BE
("MPPT Data") exceeds 16 frames
— and at frame 16 the frame index's bit 4
flips and, under the nibble split, leaks into the sequence id (and can make
frame 16 look like a fresh frame-0). That's exactly why the sequence check is
currently commented out:

# Disabled for now, that won't work for PGN 1f0be since it'll send packets that
# are larger than 16 chunks

With the spec-correct 5-bit frame index, the sequence id is stable across all
frames, so the integrity check is safe to re-enable for every PGN including
0x1F0BE — rejecting partially-missed / interleaved messages instead of blindly
concatenating them.

Test

Adds test_fastpacket_reassembly.py, which synthesises a 22-frame (>16)
message and checks:

  1. XanbusMessage (with this change) reassembles it correctly;
  2. the same frames trip the integrity check under the old 4/4 split (the bug);
  3. a ≤16-frame message still reassembles (no regression).
payload=150B -> 22 frames (>16)
PASS: XanbusMessage (3/5) reassembles the 22-frame message correctly
PASS: 3/5 reference completes and matches payload
PASS: 4/4 nibble reference fails on the same frames (bogus=True)
PASS: <=16-frame message still reassembles (no regression)
ALL TESTS PASSED

Scope / honesty

The test exercises the reassembly logic on synthesised frames. It does not
prove that a real Conext 0x1F0BE uses the 3/5 encoding — only that a >16-frame
fast-packet message requires a ≥5-bit frame index (4 bits can't count past 16),
so the 4/4 split cannot be correct for it, and that the code now handles the
standard layout. I don't have a >16-frame 0x1F0BE capture to verify against
(the install I tested on emits a 9-frame 0x1F0BE), so a check against your
hardware would be very welcome.

For what it's worth, I arrived here while validating your PGN layouts against a
second Conext system (SW 4024 + MPPT 60 150): both splits decode the short PGNs
127172/127173 identically, and battery voltage matched Modbus ground truth
(26.5 V, r=0.98) — so those layouts are confirmed on independent hardware. Thanks
for berrybms; it was the key that unlocked our decode.

kaanow and others added 3 commits July 27, 2026 23:00
berrybms splits the fast-packet control byte as 4/4 (nibble), but the NMEA2000
spec is a 3-bit sequence counter + 5-bit frame index. The two are
indistinguishable for messages of <=16 frames, which is why most PGNs work. But
PGN 0x1F0BE ("MPPT Data") exceeds 16 frames: at frame 16 the frame index's bit 4
flips and, under the nibble split, leaks into the sequence id (and can make
frame 16 look like a fresh frame-0) -- which is exactly why the sequence-
integrity check had to be disabled, leaving reassembly to trust blind
concatenation with no drop/interleave protection.

This switches to the spec-correct 5-bit frame index and re-enables the sequence
check, so partially-missed / interleaved messages are rejected for every PGN
including 0x1F0BE.

Adds test_fastpacket_reassembly.py: synthesises a 22-frame (>16) message and
verifies XanbusMessage reassembles it under 3/5, that the same frames trip the
integrity check under the old 4/4 split, and that a <=16-frame message still
reassembles (no regression). All pass.

Scope/honesty: the test exercises the reassembly *logic* on synthesised frames.
It does not prove a real Conext 0x1F0BE uses the 3/5 encoding -- only that a
>16-frame message *requires* a >=5-bit frame index (4 bits can't count past 16),
so the 4/4 split cannot be correct for it, and that the code now handles the
standard layout. Verification against a real >16-frame 0x1F0BE capture is
welcome. Separately validated on a second Conext install (SW 4024 + MPPT 60 150):
both splits decode the short PGNs 127172/127173 identically (battery 26.5 V vs
Modbus, r=0.98), confirming those layouts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MPPT 60 150 emits 21-byte DcSrcSts2 (processDcSrcSts2's fixed 27-byte
  unpack raises struct.error on this device)
- MPPT 60 150 does not report PV-side current/power on any interface;
  production must come from the assoc 0x03 output channel
- Core layouts cross-validated against InsightHome Modbus ground truth
  (battery V r=+0.98)
- Variance survey of undecoded PGNs + Modbus energy-counter labels for 1f0be

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant