I’ve been poking around with the CanShield software recently to try to clean it up. I felt that the first release was a bit cumbersome to use and had some maintenance disadvantages for me. As I’ve been considering adding features, I’ve found it hard to see how to integrate them into the current setup. I’ve fixed it up some and have posted a new release, 1.10.
The initialization and cleanup are still the same, CAN.begin() and CAN.end(). The mode is still set with CAN.setMode(), and you can still check whether a message has been received with CAN.available(). However, this is about the end of the similarities.
I thought that the message handling was cumbersome, having to make a separate call to retrieve the message ID and the data. If you’re signalling with message IDs only (no data), it’s weird to have to mark the message as received, since you don’t have to when you retrieve data.
To fix this, there is now a message object. For those unfamiliar with the concept, an object is like a variable that contains other variables. In this case, there is a CanMessage object, which contains the ID, data, data length, and a variable to mark whether the message has an extended identifier. It is no different than having several normal variables, except that the whole bundle can be passed around together. When all this data is contained together in one object, it is easier to manage messages as a whole.
To create a message object, you declare it like any other variable:
You can directly edit the variables inside of it by using the dot syntax, just like when you use different Arduino libraries. This sets the value of the variable inside the object, just like any other variable:
myMessage.id = 1234;
Objects also have functions associated with them that can manipulate the data that they can contain. These functions are also called using the dot syntax. For the CanMessage object, data is set and messages are sent using object functions. (Functions associated with an object are sometimes called “methods”. For simplicity, I’ll stick with “functions”.) The following will set a byte of data into the CAN message field, then send the message:
The length field is automatically updated when any of the set-data functions are called.
To retrieve a message, you can call CAN.getMessage(). This returns a CanMessage:
myMessage = CAN.getMessage();
You can then read out the id and data:
id = myMessage.id;
data = myMessage.getByteFromData();
If you have a few different messages that you send for your application, you can declare them once, then update their fields and send them as needed.
message1.id = 1001;
message2.id = 1002;
message3.id = 1003;
val1 = readSensor1 ();
val2 = readSensor2 ();
val3 = readSensor3 ();
At the beginning, I mentioned that this new scheme has maintenance advantages, too. I have changed the underlying structure of the library to use an MCP2515 library written in C. This is easier for me, as I develop in C. This way there is much less work in porting updates back to the Arduino environment. If anyone is writing their own C code for the Arduino board or AVR in general, it should be easy for them to use the MCP2515 and SPI implementations included in the CAN library with little or no change.
Please let me know what you think about these changes. I hope this makes the library easier to use. If you have suggestions for improvements, please send them along!