Filter a CAN message

Hi all,

I have modified the code below to turn on a MOSFET when any CAN traffic is present. It works fine. I would like to now pick a specific CAN id (i.e 0x1B0) to turn on the MOSFET. Just need some guidance on doing this before filtering further depending on the data within the CAN frame.

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

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                            // Array to store serial string

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

const int loadMosfet = 9;                       //Digital Pin 9
const int led = 8;                              //Green LED indicator showing CAN signa is active
const int powerMosfet = 4;                      //Digital output to instantly turn power mosfet on / off

unsigned long lastMessageTime;
unsigned long timeoutInterval = 10000;           //10 seconds

void setup()
{
  pinMode (powerMosfet, OUTPUT);
  digitalWrite (powerMosfet, HIGH);             // Immediatly turn on Mosfet to power up circuitry.
  pinMode (loadMosfet, OUTPUT);
  pinMode (led, OUTPUT);

  Serial.begin(115200);

  // 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...");

  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.

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

void loop()
{
  if (!digitalRead(CAN0_INT))                   // If CAN0_INT pin is low, read receive buffer
  {
    digitalWrite (loadMosfet, HIGH);            // Turn loadMosfet ON if CAN is present
    digitalWrite (led, HIGH);                   //Turn on LED to indicate CAN signal is active
    digitalWrite (powerMosfet, HIGH);
    CAN0.readMsgBuf(&rxId, &len, rxBuf);        // Read data: len = data length, buf = data byte(s)
    Serial.println("CAN Bus Is Active");
    lastMessageTime = millis();                 //Start a 'timer' to track last time a successful message was recieved
  }
  else {
    digitalWrite (led, LOW);                    //Turn off LED to show CAN signal is inactive
    Serial.println("CAN Bus Is Asleep");
  }

  if (millis() - lastMessageTime >= timeoutInterval)
  {
    digitalWrite (powerMosfet, LOW);            //Turn OFF power Mosfet to shutdown circuitry
    digitalWrite (loadMosfet, LOW);             //Turn OFF loadMosfet if CAN is not present
  }
}

Thanks in advance!

It seems you need the readMsgBuf() function. Have a look at this example.

You already have the rxId in your sketch.

(disclaimer, I have not worked with CAN myself, so I could be wrong here)

1 Like

See the "Standard_MaskFilter" and "Extended_MaskFilter" examples included with the "mcp_can" library.

Thanks,

The code can now check a particular CAN ID (0x100) and turn a led ON/OFF.:slight_smile:

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

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string

const int led = 8;                          //LED attached to pin 8 & gnd

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


void setup()
{
  pinMode (led, OUTPUT);
  Serial.begin(115200);

  // 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...");

  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.

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

  Serial.println("MCP2515 Library Receive Example...");
}

void loop()
{
  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);

    if (rxId == 0x100)                        // <<Input the specific CAN ID here
      digitalWrite(led, HIGH);
    else
      digitalWrite(led, LOW);

    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();
  }
}

I would like to be able to now turn the led ON/OFF depending on the data within the CAN frame. Looked at the example in mcp_can library but not sure how the following lines of code are filtering the messages?

  CAN0.init_Mask(0,0,0x010F0000);                // Init first mask...
  CAN0.init_Filt(0,0,0x01000000);                // Init first filter...
  CAN0.init_Filt(1,0,0x01010000);                // Init second filter...
  
  CAN0.init_Mask(1,0,0x010F0000);                // Init second mask... 
  CAN0.init_Filt(2,0,0x01030000);                // Init third filter...
  CAN0.init_Filt(3,0,0x01040000);                // Init fourth filter...
  CAN0.init_Filt(4,0,0x01060000);                // Init fifth filter...
  CAN0.init_Filt(5,0,0x01070000);                // Init sixth filter...

tidying your code abit:

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

long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128];                        // Array to store serial string

const int led = 8;                          //LED attached to pin 8 & gnd

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


void setup()
{
  pinMode (led, OUTPUT);
  Serial.begin(115200);

  // 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...");

  CAN0.setMode(MCP_NORMAL);                     // Set operation mode to normal so the MCP2515 sends acks to received data.

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

  Serial.println("MCP2515 Library Receive Example...");
}

void loop()
{
  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 == 0x100) {                     // <<Input the specific CAN ID here
      digitalWrite(led, HIGH);
      sprintf(msgString, "Standard ID: 0x%.3lX       DLC: %1d  Data:", rxId, len);
      for (byte i = 0; i < len; i++) {
        sprintf(msgString, " 0x%.2X", rxBuf[i]);
        Serial.println(msgString);
      }
    }
    else {
      digitalWrite(led, LOW);
    }

  }
}

this, by the way does NOT make use of the MCP2515 hardware filtering which was what mentioned previously.
But if you have no specific need to do so, then this code works just as fine! :slight_smile:

try running the above code. it should only printout the data bytes from 0x100.

than tell us specifically what byte/bits ur interested in and we might then be able to help you further.

hope that helps...

There are two receive buffers: RXB0 and RXB1.

RXB0 has a mask (0) and two filters (0 and 1).
RXB1 has a mask (1) and four filters (2, 3, 4, and 5).

I think the second argument to init_Mask() and init_Filt() selects Standard Data Frame (0) or Extended Frame (1). If "Standard Data Frame" is selected, the high 16 bits cover the 11-bit ID field and the low 16 bits cover the first two data bytes.

The "MASK" selects which bit you are interested in. Each 0 represents a "don't care" bit where the bit is not used for filtering.

The "FILTER" selects what values the bits we care about must have.

In the example, Mask 0 is set to 0x010F0000 so the only bits of interest are:
____ ___X ____ XXXX ____ ____ ____ ____
(Bits 0, 1, 2, 3 and 8 of the ID)

Filter 0 is set to 0x01000000 so it matches anything with the bit pattern:
xxxx xxx1 xxxx 0000 xxxx xxxx xxxx xxxx

Filter 1 is set to 0x01010000 so it matches anything with the bit pattern:
xxxx xxx1 xxxx 0001 xxxx xxxx xxxx xxxx

Since the ID is only 11 bits, I think the top 5 bit of the Mask and Filter values aren't used. Since the last 16 bits (data bits) in the Mask are set to 0 the data isn't used. The combined mask and filters will cause a match on the following ID's:
Filter 0: 1x0, 3x0, 5x0, 7x0
Filter 1: 1x1, 3x1, 5x1, 7x1

If you want the filter to only allow the ID 101, change the Mask to: 0x07FF0000 and both Filters to: 0x01010000.

To check the first two data bytes, set the last two bytes of the Mask to select which bits you care about and the Filters to which bit patterns you want to see.

1 Like

Thanks all for your help all.

@johnwasser, thank you for your detailed reply. It has helped me filter out a CAN message ID.

The CAN frame has 8 data bytes (64 bits). I am unsure how to filter out a single bit out of the 64 bits. E.g in ID 101, BYTE0, BIT0?

Thanks again!

You can only filter on the first 16 data bits of a "Standard Data Frame". Anything beyond the first 16 bits will have to be done in software, after receiving the frame.

If the low-order bit (BIT0) of the first data byte (BYTE0) must be zero:
Mask0: 0x07FF0100
Filter0: 0x01010000
Filter1: 0x01010000

If the low-order bit (BIT0) of the first data byte (BYTE0) must be one:
Mask0: 0x07FF0100
Filter0: 0x01010100
Filter1: 0x01010100

Thanks :slightly_smiling_face:@johnwasser

  1. Are we first filtering out the ID first?

then the data?

  1. Why do we need to have the same values repeated in filters 1 & 2?

Would you know of a sample code for filtering of the entire 64 bit data part of the Standard CAN Frame? I don't currently have an application for this but I would like to filter the data part of the CAN frame to turn on an LED just to learn. Can't seem to find anything on the web.

Thanks again..

The filtering of both happens at the same time. You said that you wanted to filter for the value of BYTE0 BIT0 but you didn't say if you were filtering for messages where BYTE0 BIT0 was 0 or where BYTE0 BIT0 was 1 so I gave both solutions.

Because if you don't set both filters (all four, in the case of Mask1) the data left in the filter that you don't set may filter for messages that you don't want.

I don't know of any examples. Just receive the frames of interest, based on the hardware filtering, and then, in software, check the data in the frame for whatever criteria you want.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.