Arduino + Controller Area Network (CAN)
Stolen from Wikipedia: The CAN (controller area network) bus is a vehicle bus standard designed to allow microcontrollers and devices to communicate with each other within a vehicle without a host computer. This bus standard includes it's own messaging protocol for communications between nodes on the network.
One of the most popular uses for this technology is in the automobile where the network provides a communication channel between the ECU, Transmission, airbags, braking systems, etc...
As you'll have noticed, I've recently been investigating OpenLCB which uses this technology for communications between its nodes on a model railway. The article below will show how the CAN bus can be incorporated with an Arduino to allow communications between different microcontrollers/nodes on your network.
Topography
The OpenLCB project dictates that the CAN bus implemented via their standards must not be in the form of a ring. When I initially saw the Railstars:io prototype I expected that a ring would be required; the prototype shows an 'in' and 'out' communications port. Little did I realise that, upon closer inspection, the io could act as a termination point for the CAN bus that it connects to. We therefore will implement a layout as follows on our railways...
The OpenLCB project will use standard RJ45 cables (i.e. ethernet cables) due to the requirement of twisted pair technology to guarantee data transmission.
Interfacing with the Arduino via SPI
The Serial Peripheral Interface is a communication standard used to provide communications between integrated circuits. The Arduino has an SPI library and therefore allows us to easily communicate to the CAN interface.
We'll be using the MCP2515 and MCP2551 integrated circuits to transmit/receive our data from SPI onto the CAN bus. This setup has been done before in Sparkfun's CAN-BUS Shield, the CAN-CAN, the Universal CAN Library (Universelle CAN Bibliothek) and the CANduino. The CAN-CAN Schematic and Sparkfun's schematics provide a great reference point for hooking the whole lot up. Of course, you could also just grab their shield if you don't want to build everything yourself.
Schematic
The image below should be pretty self-explanatory. See the next section for the exact pins for the SPI interface. RESET and INT can go to any digital pin.
Building, troubleshooting and talking to one's self
I built two at the same time to be able to test node-to-node communications. There was nothing overly tricky in the construction... I just chose to use relatively tight pcb prototyping boards which didn't leave much room for error. The above schematic was followed and the devices were hooked up to my Arduino(s).
I tried the loopback test from the canduino project and got random responses... the data going in was not the same as the data coming back. I started the debugging process after letting out a little sigh.
It turns out that, on all Arduinos, the SPI interface can only be used on specific pins:
SPI Pin | Arduino Mega | Smaller Arduinos (168,328) |
---|---|---|
SS/CS | 53 | 10 |
MOSI | 51 | 11 |
MISO | 50 | 12 |
SCK | 52 | 13 |
Note that the CAN library also then required a change as the pins in it are hard-coded to a 168/328 Arduino and wouldn't have ever worked with the Mega. You can find my fix for this down below.
I finally had responses after the pins were in the correct locations... but not the responses I wanted to see; I looked at the crystals next. My Arduinos all used 16.000MHZ crystals, but my local hobby shop only had 4mhz, 8mhz or 20mhz. This concerned me and I google'd and google'd to work out if, in the same circuit, multiple ICs could be driven off different oscillation rates. I decided that, since the 'SPI' interface had it's own 'bus rate' and the CAN bus also had it's own 'bus rate' that the crystals therefore did not affect these speeds. Also, the Arduino would dictate the speed of the SPI interface since it was the master (and slower.) I therefore bought 20mhz crystals for the MCP2515 chips, with my fingers crossed. Of course, this could have been the next issue.
Fortunately, from my previous attempts at barebones Arduinos, I had a 16mhz crystal on-hand. I swapped out the 2 capacitors and the 20mhz for the 16mhz and hooked it all up again. Nothing...
So, I then whipped out my Atmega328p 'barebones' Arduino and hooked it up to that. The SPI pins were different, as per above, so I made sure they were correct... and the CAN library from the canduino source had to be modified back to the required pins. After putting it all together I checked the serial output in the Arduino Serial Monitor and saw the correct response. The loop back test was working!
WTH... there was no reason that it should... except that maybe the SPI interface on my Mega was fried? I then, just because I hate caution and feed it to the wind all the time, swapped the 20mhz crystal and capacitors back in. It STILL WORKED! Good... I could keep the 16mhz for my other barebones Arduino.
So... with a known-working CAN node, I swapped it back onto the Mega. WTH... it worked. I now had my CAN nodes (by this time the other node built was also functioning thanks to it's guinea pig brother) talking to themselves. It was time to get them to talk to each other!
Talking in the CAN
Hah... there's an unwritten rule to not talk whilst in the toilet, but in our situation we'll make an exception. From the start I'd built the bus and was just itching to get the damn thing transmitting messages. The setup was simple: terminal blocks were used at junctions and standard dodgy speaker wire was the main network cable. 120ohm resistors were the terminators as per the standard CAN specification.
The only real issues here were the source code (or my lack of knowledge of how it worked) and the usage of crystals. Yes, they came back to haunt me... it turns out that, whilst I had the loopback going, I'd left a 16mhz on one node and a 20mhz on the other. In the source code you specify the 'CAN Bus Speed' which, of course, was configured the same on both Arduinos. The actual issue was that, when the Arduino told the CAN controller to run at bus speed '1000', the CAN nodes did this but, due to their differing crystals, their calculations of what '1000' meant were incorrect! Since one was running 4mhz faster (20mhz vs. 16mhz) it must've been communicating on the bus at a different rate and therefore confusing the hell out of the other node.
The next issue was the code... I looked over it and thought that each node would take turns in transmitting and recieving... I was wrong: you had to actually set one as the sender and one as the receiver. I configured this based on the Arduino class (by #IFDEF just as the SPI pins were configured) and then had communication! My nodes were live and functional!
RX/TX LEDs
The final step was wanting RX/TX activity LEDs. It turns out that the MCP2515 supports this, but you need to do a little configuration. If you view the datasheet at microchip you'll note that the RXnBF Pins can be configured as 'nothing', 'general outputs' or 'low on RX buffer full'. I was hoping that obe would be 'high on TX buffer', but no such luck. It actually turns out that the chip has two RX buffers and therefore two RXnBF Pins. LEDs attached to these pins will, once configured as per below, illuminate once the associated RX buffer is full. If you ever see both full then you might be losing data?
Second option: It turns out that in the MCP2515 Demo Board PDF that you can just put two LEDs on the RXCAN(1)/TXCAN(2) pins of the MCP2515.
Third option: It seems that you can also configure the INT pin to fire on TX buffer 'emptying'. You'll have to disable the pin firing on all other error situations to do this. By default, this pin is usually pulled 'high' by the MCU, so you must either disconnect that, use a resistor between the MCU and MCP2515 or just pull the pin high normally via a pullup resistor and then connect an LED in parallel with this to the INT pin. I imagine that in more complicated scenarios you'd actually want to know once the MCP2515 has generated an interrupt.
IDs: Standard or Extended?
All messages sent on the network have a 'frame id' of either (standard mode) 11-bits or (extended mode) 29-bits. It must be understood that this 'frame id' does not actually identify a node! It is simply an identifier for the actual message being transmitted. This identifier can contain any form of information you want; the bits are yours to play with.
One main use for this identifier is to record the intended recipient of the message you are sending. Depending on the amount of nodes in your network (don't forget to think of future scalability!) you can use anywhere up to 29-bits to uniquely identify them. Of course, this identifier could just be used to identify the 'class' of node and then you could put further data in the message to indicate the exact recipient. You could also go the other way and only use a portion of the identifier for the recipient code and have the rest for other uses.
The MCP2515 has in-built filtering to work on the identifier of incoming messages. If you're only using standard mode then the filters also apply to the first two bytes of the message. The filtering works inside the MCP2515 and therefore doesn't saturate your SPI link as the messages are stopped prior to being put into the RX buffer.
MCP2515 Modes
There's 5 modes that you can set the MCP2515 into. These are as follows:
Mode | Description |
---|---|
Configuration | This mode allows the developer to update settings. The following settings require this mode to be set first: CNF1,CNF2,CNF3,TXRTSCTRL,MASKS,FILTERS. |
Normal | Standard operating mode. RXM0 and RXM1 per buffer do not apply in this mode. All filters and Masks do apply. |
Sleep | Low-power mode. SPI remains active, and the MCP2515 will wake up if there is activity on the CAN bus if the WAKIE/WAKIF interrupts are enabled. After woken, the chip will be in Listen Mode. |
Listen | Make sure there are two or more nodes on the CAN bus. Whilst in Listen mode, the chip won't output a thing, so if another chip sends a message then the message will constantly circulate on the bus if there is no other node to 'ACK' it. Another node must be there to stop the message transmission. RXM0 and RXM1 per buffer apply in this mode and the masks/filters also do too. |
Loopback | All messages sent are returned via the RX buffers, filtered first. No messages are sent out over the CAN. No messages are received from the CAN. |
Buffers
It needs to be noted that the MCP2515 has two receiving buffers known as Buffer 0 and Buffer 1. Messages will go to the first if it's free (and not filtered) and then the second. Filters can be set on both, but the second buffer is much more flexible with four filters.
I don't know the exact reason for the MCP2515 containing two buffers, and then why one is more flexibile than the other, but in the end it's up to the developer as to how to use them. Filters can be configured to allow specific message 'types' on each buffer. This then means that, for example, the developer could configure the second buffer to only allow emergency messages such as 'STOP THAT TRAIN' whilst the first deals with the standard communications.
Filtering CAN Messages via the MCP2515
With the base configuration, an MCP2515 controller will accept any messages sent across the network. This therefore means that your nodes need to filter each individual message coming across the bus to determine if they themselves are the intended recipient. Fortunately the MCP2515 has in-built configurable filtering to allow only messages intended for the node to be passed through its RX buffers.
The MCP2515 has 6 configurable filters with 2 masks. The first mask works in conjunction with the first two filters and applies to the first receiving buffer whereas the second mask works with the final 4 filters and works with the second receiving buffer. Filtering is enabled when any bit of a mask is '1' and disabled when the masks are completely zero'd out.
You may be wondering why both a mask and a filter are needed? As mentioned above, you may not have used the entire 'identifier' area of the sent message to indicate the recipient. Therefore, when you're checking an incoming message, you wont want to filter the entire identifier. As the identifier bits can be a '1' or '0', you need to initially specify which bits you want to check (mask) and then whether or not they are a '1' or '0' (filter).
Node 1 ('101') | Node 2 ('110') | |
---|---|---|
Identifier | 10110 | 10110 |
Mask | 00111 | 00110 |
AND'd | 00110 | 00110 |
Filter | 00101 | 00110 |
Match? | False | True |
In the table above, you can see that Node 1 failed to match. The filter applied was looking for '101' (5 in decimal) but the lower 3 bits identifier (that's what the mask was looking for) was actually '110' (6 in decimal). You can see that the actual result was '110' after the mask was applied to the identifier. The second node matched as the filter is actually looking for '110'.
Now, we have to use the full registers when we apply this to the MCP2515. This means filling out 29bits of data. When in doubt, fill everything else with zeroes. Each register in the MCP2515 is 8bytes, this therefore means that you need 4 bytes per filter and 4 bytes per mask. The 6 filters and 2 masks that were mentioned above are located in the following areas:
Mask 0 | Filter 0 | Filter 1 | Mask 1 | Filter 2 | Filter 3 | Filter 4 | Filter 5 |
---|---|---|---|---|---|---|---|
0x20 | 0x00 | 0x04 | 0x24 | 0x08 | 0x10 | 0x14 | 0x18 |
0x21 | 0x01 | 0x05 | 0x25 | 0x09 | 0x11 | 0x15 | 0x19 |
0x22 | 0x02 | 0x06 | 0x26 | 0x0a | 0x12 | 0x16 | 0x1a |
0x23 | 0x03 | 0x07 | 0x27 | 0x0b | 0x13 | 0x17 | 0x1b |
Right, now that you know you need 4 bytes for a filter and then 4 bytes for a mask... and you know where to store them... you'll probably now need to know how to construct a mask and filter. We'll start with a mask that looks for the value 11 (decimal!) [0x0b in hex, 1011 in binary] in the standard 11-bit identifier. As you can guess, I've just provided the filter by representing the value in binary; we simply need to zero-out the bits to the left to ensure we provide the correct number. Have I provided the mask? No! Look at the truth table below if we were to use 1011 as the mask.
Node 1 ('11011') | Node 2 ('01111') | Node 3 ('01011') | |
---|---|---|---|
Identifier | 11111 | 11011 | 01011 |
Mask | 01011 | 01011 | 01011 |
AND'd | 01011 | 01011 | 01011 |
Filter | 01011 | 01011 | 01011 |
Match? | True | True | True |
They all matched!?!?!? Since we were only checking the 'exact' value via the mask, we weren't actually looking to see if any of the bits around the value were set. The basic principal is that we need to know the maximum length of the value we could be looking for and then use that as the mask. For example, if you have up to '11111' (32 including '0') ids on your network, then you want to make sure that your mask is '11111' and that your filter is the exact number of the node checking if the message is theirs.
Node 1 ('01011') | Node 2 ('01111') | Node 3 ('11011') | |
---|---|---|---|
Identifier | 11111 | 11011 | 01011 |
Mask | 11111 | 11111 | 11111 |
AND'd | 11111 | 11011 | 01011 |
Filter | 01011 | 01011 | 01011 |
Match? | False | False | True |
That's better! Make sure your values are correct! So, in the example below we'll use '11111' as the max value and therefore the mask. We'll then use 01011 as the value of the node we're pretending to be and we'll insert these into the correct registers (being Mask 0 and Filter 0.)
Message Acceptance Process
So, two buffers with differing filters... what happens when a message arrives? The basic idea is that a message will hit the first buffer and, if not accepted, attempt to hit the second buffer. The message is then discarded if the second buffer is also configured to not accept this message.
Note: There are also two bits (RXM0 and RXM1) in the RXBnCTRL (where n is either 0 or 1 depending on the buffer) that determine how messages are accepted. Note: My code currently has the ability to set these registers; for the life of me I cannot actually get the chip to function as per the datasheet when these registers are set. If anyone else has succeeded in using the RXM bits successfully then please leave a comment and tell how you did it!
RXM0 | RXM0 | Description |
---|---|---|
0 | 0 | [Default] Enable reception of all valid messages as determined by the appropriate acceptance filters. (See the pseudo-code below as to how the masks and filters apply.) |
0 | 1 | Only accept standard messages. Filters are useless if the EXIDE bit does not match with this value. For example, if you put an extended filter on buffer 0 but you disable extended messages to buffer zero with this configuration then that filter will never be used. |
1 | 0 | Only accept extended messages. The same rules apply that any standard filters on a buffer with this setting will not be used. |
1 | 1 | Receive ALL messages regardless of filters. Also recieve messages that have caused an error during transmission. The fragment of message received in the buffer prior to the error will be brought through. This mode should really only be used for debugging a CAN system. |
So, as long as the RXM bits are both set to '0', the message buffers will then process through the following procedure to accept or deny messages.
for EACH BUFFER b: (initially 0) if (MASK 'b' is ZERO) { //mask 0 for buffer 0 and mask 1 for buffer 1 Accept new message on Buffer b } else { using FILTERs 0,1 (if b equals 0) OR FILTERs 2,3,4,5 (if b equals 1) if (both the message and filter are 'extended') or (both the message and filter are 'standard') then for each '1' in MASK as 'x', does FILTER[x] equal message[x]? if all match then accept message else check next filter as this filters bits do not match the relevant message bits. else check next filter as this filter type does not match this message type. if still not accepted then TRY BUFFER 1 }
A mask is disabled when all its bits are '0'. As soon as a bit is '1' then that mask applies to the relevant buffer. As per the section above, the bits in the filter are then checked against the relevant message bits based on what bits the mask specifies the process to check.
On the contrary, a filter is not 'disabled' when all bits are '0'; instead you have configured two filters, on the buffer related to the mask, which will only permit standard messages where the identifier is zero. If you set the mask on a buffer to anything other than 0 and then don't touch the filters, this will prevent extended messages from coming through (as both filters have '0' as the EXIDE bit) and also prevent any standard messages that do not have an identifier of zero.
In the event that you don't want to receive a specific message type, you must put a mask on both receiving buffers and then an appropriate filter to block out the specific identifier bits. On the contrary, make sure that your masks and filters are smart enough to capture all other data required for your CAN node.
If in doubt, write an 'all-seeing' debugging application to view all traffic on your bus... it'll allow you to diagnose why messages are landing where you might not want them to. As per above, setting both RXM bits to 1 will bypass your filters; this way you don't have to muck around with your own configuration too much. Just make sure you have more than two other nodes on the bus if you want another in Listen mode. Having one node on the bus, or two with one in listen mode, will mean that any message transmitted will stay perpetually on the wire. A message must be consumed by another node for it to disappear.
Storing Masks and Filters on the MCP2515
Scroll up to see the register addresses for the masks and filters. The contents of each of these registers looks as follows:
Filter/Mask | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|---|
Byte 0 (RX[F|M]nSIDH)* | SID10 | SID09 | SID08 | SID07 | SID06 | SID05 | SID04 | SID03 |
Byte 1 (RX[F|M]nSIDL)* | SID02 | SID01 | SID00 | –– | EXIDE/––** | –– | EID17^ | EID16^ |
Byte 2 (RX[F|M]nEID8)* | EID15 | EID14 | EID13 | EID12 | EID11 | EID10 | EID09 | EID08 |
Byte 3 (RX[F|M]nEID0)* | EID07 | EID06 | EID05 | EID04 | EID03 | EID02 | EID01 | EID00 |
- *: 'n' is the filter number, i.e. RXF0SIDH is the 'Standard ID High Receive Register Filter 0'.
[F|M] means that the name is 'RXF1SIDH' for Filter 1 and 'RXM1SIDH' for Mask 1. - **: 'EXIDE': set this to '1' and this filter will only apply to Extended IDs, '0' means it will only apply to standard IDs.
This byte is unimplemented for Masks. Just leave it as zero. - ^: These two bits are not used if you are in standard mode as you only need 16 bits to check the first two bytes of data.
Have I lost you yet? Each filter and mask needs to be able to store both the Standard ID bits and the Extended ID bits. As mentioned before, if you're in standard mode then the 'lower' two bytes (i.e. bits 16 to 0 [17,18 are ignored!]) of the extended area are for matching against the first two bytes of data. You can disable this by ensuring that the associated mask has zeroes in the extended area. On the contrary, if you wanted to check the first byte in the data for a specific value then you would pass in '001111111100000000' as the extended area of the mask with the relevant extended bits set in filter.
Somewhere up above I mentioned we'd use '11111' for the mask and '01011' for the node id. We'll now store this in the registers. We'll use Mask 0 and Filter 0 for this and we'll make sure the rest are all zeroes so they don't apply. You'll note that the first register (RXM0SIDH) starts with zeros; as per above this means that the identifier could actually contain any data in this area and, since the mask is '0' for these bits, the acceptance process wont care at all! The registers will need to be set as follows:
Register | Data | Register | Data |
---|---|---|---|
RXM0SIDH [0x20] | 0b00000011 | RXF0SIDH [0x00] | 0b00000001 |
RXM0SIDL [0x21] | 0b11100000 | RXF0SIDL [0x01] | 0b01100000 |
RXM0EID8 [0x22] | 0b00000000 | RXF0EID8 [0x02] | 0b00000000 |
RXM0EID0 [0x23] | 0b00000000 | RXF0EID0 [0x03] | 0b00000000 |
Note: All other Registers in the RXF/RXM space need to be zero'd out! |
So, as you can see above, we've put the mask 0b00000011111 in the 'standard id area' of Mask 0 and 0b00000001011 in the 'standard id area' of filter 0. Based on our logic above this will then only allow messages through that match the above requirements. Below is the code to actually do this on the Arduino.
#define MASK_0 0x20 #define FILTER_0 0x00 void CANClass::setMaskOrFilter(byte mask, byte b0, byte b1, byte b2, byte b3) { setMode(CONFIGURATION); setRegister(mask, b0); setRegister(mask+1, b1); setRegister(mask+2, b2); setRegister(mask+3, b3); setMode(NORMAL); } //set MASK 0 for RXB0 a mask checks all bits of the standard id. CAN.setMaskOrFilter(MASK_0, 0b11111111, 0b11100000, 0b00000000, 0b00000000); //set Filter 0 to 0x555. Therefore only messages with frame id: 0x555 are allowed through this buffer. CAN.setMaskOrFilter(FILTER_0, 0b10101010, 0b10100000, 0b00000000, 0b00000000);
Note that only setting this for buffer 0 will mean that any message that doesn't match will come through on buffer 1. You'll need to also set a mask on buffer 1 to stop the messages completely. Of course, you can also only ever check buffer 0 for messages and not care if buffer 1 has anything waiting.
Filters in action: Guessing game.
Ok, who would've thought you could make microcontrollers play a game together. You know the old trick, someone else chooses a 'random' number and you get to 'randomly' guess it. Why don't we make our Arduino's play the same game?
We'll set the filter on buffer 0 to the number the receiver is thinking of. This means that the sender will only actually get a message through to the receiver if they guess the correct identifier. Both controllers will then swap roles. Just to make life easier we'll limit the range of numbers allowed, but we'll still make the guessing as 'random' as possible.
I could paste the full code here, but instead I'll just post the general idea.
Main setup | |
---|---|
Set Mask of Buffer 0 to the 11-bit standard identifier Set Mask of buffer 1 to the same 11-bit id set filter 2 (first filter of buffer 1) to a specific ID for messages that aren't guesses. Set one node to RX and one to TX Initialise the random generator. |
|
Receiver Role | Transmitter Role |
|
|
Source code (including my version of the CAN Library)
The final piece of the puzzle is always the source code. I've included the guessing game example as well. Note that my code is based off the canduino and I intend to send changes upstream where possible to make life easier for everyone.