Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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-0xFF is 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.

PGNNameWhat it carries
129025GNSS Position, Rapid UpdateLatitude / longitude only, sent fast (the steering feed)
129026COG & SOG, Rapid UpdateCourse over ground + speed over ground, sent fast
129029GNSS Position DataThe full fix: lat/lon/alt, fix type, sats, ref station
129539GNSS DOPsDilution of precision (HDOP/VDOP/PDOP) — fix quality
129540GNSS Satellites in ViewPer-satellite detail (PRN, elevation, azimuth, SNR)
127250Vessel/Vehicle HeadingHeading (true or magnetic), deviation, variation
127251Rate of TurnAngular rate
127257AttitudeYaw, 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:

GroupRepresentative PGNsWhat you get
GNSS / nav129025, 129026, 129029, 129539, plus position-delta and XTEPosition, COG/SOG, detailed fix, DOPs, deviation
Orientation127250 heading, 127251 rate of turn, 127257 attitude, mag varHeading, turn rate, yaw/pitch/roll, variation
Time126992 system timeDate/time on the bus
Marine-ishrudder, speed through water, water depth(present for completeness; less used on land)
Engineengine parameters rapid, fluid level, battery statusQuick engine RPM/status, tank levels, voltage
Environmentwind 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:

PGNNameRole
126993HeartbeatPeriodic liveness — “I am still here”
126996Product InformationDevice model, software version, identity
126998Configuration InfoInstallation / 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.