The CANopen protocol sits on top of the low-level CAN bus protocol. Due to the way CAN bus packets are tagged with a message ID, but not specifically routed from or to a specific device (i.e. node), and due to the maximum packet payload size of 8 bytes, you really need something like CANopen to make it usable. For reference, here’s a 2-node CAN bus setup:
The interesting bit about CANopen, is that it’s all about data acquisition and process control (that’s what you do when you put this stuff in a car, after all). And what intrigues me about that, is the similarity with monitoring and controlling stuff around the house.
I’ve been reading myself silly lately, to wrap my head around all the CANopen terminology and to try and distill the essence of… ehm, CANopen, life, and a few other things :)
But instead of just describing CANopen as it is, and pouring lots of its concepts and new terminology on you, let me try to describe this from an outsider’s perspective:
CANopen works in terms of accessing and saving objects. It’s not necessarily 100% centralised, but that’s how it often gets used: a central CANopen node to control, inquire, and manage a bunch of CANopen slave nodes, all on the same CAN bus.
There are a lot of conventions w.r.t. these objects, because the goal of CANopen is to allow devices from different manufacturers to inter-operate (that’s probably why it’s called “open”). So there’s a standard way to find out which devices are present and what their basic capabilties are, to enable/disable each one of them, to configure their communication settings, to start and stop data acquisition streams – all with lots of room for manufacturer- and device-specific details to be managed as well.
The central concept here is the “Object Dictionary” in each device, which is indexed by a 16-bit number, plus a numeric 8-bit subindex for entries which are more structured. Objects can be 1..4 bytes, in which case they can be exchanged in one packet, or larger, in which case CANopen takes care of all the packing and unpacking.
There is a simple yet effective heartbeat mechanism, so that each device (and of course also the master) can figure out which devices are currently present, which are active, and – with timeouts – which devices seem to have dropped out for some reason.
Each device announces itself when powering up, so auto-discovery is possible, even for devices which the master has never seen before – although it’ll be more effective when the master has a detailed description of each type of device, so that it can optimally manage and control it.
There are mechanisms for urgent error reporting, dropping out gracefully, and throttling to avoid bus congestion.
Everything is prioritised (the CAN bus does that), with top priority (msg ID 000) given to essential (but infrequent) master messages, such as starting and stopping device activity on the bus. Note that, due to the top priority, these will always get through.
Next in priority is a time synchronisation message, which can be used to make all device clocks “sing in sync”. With a 1 Mbit/s bus rate, these sync messages will never have more than about 150 µs jitter (and there’s a clever optional scheme to increase the time resolution in all slaves to 1 µs).
There is much more which I can’t all describe in this post, but the one design idea I’d like to highlight is the distinction between managing a large set of objects (via SDO’s, i.e. Service Data Objects) and maximally efficient data streams (via PDO’s, i.e. Process Data Objects). Where PDO’s can be set up to be sent across in a regular time-synchronised manner or in a more ad-hoc / event-based / asynchronous fashion.
I really like this distinction: there is a large heterogenous mix of data objects, which can be accessed and changed via individual SDO requests, and there are streams of PDO data which don’t waste time and bandwidth on describing themselves – just the raw bytes. The heterogenous data objects can fully describe those streams, their exact byte (even bit) structure, the conditions under which data gets sent, their frequency, and their priority.
Lots of interesting connections with the way the RF12 driver sends packets, the announcer packet idea, and the way HouseMon distinguishes between incoming data and the meta-data inside each driver describing these incoming packets.