Problem connecting 2 arduinos with CAN BUS

Hi everyone, I started a project where I need to connect 2 Arduino with CAN-BUS protocol. For this I went to the simplest and tried to make ONLY the communication via CAN bus of the 2 Arduino with a simple example I found in the library that you can download directly from Arduino called: "CAN_BUS_Shield-master".

As known, there are 2 codes (1 for the transmitter and 1 for the receiver), the transmitter one works just fine (I think). The problem comes in the receiver code, when it checks if data is coming with sentences like:

if(CAN_MSGAVAIL == CAN.checkReceive())

I tried different codes and libraries but the problems repeat at the same point (when the receiver checks if data is coming) and I know the transmitter is sending...

Can someone explain what is wrong? I will attach the code and also a schematic of what and how i connected.

TRANSMITTER CODE:

#include <mcp_can.h>
#include <SPI.h>

/*SAMD core*/
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
  #define SERIAL SerialUSB
#else
  #define SERIAL Serial
#endif

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int ledHIGH    = 1;
const int ledLOW     = 0;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
    SERIAL.begin(115200);

    while (CAN_OK != CAN.begin(CAN_500KBPS))              // init can bus : baudrate = 500k
    {
        SERIAL.println("CAN BUS Shield init fail");
        SERIAL.println(" Init CAN BUS Shield again");
        delay(100);
    }
    SERIAL.println("CAN BUS Shield init ok!");
}

unsigned char stmp[8] = {ledHIGH, 1, 2, 3, ledLOW, 5, 6, 7};

void loop()
{   SERIAL.println("In loop");
    // send data:  id = 0x70, standard frame, data len = 8, stmp: data buf
    CAN.sendMsgBuf(0x70, 0, 8, stmp);
    delay(1000);                       // send data once per second
}

RECEIVER CODE:

#include <SPI.h>
#include "mcp_can.h"

/*SAMD core*/
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
  #define SERIAL SerialUSB
#else
  #define SERIAL Serial
#endif

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;
const int LED        = 8;
boolean ledON        = 1;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
    SERIAL.begin(115200);
    pinMode(LED,OUTPUT);

    while (CAN_OK != CAN.begin(CAN_500KBPS))              // init can bus : baudrate = 500k
    {
        SERIAL.println("CAN BUS Shield init fail");
        SERIAL.println("Init CAN BUS Shield again");
        delay(100);
    }
    SERIAL.println("CAN BUS Shield init ok!");
}


void loop()
{
    unsigned char len = 0;
    unsigned char buf[8];

    if(CAN_MSGAVAIL == CAN.checkReceive())            // check if data coming
    {
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

        unsigned long canId = CAN.getCanId();

        SERIAL.println("-----------------------------");
        SERIAL.println("get data from ID: 0x");
        SERIAL.println(canId, HEX);

        for(int i = 0; i<len; i++)    // print the data
        {
            SERIAL.print(buf[i]);
            SERIAL.print("\t");
            if(ledON && i==0)
            {

                digitalWrite(LED, buf[i]);
                ledON = 0;
                delay(500);
            }
            else if((!(ledON)) && i==4)
            {

                digitalWrite(LED, buf[i]);
                ledON = 1;
            }
        }
        SERIAL.println();
    }
}

SCHEMATIC (The connection between CANs is missing I am concerned. Sorry about that)

Could you be so kind and let me know what the exact CAN module is? Do you have a link to schematic of the board.

Also I found multiple CAN libraries in the Arduino IDE library manager. Please let me know which one you are using.

Do you have an oscilloscope?

Of course and thanks for your help!

The can module that I am using is the MCP2515 CAN module. You can find the datasheet of the controller here: http://henrysbench.capnfatz.com/wp-content/uploads/2017/01/MCP2515-Data-Sheet.pdf.

Here you also have a link to download the library I am using and inside you can find the examples I am running too (Send_Blink and Receive_Blink): GitHub - Seeed-Studio/Seeed_Arduino_CAN: Seeed Arduino CAN-BUS library - MCP2518FD&MCP2515&MCP2551

Since I am studying in a university I may have access to an oscilloscope but currently, I do not.

I do not understand what you mean by "Do you have a link to schematic of the board". By that you mean the image of the schematic I posted?

Thanks again and hope this helps! waiting for your response.

I would like to see the schematic of the CAN module (the two little boards in your schematic) not the chip, that is on it.

I will have a look at the library.

I think that's what you refer to:
canmodule_schematic.jpg

canmodule_schematic.jpg

You cannot be sure the transmit side is OK, because you do not check. Lets look at the likely cause first.

The picture is good enough. The board has a termination resistor you will need to enable them on both boards. Also make sure the two wires are the same length. The CAN bus needs to be terminated at both ends. If you add more nodes you do not enable the resistors for the nodes in the middle of the bus.

In CAN you cannot transmit messages without another node reading it. Even if the nodes is not the intended receiver it will read the message and acknowledge the message if it was valid. So, making sure the wiring is done properly is essential. Every single message needs to be acknowledged in the message frame by any node (this is done automatically by the MCP2515). If the message is not acknowledged, because the receiving node did not see a valid message, the message is repeated.

In your sender software you should evaluate the return value of the CAN.sendMsgBuf function. This will tell you whether the send was successful. However, the library would have to wait for that transmission to finish. And I do not think it does. I believe it just checks whether a transmit buffer is available. Because there are 3 transmit buffers you will only see an error when you try to send 4 messages and evaluate the return value.

The most basic test you could do is start both nodes. The receiver only needs to be configured for the same speed. You do not need to read the buffer. Send one message and have a look at the bus with an oscilloscope. If the system is setup correctly you will see one message. If there is an issue, the message will be repeated (Exception, if the MCP2515 One shot mode is enabled, but the library does not seem to be using this feature).

While you do not have access to an oscilloscope, send a couple of messages and compare the result. I suspect when you send the 4th message, the result will change. This will indicate that the messages are still all in the transmit buffer.

byte result = CAN.sendMsgBuf(0x70, 0, 8, stmp);
Serial.println(result);

Do this at least 4 times with a nice delay in between. If the result changes, the receiving node did not read the message as vaild.

Once again thanks for the effort and help!

First, replaying to the termination resistor part: if I do understand it right you are telling me to enable both of them by putting in the jumper at R2 (120ohms).

Second, I included the sentences you told me:

byte result = CAN.sendMsgBuf(0x70, 0, 8, stmp);
Serial.println(result);

And YES! your suspicions were true when I start the Serial Monitor of the sender it appears the following:

Enter setting mode success
set rate success!!
Enter Normal Mode Success!!
CAN BUS Shield init ok!
In loop
7
In loop
7
In loop
7
In loop
6
In loop
6
... And repeats the 6 over and over. At the fourth message the result changes!

What is this due to? Are the 2 CANs not synchronized? How can I fix it?
Thanks!!

Yes, you need to enable both 120Ohm resisitors, these are the bus termination resisitors. See the picture at Wikipedia.

Have a look into the mcp_can_dfs.h file. The return values you are getting are:

#define CAN_GETTXBFTIMEOUT (6)
#define CAN_SENDMSGTIMEOUT (7)

In loop
7 // message send timeout, the message could not leave the buffer in time
In loop
7 // using second TX buffer, same thing message could not be send in time, hardware will continue to try
In loop
7 // using third buffer, same as above
In loop
6 // all transmit buffers are full because the messages could not be send, the hardware is still trying
In loop
6

As I wrote before. The CAN message needs to be acknowledged by any node. The physical signal is read by all nodes all the time. The high bits in CAN are recessive, they can be overwritten. During the ACK slot the sender sends a high bit and it needs to be overwritten by a low signal. This is done in hardware. But when the signal is bad because the termination resistors are missing this does not happen.

Oh I see! thanks for the explanation though. I am going to put the jumpers on and I will update you the news!

Enabling the resistors the code works fine! Now it is time to complicate the things a bit!!

Really appreciate your help Klaus_K.