Send / Receive with Extended CAN ID & MCP2515 CAN module [ Solved ]

I have a instrument which keeps sending CAN messages as below :

This uses the extended ID. I want to be able to receive this using the MCP2515 module + Arduino UNO.

Is it possible if I use the MCP2515 library or I need to use some other library ?

PCAN_XMD_RxData.JPG

PCAN_XMD_RxData.JPG

It might work. If the time stamps on the left of the image are milliseconds then you have 3 milliseconds between messages which is probably OK for an Arduino MC2515 combination.

mikb55:
It might work. If the time stamps on the left of the image are milliseconds then you have 3 milliseconds between messages which is probably OK for an Arduino MC2515 combination.

My main concern is on the extended ID. Is the MCP2515 library capable of handling that ?

Sorry to ask this as right now I don’t have the hardware on hand to check and yet to fully go through the library.

Timing is not critical and I am happy with a 100 ms intervale between reads.

Thanks !

There are many libraries to choose from. Some good, some not so good. They all work to some degree.

The only one I've used in a real project is the GitHub - coryjfowler/MCP_CAN_lib: MCP_CAN Library library which does have support for extended IDs.

If you are doing infrequent reads then bear in mind that the MCP2515 is always receiving data and has a 2 message receive buffer that once full stays that way until you read it. Doing a read always pulls data from the buffer, so if you want up to date data you need to read and discard the first two messages and then wait for a new one to arrive.

mikb55:
There are many libraries to choose from. Some good, some not so good. They all work to some degree.

The only one I've used in a real project is the GitHub - coryjfowler/MCP_CAN_lib: MCP_CAN Library library which does have support for extended IDs.

Great .. this library looks exactly the one i wanted as it explicitly handles extended ID. Let me check and revert. I guess this should work for my use as its a simple requirement to receive and send some messages once every 100ms or so.

Thanks

OK now the Receive example code in the MCP_CAN_Lib worked the first shot.

I then tried the Send Example code. I modified the PGN+SA to 0xFF0122 to suit the Receiver and sent a 8 byte array. I have a basic doubt on forming the 8 byte array… my CAN receiver expects a value of 8 bits ( decimal 0-255). So how do I format the data ? I am now sending 8 bytes with the LSB set to 125 which is what I want to try.

After the initialization I get a "Error Sending Message " report. And the Receiver reports a time out error. So what is the mistake in the code below :

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

MCP_CAN CAN0(10);     // Set CS to pin 10

void setup()
{
  Serial.begin(9600);

  // Initialize MCP2515 running at 16MHz with a baudrate of 250kb/s and the masks and filters disabled.
  if (CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
  else Serial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);   // Change to normal mode to allow messages to be transmitted
}

byte data[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D};  // Send a value of 125

void loop()
{
  // send data:  ID = 0x100, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send
  byte sndStat = CAN0.sendMsgBuf(0xFF0122, 0, 8, data);
  if (sndStat == CAN_OK) {
    Serial.println("Message Sent Successfully!");
  } else {
    Serial.println("Error Sending Message...");
  }
  delay(1000);   // send data per 1000ms
}

It looks like the MCP2515 is a CAN- do.
Spec sheet:

I do not separate CAN receive and CAN send routines, If there is something in the receive buffer it needs to be read out before a send, is my understanding. Even though I call this CAN task send, I do both a receive and send in one tasking:

void fSendCAN_Buss( void *pvParameters )
{
  stuSERVO_Message pxServo_Message;
  for ( ;; )
  {
    xEventGroupWaitBits (eg, evtSendCAN_Buss, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( xSemaphoreTake( sema__Send_CAN_Bus, xTicksToWait0 ) == pdTRUE ) // grab semaphore no wait
    {
      CAN_frame_t rx_frame;
      if ( (CAN_cfg.rx_queue != NULL) && (uxQueueMessagesWaiting(CAN_cfg.rx_queue)) ) // if queue not null and something is waiting in queue
      {
        if (xQueueReceive( CAN_cfg.rx_queue, &rx_frame , xTicksToWait0) == pdTRUE )
        {
          if ( rx_frame.MsgID == 2 )
          {
            if ( rx_frame.data.u8[0] == '6' )
            {
               EOT = false;
            }
            if ( rx_frame.data.u8[0] == '4' )
            {
              EOT = false;
              xSemaphoreGive ( sema_HexaPodAdjustment ); // begin walking forward task
            }
          } // if ( rx_frame.MsgID == 2 )
        } // if (xQueueReceive( CAN_cfg.rx_queue, &rx_frame , xTicksToWait5) == pdTRUE )
      } // if ( (CAN_cfg.rx_queue != NULL) && (uxQueueMessagesWaiting(CAN_cfg.rx_queue)) )
      //      // ack the initialization
      if ( xQueueReceive ( xQ_SERVO_Message, &pxServo_Message, QueueReceiveDelayTime ) == pdTRUE )
      {
        rx_frame.FIR.B.FF = CAN_frame_std;
        rx_frame.MsgID = pxServo_Message.MsgID;
        rx_frame.FIR.B.DLC = pxServo_Message.DLC;
        rx_frame.data.u8[0] = pxServo_Message.Instruction;
        rx_frame.data.u8[1] = pxServo_Message.p1;
        rx_frame.data.u8[2] = pxServo_Message.p2;
        rx_frame.data.u8[3] = pxServo_Message.p3;
        rx_frame.data.u8[4] = pxServo_Message.p4; // end of torque to position
        rx_frame.data.u8[5] = pxServo_Message.p5;
        rx_frame.data.u8[6] = pxServo_Message.p6;
        rx_frame.data.u8[7] = pxServo_Message.p7;
        ESP32Can.CANWriteFrame(&rx_frame);
      }
      xSemaphoreGive ( sema__Send_CAN_Bus );
    }  // if ( xSemaphoreTake( sema__Send_CAN_Bus, xTicksToWait0 ) == pdTRUE ) // grab semaphore no wait
  } // the for loop
  vTaskDelete( NULL );
} // void fSendCAN_Buss( void *pvParameters )

The event evtSendCAN_Buss is triggered once a milliSecond.

With CAN0.sendMsgBuf and extended addresses you need to change the second parameter from 0 to 1.

mikb55:
With CAN0.sendMsgBuf and extended addresses you need to change the second parameter from 0 to 1.

Thanks that did the trick and now I can send / receive. But there is a new problem … if you look at the code its been designed to send one Tx frame every second and the connected instrument is set to send one frame every second. So its one Tx and one Rx per second. All fine upto 65 seconds. Then the Tx frames start going out rather fast … almost every 50ms or so. Unable to understand this … if you look at the attached serial dump you can see my problem. Any idea why this is happening ?

/*
  07 Nov 2019

  Checked OK with the MCP2515 Module.
   Used  NANO board.
   CS to PIN10
   INT to PIN02
   SI to PIN11
   SO to PIN12
   CSK to PIN13
   VCC to 5V
   GND to GND

   Eventhough the XMD default baudrate is 250K this program is at 500K and only then works.

*/

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

#define CAN0_INT 2                          // Set INT to pin 2
MCP_CAN CAN0(10);                           // Set CS to pin 10

unsigned int dataToSend ;
byte data[2] = {0x0, 0x0} ;                 //  Sending 32000; Get the required decimal value between 0 and 65000 ; Convert to Hex ; Swap
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string

unsigned int scanLoopInterval  = 1000;
unsigned int scanLoopMs = millis();

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

void setup()
{
  Serial.begin(9600);

  // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.

  if (CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) {
    Serial.println("MCP2515 Initialized Successfully!");
  }
  else {
    Serial.println("Error Initializing MCP2515...");
  }

  // Change to normal mode to allow messages to be transmitted

  CAN0.setMode(MCP_NORMAL);

  pinMode(CAN0_INT, INPUT);                            // Configuring pin for /INT input
}

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

void loop()
{
  readCAN_Msg();                                      // Check incoming datal based on interrupt status

  if ((millis() - scanLoopMs) > scanLoopInterval) {   // Send data 
    scanLoopMs = millis();
    dataToSend++;
    if (dataToSend > 65000 ) dataToSend = 0;
    data[0] = lowByte(dataToSend);
    data[1] = highByte(dataToSend);
    sendCAN_Msg();
  }
}

//0000000000000000000000000000000000000000

void sendCAN_Msg() {

  // sendMsgBuf(ExtendedID,1,Data length, data)

  byte sndStat = CAN0.sendMsgBuf(0x1CFF0122, 1, 2, data);
  if (sndStat == CAN_OK) {
    Serial.print(dataToSend); 
    Serial.println(" Sent Successfully!");
  }
  else {
    Serial.println("Error Sending Message...");
  }
}

//0000000000000000000000000000000000000000

void readCAN_Msg() {

  if (!digitalRead(CAN0_INT))                        // If CAN0_INT pin is low, read receive buffer
  {
    CAN0.readMsgBuf(&rxId, &len, rxBuf);             // Read data: len = data length, buf = data byte(s)

    if ((rxId & 0x80000000) == 0x80000000)           // Determine if ID is standard (11 bits) or extended (29 bits)
      sprintf(msgString, "Extended ID: 0x%.8lX  DLC: %1d  Data:", (rxId & 0x1FFFFFFF), len);
    else
      sprintf(msgString, "Standard ID: 0x%.3lX       DLC: %1d  Data:", rxId, len);

    Serial.print(msgString);

    if ((rxId & 0x40000000) == 0x40000000) {          // Determine if message is a remote request frame.
      sprintf(msgString, " REMOTE REQUEST FRAME");
      Serial.print(msgString);
    }
    else {
      for (byte i = 0; i < len; i++) {
        sprintf(msgString, " 0x%.2X", rxBuf[i]);
        Serial.print(msgString);
      }
    }
    Serial.println();
  }
}

//****************************************
//               END FILE
//****************************************

https://www.arduino.cc/reference/en/language/functions/time/millis/

It says

"Please note that the return value for millis() is of type unsigned long, logic errors may occur if a programmer tries to do arithmetic with smaller data types such as int. Even signed long may encounter errors as its maximum value is half that of its unsigned counterpart."

mikb55:
millis() - Arduino Reference

It says

"Please note that the return value for millis() is of type unsigned long, logic errors may occur if a programmer tries to do arithmetic with smaller data types such as int. Even signed long may encounter errors as its maximum value is half that of its unsigned counterpart."

My bad. Many times when you debug, the obvious is missed as you take it for granted :slight_smile: .

Thanks.