AI-generated text still pending human review/editing.
NMEA 2000 ↗ ISOBUS ↗
A heavy-duty truck broadcasts its engine speed in a CAN frame with the 29-bit ID 0CF00400. Everything about that frame's meaning, who sent it, what it carries, how urgent it is, lives inside that ID. Type any 29-bit ID and payload below and take it apart. Hover any field or byte for what it means.
29-bit CAN ID (hex)
Data bytes

Reading the ID: the 29-bit split

Classic CAN gives an 11-bit identifier; the extended format gives 29. On a car those extra bits are mostly unused. J1939 instead assigns every one of them a job, so the ID itself is a small structured record. Take the running example 0CF00400 apart from the top:

The top 3 bits are the priority. 0C in the first hex digit pair is 0000 1100 in binary, and the ID is only 29 bits wide, so the priority sits in bits 28 to 26: 011, priority 3. Lower wins arbitration, exactly as raw CAN IDs do; 0 is the most urgent and 6 is the default for routine broadcasts. Two single bits follow: the Extended Data Page (reserved, and 0 on a J1939 bus) and the Data Page, which becomes bit 16 of the PGN (counting from 0). Then come three whole bytes: PDU Format F0, PDU Specific 04, and Source Address 00.

The source address is the simplest of the three: it is the sender's claimed bus address, and 00 is the standard address of Engine #1. Which leaves the two bytes in the middle, and those are where the interesting rule lives.

PDU1 vs PDU2: one byte, two jobs

J1939 messages are named by their PGN (Parameter Group Number), and the PGN comes out of the PDU Format and PDU Specific bytes. But the PDU Specific byte means two different things depending on the PDU Format value, and the cutoff is 240 (0xF0):

If PDU Format is below 240 (called PDU1), the message is addressed to one node: PDU Specific is a destination address, and the PGN is just the PDU Format byte shifted up (PF << 8, low byte zero). If PDU Format is 240 or above (PDU2), there is no destination, the frame is a broadcast for anyone who cares, and PDU Specific becomes a Group Extension that is part of the PGN itself (PF << 8 | PS).

In 0CF00400, PDU Format is F0, exactly at the cutoff, so this is PDU2: broadcast, and the PGN is 0xF004, decimal 61444. That number has a name in SAE J1939-71: EEC1, Electronic Engine Controller 1, the message that carries engine speed. No request needed, no destination, the engine just shouts it onto the bus every few tens of milliseconds.

A PDU1 counter-example: the Request PGN (59904, PDU Format EA), sent by a tool at address F9 to the engine at 00, asking it to send PGN 65260 (the VIN). The 3-byte payload is the requested PGN, little-endian.

Note what changed in the decoder: with PDU Format below 240, the blue byte flipped from "Group Extension" to "Destination", and the PGN's low byte went to zero. That one comparison (PF < 240) is the single most load-bearing rule in J1939 ID parsing.

SPNs: from bytes to physical values

Inside a PGN's 8 data bytes live SPNs (Suspect Parameter Numbers), the individual signals. Each SPN has a fixed position, width, scale and offset, published in J1939-71 (unlike a car's proprietary DBC, the dictionary is standard, which is why one J1939 tool works across truck brands). Decode the running example's payload FF FF 7D 68 13 00 FF FF by hand. J1939-71 counts data bytes from 1, and so does this page:

Engine speed is SPN 190: bytes 4 and 5, 16 bits little-endian, 0.125 rpm per bit. Bytes 4 and 5 are 68 13, so the raw value is 0x1368 = 4968, and 4968 × 0.125 = 621 rpm. An idling diesel. Byte 3 is the actual engine torque, one byte with a −125 offset: 0x7D = 125, 125 − 125 = 0 % torque, which is what idle should read.

And the FF bytes are not garbage. J1939-71 reserves the top of every SPN's range, judged by the value's most significant byte: up to FA is a real value, FB is a parameter-specific indicator, FC-FD are reserved, FE means error and FF means not available (so 0xFFxx for a word). This ECU simply doesn't populate those signals, and a decoder must show "n/a" rather than a nonsense value. The decoder above greys them out for exactly that reason. A 2-bit status works the same way in miniature: 00 off, 01 on, 10 error, 11 not available.

Engine Temperature 1 (PGN 65262): coolant at byte 1 with a −40 °C offset, so 5A = 90 − 40 = 50 °C, and an oil temperature scaled at 1/32 °C per bit with a −273 offset (yes, it counts from absolute zero).
Cruise Control / Vehicle Speed (PGN 65265): wheel-based speed in bytes 2 and 3 at 1/256 km/h per bit. 00 50 little-endian is 0x5000 = 20480, ÷ 256 = 80 km/h.

When 8 bytes aren't enough: the Transport Protocol

A VIN is 17 characters and a frame holds 8 bytes, so J1939 needs its own multi-frame layer (it predates ISO-TP and works differently: the length and the carried PGN are announced in a separate message rather than in a first-frame header). Two PGNs do all the work. TP.CM (PGN 60416, PDU Format EC) manages the connection, and TP.DT (PGN 60160, PDU Format EB) carries the data, 7 bytes per frame after a 1-byte sequence number. That 7-byte stride caps a message at 255 packets × 7 = 1785 bytes.

For broadcasts there is no handshake at all. The sender transmits a single TP.CM with control byte 0x20, a BAM (Broadcast Announce Message), saying "a message of this size and PGN is coming in this many packets", then streams the TP.DT frames with 50 to 200 ms between them. Receivers reassemble or ignore; nobody replies. Point-to-point transfers use the same TP.DT frames but wrap them in an RTS/CTS handshake (control bytes 0x10/0x11, closed by an End-of-Message ACK 0x13), which lets the receiver pace the sender the way ISO-TP flow control does.

Type a VIN below and watch the BAM sequence build. Hover the bytes: the TP.CM payload packs the control byte, the total size (little-endian), the packet count, and the PGN being carried (also little-endian, 3 bytes).

BAM simulator: send a VIN (PGN 65260)
VIN text

Who's talking: addresses and the address claim

Every sender needs a source address, and J1939 hands them out two ways. Well-known functions have fixed addresses from SAE J1939 (0 Engine #1, 3 Transmission #1, 11 Brakes - System Controller, 23 Instrument Cluster #1, and so on up through the standard list; 249 is the customary service-tool address, 255 is the global broadcast destination, 254 the null address). Everything else claims one at startup: the device broadcasts PGN 60928, Address Claimed, whose 8-byte payload is a 64-bit NAME encoding who it is (manufacturer code, function, industry group, a unique identity number). If two devices claim the same address, the one with the numerically lower NAME wins and the loser must claim a different address or fall silent. The result is plug-and-play addressing with no central allocator, which is why the same standard scaled from trucks to boats (NMEA 2000) and tractors (ISOBUS).

An Address Claimed frame: PGN 60928 is PDU1, sent to the global destination FF, source 00. The payload is the 64-bit NAME; the decoder breaks out its fields.

Practical notes

The classic J1939 bus runs at 250 kbit/s (J1939-14 later added a 500 kbit/s profile), so set the speed before connecting a real adapter. In sloppyCAN, the J1939 / N2K tab does live what this page does statically: it splits every 29-bit ID, names the PGN, decodes the SPNs, reassembles TP transfers, and tracks address claims. The Demo button generates J1939 traffic (engine, cruise-control, electrical and driver-demand groups plus an engine address claim) so you can watch the PGN monitor with no hardware attached.

One honest caveat: the J1939-71 dictionary is huge, and this page decodes the same few dozen parameter groups the app does (it loads the app's own table). An ID that parses cleanly but shows "not in sloppyCAN's J1939 dictionary" is still a perfectly valid J1939 frame; the split and the PDU1/PDU2 rule apply to every 29-bit ID on the bus.