NMEA 2000 on the bus
If you have read the J1939 page, you already understand most of NMEA 2000. That is not an accident: NMEA 2000 is built directly on J1939. Same 29-bit CAN identifier, same 250 kbit/s wire, same PGN/source/priority model, same address-claiming mechanism. N2K took the heavy-vehicle networking stack and pointed it at the marine and positioning world — depth, heading, wind, and above all GNSS position.
In agriculture, NMEA 2000 is how the GNSS receiver, the compass, and weather sensors put
their data on the bus, and it frequently shares the same physical bus as ISOBUS. So a
decoder that wants position has to speak N2K. That is what machbus::nmea is for.
What N2K reuses from J1939
┌─────────────────────────────────────────────────────────────┐
│ NMEA 2000 (positioning / marine) │
│ GNSS · heading · attitude · wind · depth · environment │
├─────────────────────────────────────────────────────────────┤
│ SAE J1939 │
│ 29-bit identifier · PGN/SPN · source addr · priority │
│ address claiming (NAME) · transport for big messages │
├─────────────────────────────────────────────────────────────┤
│ CAN 2.0B @ 250 kbit/s │
└─────────────────────────────────────────────────────────────┘
Concretely, N2K inherits:
- The identifier layout — priority, EDP/DP, PF, PS, source — decoded by the same
net::Identifier. A GNSS PGN cracks open exactly like an engine PGN. - PGNs as message identity. N2K PGNs are large numbers (129025, 127250, …) because many live in the broadcast (PDU2) range, where the PS byte folds into the PGN.
- Address claiming and NAME. N2K devices claim an address and announce a NAME just as J1939/ISOBUS nodes do. As a pure listener you ignore this; as a participant you do not.
- Little-endian byte order and not-available sentinels. Same rules: LSB first,
all-ones means “no data.” A latitude of all-
0xFFis no fix, not a real coordinate.
What N2K adds is mostly a different catalogue of PGNs and a different multi-frame mechanism.
Fast Packet — N2K’s multi-frame transport
J1939 carries big messages with TP/BAM/ETP. NMEA 2000 has its own scheme for the moderately-large records it sends constantly — most importantly a detailed GNSS fix that does not fit in eight bytes. That scheme is Fast Packet.
Fast Packet is lighter than full transport: no announce, no handshake. It steals a few bits at the start of the payload for sequencing, so the frames self-describe how to reassemble.
First frame of a Fast Packet sequence
┌──────────┬──────────┬──────────────────────────────────────┐
│ seq/frame│ total len│ data bytes (start of the record) │
│ counter │ (1 byte) │ │
└──────────┴──────────┴──────────────────────────────────────┘
Continuation frames
┌──────────┬─────────────────────────────────────────────────┐
│ seq/frame│ more data bytes │
│ counter │ │
└──────────┴─────────────────────────────────────────────────┘
counter encodes [ sequence id | frame number ]
→ group frames by sequence id, order by frame number, drop the
header bytes, concatenate → one whole record.
The mental model:
- The first frame carries the total byte count and the first chunk.
- Continuation frames carry an incrementing frame number within the same sequence id.
- You group by sequence id, sort by frame number, strip the counter bytes, and concatenate to recover the full record. Then you decode it like any single-frame PGN.
Because Fast Packet is broadcast and needs no handshake, a pure listener can reassemble it — exactly the property that makes N2K position decodable without joining the network.
The navigation and GNSS PGNs
This is the heart of why you would decode N2K in a field. A GNSS receiver does not send one “position” message; it spreads position, velocity, orientation, and quality across several PGNs at different rates. A decoder typically fuses them.
| PGN | Name | What it carries |
|---|---|---|
| 129025 | GNSS Position, Rapid Update | Latitude / longitude only, sent fast (the steering feed) |
| 129026 | COG & SOG, Rapid Update | Course over ground + speed over ground, sent fast |
| 129029 | GNSS Position Data | The full fix: lat/lon/alt, fix type, sats, ref station |
| 129539 | GNSS DOPs | Dilution of precision (HDOP/VDOP/PDOP) — fix quality |
| 129540 | GNSS Satellites in View | Per-satellite detail (PRN, elevation, azimuth, SNR) |
| 127250 | Vessel/Vehicle Heading | Heading (true or magnetic), deviation, variation |
| 127251 | Rate of Turn | Angular rate |
| 127257 | Attitude | Yaw, pitch, roll |
How to think about them together:
- 129025 (rapid position) and 129026 (rapid COG/SOG) are the fast, lightweight feeds — what a guidance controller leans on. They fit in single frames.
- 129029 (detailed GNSS) is the rich, slower record — it is a Fast Packet message because it carries altitude, fix type, satellite count, and reference-station data all at once. This is the one that needs reassembly.
- 129539 (DOPs) tells you whether to trust the fix. A great-looking latitude with a terrible HDOP is not a usable position. Always read quality alongside coordinates.
- 127250 (heading) and 127257 (attitude) come from a compass/IMU, not the GNSS engine, but a navigation consumer fuses them with position to know orientation, not just location.
machbus’s positioning layer collapses these into a coherent GNSSPosition /
GNSSBatch, so you reason about “a fix” rather than five disjoint PGNs. The deeper
positioning theory — datums, fix types, RTK — is in
Positioning: NMEA and GNSS.
What machbus::nmea decodes
The nmea module is deliberately focused. NMEA 2000 has a large PGN catalogue; machbus
decodes a selected navigation / environment / engine subset that covers what an
agricultural consumer actually needs, plus the management PGNs and an NMEA 0183 parser.
The selected N2K PGN subset (via NMEAInterface)
Around two dozen PGNs, grouped by concern:
| Group | Representative PGNs | What you get |
|---|---|---|
| GNSS / nav | 129025, 129026, 129029, 129539, plus position-delta and XTE | Position, COG/SOG, detailed fix, DOPs, deviation |
| Orientation | 127250 heading, 127251 rate of turn, 127257 attitude, mag var | Heading, turn rate, yaw/pitch/roll, variation |
| Time | 126992 system time | Date/time on the bus |
| Marine-ish | rudder, speed through water, water depth | (present for completeness; less used on land) |
| Engine | engine parameters rapid, fluid level, battery status | Quick engine RPM/status, tank levels, voltage |
| Environment | wind data, outside environmental, temperature, humidity, | Wind, ambient temp/humidity/pressure |
| pressure |
The NMEAInterface is the dispatcher: it knows which of these PGNs to recognise, runs
Fast-Packet reassembly where needed, and produces the typed records.
The management PGNs
Three N2K network-management PGNs round out the picture. These are the N2K analogues of the housekeeping every J1939 network does:
| PGN | Name | Role |
|---|---|---|
| 126993 | Heartbeat | Periodic liveness — “I am still here” |
| 126996 | Product Information | Device model, software version, identity |
| 126998 | Configuration Info | Installation / configuration text fields |
The N2KManagement helper handles these: it tracks heartbeats for liveness, and
collects product/config information about devices on the bus.
The NMEA 0183 serial parser
Older GNSS receivers — and many cheap GNSS pucks — do not speak N2K at all. They emit
NMEA 0183: comma-separated ASCII sentences over a serial line ($GPGGA,…,
$GPRMC,…, and friends), not CAN frames. It is a different physical and framing world,
but the information overlaps heavily with the N2K GNSS PGNs.
nmea’s SerialGNSS parses that 0183 sentence stream into the same kind of typed
position/time records, so a consumer can take GNSS from either source — a CAN-bus N2K
receiver or a serial 0183 puck — and treat the result uniformly.
The event / callback decode model
A small but important conceptual point: NMEA data is a stream, not a request/response. The receiver broadcasts position several times a second whether anyone is listening or not. So the natural shape for consuming it is event-driven, not poll-driven.
frames arrive ──► reassemble (Fast Packet) ──► decode PGN
│
▼
┌──────────────────────────────────────┐
│ your callback fires with a typed │
│ record: "new GNSS fix", "heading" │
└──────────────────────────────────────┘
You register interest once; as frames flow, the interface reassembles, decodes, and invokes your handler with each completed record. You react to fixes as they happen rather than asking “where are we?” on a timer. This matches the pump-style architecture machbus uses throughout — you feed frames in, decoded events come out — and it is exactly the listener-friendly model that lets you read position off a shared ISOBUS/N2K bus without ever claiming an address.
Cross-links
- Positioning: NMEA and GNSS — GNSS fundamentals, fix types, datums, and how positioning fits the wider stack.
- J1939 messages and PGNs — the PGN/SPN model and transport that N2K inherits.
- Anatomy of a CAN frame — the shared 29-bit identifier and the PDU1/PDU2 split.