The intention of this tutorial is to provide an example on how to intercept, modify and re-transmit a specific CAN message while “passing through” all other messages untouched using the DUE and the dual can shield. This could be used for “spoofing”, new systems integration, or sharing data between 2 buses with differing baud rates.
Step #1 Build and test the shield
Before we get started, lets make sure everything works. Follow our building and testing tutorial here for the DUE CAN shield.
Step #2 Wire up the two CAN ports
In this example, We will be intercepting the CAN lines from one “black box” to another with the DUE int the middle acting as a “modifier-repeater”. We are receiving a message on CAN ID 0x7E8 on CAN 0 modifying it’s contents and re-transmitting it on CAN 1 (however not modifying it’s contents from CAN 1 to CAN 0). We will also “pass-thru” messages 0x101, 0x7DF, 0x7E0, and 0x7E1 bidirectionally between CAN 0 and CAN 1 as “untouched”. The CAN bus should be physically broken (not spliced) with the upstream transmitting device sending message 0x7E8 wired to CAN H and CAN L signals on the Arduino DUE CAN shield port 0, and the downstream devices wired to CAN H and CAN L on CAN 1.
Step #3 Program it
Open the IDE and go to File->Examples->CAN and select “CAN_Example_Gateway.ino”. Remember you’ll need to have all of the DUE drivers installed and there is a tutorial on the arduino due website here. Make sure you have selected the proper board and port selected from Tools->Board->Arduino DUE Programming Port and Port->COMxx. Now click upload button, it will verify and upload the code to the DUE. The board should be communicating as a gateway at this point. You could add “print” statements as you like for debug messages to the serial terminal.
Step #4 Modify it
See the example code below (and on github here) to modify for your message types, baud rates etc. Thank you for your purchase, we wish you success!
#include <CAN_Acquisition.h> /************************************************************************************************************************************************************************** This example is built upon the "CANAcquisition" class cAcquireCAN. This example shows how to receive message 0x7E8, modify it and re-send it on CAN1 while acting as a "gateway" and pass through the rest of the ID's between CAN0 and CAN 1 /*************************************************************************************************************************************************************************/ //create the CANport acqisition schedulers cAcquireCAN CANport0(CAN_PORT_0); cAcquireCAN CANport1(CAN_PORT_1); /** * inheriting this class allows us to implement the RX callback function at the top layer */ class cGatewayRXFrameCAN0 : public cCANFrame { bool CallbackRx(RX_CAN_FRAME *R); }; class cGatewayRXFrameCAN1 : public cCANFrame { bool CallbackRx(RX_CAN_FRAME *R); }; //we are going to gateway messages 101,7DF,7E0,7E1, 0x7E8 /****** CAN 0 messages ******/ cGatewayRXFrameCAN0 CAN0_0x7E8; cGatewayRXFrameCAN0 CAN0_0x101; cGatewayRXFrameCAN0 CAN0_0x7E0; cGatewayRXFrameCAN0 CAN0_0x7E1; cGatewayRXFrameCAN0 CAN0_0x7DF; /****** CAN 1 messages ******/ cGatewayRXFrameCAN1 CAN1_0x7E8; cGatewayRXFrameCAN1 CAN1_0x101; cGatewayRXFrameCAN1 CAN1_0x7E0; cGatewayRXFrameCAN1 CAN1_0x7E1; cGatewayRXFrameCAN1 CAN1_0x7DF; UINT8 i; void setup() { //start serial port at 115.2kbps for PC comms Serial.begin(115200); //debugging message for monitor to indicate CPU resets are occuring Serial.println("System Reset"); //output pin that can be used for debugging purposes pinMode(13, OUTPUT); //start CAN ports, set the baud rate here. CANport0.initialize(_500K); CANport1.initialize(_500K); //initialize raw CAN mesasges /****** CAN 0 - Messages ******/ CAN0_0x7E8.ID = 0x7E8; CAN0_0x101.ID = 0x101; CAN0_0x7E0.ID = 0x7E0; CAN0_0x7E1.ID = 0x7E1; CAN0_0x7DF.ID = 0x7DF; /****** CAN 1 - Messages ******/ CAN1_0x7E8.ID = 0x7E8; CAN1_0x101.ID = 0x101; CAN1_0x7E0.ID = 0x7E0; CAN1_0x7E1.ID = 0x7E1; CAN1_0x7DF.ID = 0x7DF; //add messages to rx (set RX filters) CANport0.addMessage(&CAN0_0x7E8, RECEIVE); CANport0.addMessage(&CAN0_0x101, RECEIVE); CANport0.addMessage(&CAN0_0x7E0, RECEIVE); CANport0.addMessage(&CAN0_0x7E1, RECEIVE); CANport0.addMessage(&CAN0_0x7DF, RECEIVE); CANport1.addMessage(&CAN0_0x7E8, RECEIVE); CANport1.addMessage(&CAN0_0x101, RECEIVE); CANport1.addMessage(&CAN0_0x7E0, RECEIVE); CANport1.addMessage(&CAN0_0x7E1, RECEIVE); CANport1.addMessage(&CAN0_0x7DF, RECEIVE); } //this is our timer interrupt handler, called at XmS interval void CAN_RxTx() { //receive messages, don't periodically transmit (but you can transmit asynchronously via CANportx.TXmsg(xxx) in our overloaded messages below) CANport0.run(POLLING_noTx); CANport1.run(POLLING_noTx); } void loop() { //handle can port RX in tight loop CAN_RxTx(); } //this method is called whenever we receive a registered message on CAN 0 bool cGatewayRXFrameCAN0::CallbackRx(RX_CAN_FRAME *R) { bool retVal = false; U8 i; if (R) { //go ahead and immediately stuff message payload from raw can frame for (i=0;i<8;i++) { this->U.b[i] = R->data.bytes[i]; } //have a look to see if this is the message we are looking for (0x7E8) on CAN0 to modify and pass through on CAN1 if(this->ID == 0x7E8) { //we want to increment the last byte of this message and resend this on CAN1 this->U.b[7] += 1; } //gateway all registered RX messages from CAN 0 to CAN 1 CANport1.TXmsg(this); } return(retVal); } //this method is called whenever we receive a registered message on CAN 1 bool cGatewayRXFrameCAN1::CallbackRx(RX_CAN_FRAME *R) { bool retVal = false; U8 i; if (R) { //go ahead and immediately stuff message payload from raw can frame for (i=0;i<8;i++) { this->U.b[i] = R->data.bytes[i]; } //gateway all registered RX messages from CAN 1 to CAN 0 CANport0.TXmsg(this); } return(retVal); }