I’ve been looking at NMEA2000 and CANbus lately. I’m attempting to reverse engineer the binary protocol seen coming out of the USB port on a Garmin GND10 network bridge, and that opens a whole field of new and interesting stuff to know about.
Quick background: NMEA2000 is a application-level protocol that is used on ships and smaller boats, to communicate things like heading, speed and position between sensors (gps, compass, etc) and displays on the bridge. The physical, datalink and network layers (to the extent that it applies) in this are CANbus.
It is truly fascinating how complicated “industry standard” can be. Adding to that, each of the 8 ISO1939 standards defining it is 168CHF each. Not an option for me at the moment, so here I go digging in binary data. (And in retrospect, complain less about RFCs in the future. They are free, open and mostly well thought out.)
The Wikipedia nodes for both these contain lots of facts, but perhaps doesn’t give you the overview you need as an implementor.
CANbus can be sent over many different physical setups, for NMEA2000 there are five wires: ground, +12V vcc, can-hi, can-lo and shield. Typically you have a canbus transciever circuit/IC to do the differential encoding on can-lo and can-hi, and a separate canbus controller for the protocol itself. The connectors for NMEA2000 is a round quick-release kind, but you can also do canbus over cheap cat5+rj45 if you want. (250kbit/s is rather forgiving these days)
CANbus frames have a 32 bit identifier and up to 8 bytes (yes, this is quite adorable) of payload. The wire format contains additional bits here and there, a length field and a CRC and DC-correcting tailer. All this is probably interesting if you create the controllers, but for anyone writing software it doesn’t matter. Especially the part about the frame identifier is either 11 bit or 29 bits, you can ignore. At least for NMEA2000 only CAN2.0B with 29 bit identifiers are used.
On the software side, you get: 1) 32bit frame identifier, 2) the length indicator, and 3) the >=8 bytes of data. Everything else is handled by your interfacing hardware. Hurray.
The 32 bit identifier is a bitfield consisting of source, destination, priority and the Parameter Group Number (PGN).
In NMEA2000 most frames are broadcasted without any specific receivers in mind. The PGNs are defined by the non-public standard made by NMEA, and defines the number of payload bits and what they contain.
Which range a PGN is in also define which framing method is used. CAN frames with a PGN in a single-frame range has the single-frame 8 bytes of payload. Some PGNs are in a “fast-packet” range, where a collection of (up to) 32 CAN-frames can be sent with a session id, sequence counter embedded in the first 8 bits of each payload. The first frame in the sequence additionally has a sequence length in the second byte. This allows the receiver to reassemble the sequence in userspace for a combined payload of up to 233 bytes.
I have no idea how controllers handle lost frames in a fastpacket sequence, most likely they just drop the whole sequence. I also wonder if anything here is complicated enough to that out-of-order delivery can happen. Haven’t seen it yet, though.
There is an additional “ISO” transfer mode that is a (“unicast”) request/response method. Back when I was playing with ODB2 in cars, you had to request a specific ID with data. I think this what was being used there, but haven’t verified. It supports up to 1785 bytes, so obviously there is some segmentation and reassembly method there as well. I haven’t looked at it yet, as I’m mostly interested in the broadcasted updates.
On the network management side, there is an ARP/ND alike way for connecting units to claim source IDs. Lower source id means higher network priority on collisions. so there is some magic to it. I haven’t come this far yet.
Trying to keep this short and readable, let me round off with mentioning the CANbus network interface in Linux. There is kernel support now and you get a network interface with a whopping 8 byte MTU. There is even a loopback interface for it, so you can talk CAN between applications if you don’t have a physical CAN interface. Look at the documentation for can-utils.git for details.
I’ll try to post again when I figure out more of this stuff.