Hi.
I have self made circuit board for reading data from CAN buses in my car.
It's based on ESP32. I'm using two CAN interfaces on this board.
First, built in ESP32, is using to obtain data via UDS. Everything is working fine here.
Second CAN interface, it's MCP2515 connected via SPI to ESP32, and purpose is to read frames from PT-CAN.
Generally everything works well, but I can't make filtering to work as I want, or I don't fully understand how filtering works in this driver.
So, I need to filter few CAN IDs from among many (without filtering I'm not able to capture desired frames, there is so many frames on the bus). Here are IDs: 0x0A5, 0x0F3, 0x1A1, 0x202, 0x281, 0x3F9.
I configured filters like below:
And in this configuration, receiving of frames works in odd way. Frames 0x3F9, 0x202, 0x281 are received periodically. Frames 0x1A1 and 0x0A5 sometimes are received, sometimes not. 0x0F3 is not received.
I'm wondering if it's filtering problem or my code (receiving is working in task in FreeRTOS), or maybe I should put 16MHz quartz instead of 8MHz.
Receive code on Arduino Nano 33 IoT: Note, example uses custom MCP_CAN_lib to fit MIKROE CAN SPI Click 3.3V with 10MHz crystal, code can be found at MCP_CAN_lib / Pull requests #33.
#include "mcp_can.h"
#include <SPI.h>
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
#define CAN0_INT_PIN 9
#define CAN0_CS_PIN 10
MCP_CAN CAN0(CAN0_CS_PIN);
void setup()
{
Serial.begin(115200);
while ((!Serial) && (millis() < 3000)); // Wait for Serial connection
if (CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_10MHZ) == CAN_OK)
Serial.print("MCP2515 Init Okay!!\r\n");
else
Serial.print("MCP2515 Init Failed!!\r\n");
pinMode(CAN0_INT_PIN, INPUT); // Setting pin for /INT input
setCANFilterConfig();
CAN0.setMode(MCP_NORMAL); // Exit configuration mode
}
void loop()
{
if (CAN_MSGAVAIL == CAN0.checkReceive())
{
CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
Serial.print("ID: ");
Serial.print(rxId, HEX);
Serial.print(" Data: ");
for (int i = 0; i < len; i++) // Print each byte of the data
{
if (rxBuf[i] < 0x10) Serial.print("0");
Serial.print(rxBuf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
}
// Function to set mask and filter configuration
void setCANFilterConfig() {
CAN0.init_Mask(0, 0x07FF); // Full mask for ID
CAN0.init_Mask(1, 0x07FF); // Full mask for ID
CAN0.init_Filt(0, 0x05E); // Filter for ID 0x5E (standard ID 94)
CAN0.init_Filt(1, 0x061); // Filter for ID 0x61 (standard ID 97)
Serial.println("Config 1: Standard IDs 0x5E and 0x61 only");
}
Serial Monitor output:
Entering Configuration Mode Successful!
Setting Baudrate Successful!
MCP2515 Init Okay!!
Starting to Set Mask!
Setting Mask Successful!
Starting to Set Mask!
Setting Mask Successful!
Starting to Set Filter!
Setting Filter Successful!
Starting to Set Filter!
Setting Filter Successful!
Config 1: Standard IDs 0x5E and 0x61 only
void setCANFilterConfig() {
// **Config 2: Allow a range of IDs by using partial mask**
CAN0.init_Mask(0, 0x07F0); // Only check the first 8 bits of ID
CAN0.init_Mask(1, 0x07F0);
CAN0.init_Filt(0, 0x050); // Accept IDs 0x50 to 0x5F
Serial.println("Config 2: Allow IDs in range 0x50-0x5F");
}
Serial Monitor output:
Entering Configuration Mode Successful!
Setting Baudrate Successful!
MCP2515 Init Okay!!
Starting to Set Mask!
Setting Mask Successful!
Starting to Set Mask!
Setting Mask Successful!
Starting to Set Filter!
Setting Filter Successful!
Config 2: Allow IDs in range 0x50-0x5F
ID: 5E Data: 08 58
ID: 5F Data: 08 58
ID: 60 Data: 08 58
ID: 61 Data: 08 58
ID: 5F Data: 08 58
ID: 60 Data: 08 58
ID: 61 Data: 08 58
ID: 5E Data: 08 58
ID: 5E Data: 08 58
I would also suggest using hardware interrupts to read the buffers instead of polling the state of the interrupt like most examples do in the library. It is entirely likely stuff is happening too fast to capture with a polled MCP2515. I experienced similar when I was reverse engineering a DeviceNET bus.
There is also a hardware overflow bit in the MCP2515 that will push a received message in buffer 1 to buffer 2 overwriting buffer 2. I am pretty sure that is enabled by default in the library. You should try disabling that.
bit 6-5 RXM[1:0]: Receive Buffer Operating mode bits
11 = Turns mask/filters off; receives any message
10 = Reserved
01 = Reserved
00 = Receives all valid messages using either Standard or Extended
Identifiers that meet filter criteria; Extended ID Filter registers,
RXFnEID8:RXFnEID0, are applied to the first two bytes of data
in the messages with standard IDs
Hi guys. Receiving in interrupt do the trick, filtering is working as expected now, big thanks to both of you!
Right now I need to spend some time with my freertos code, receiving stops after a while, I think some queues may be overloaded.
I understand you are busy, but I would greatly appreciate it if I could ask you a question.
I am using an ESP32 and MCP2515 and was able to receive by polling using the same library. However, when I ran the interrupt program, the INT did not respond at all and did not show up on the serial monitor. Normally it should go from 5V to 0V and the interrupt should be recognized, but it stays at 5V all the time.
I would appreciate it if you could tell me if there is a way to fix this.
the ESP32 uses 3.3V logic - inputting 5V can damage the device
All ESP32 GPIOs can be configured as interrupts.
using a MCP2515 with an ESP32 I used GPIOs
// MCP2515 INT to ESP32 GPIO22
// MCP2515 SCK to ESP32 GPIO18 CLK
// MCP2515 SI to ESP32 GPIO23 MOSI
// MCP2515 SO to ESP32 GPIO19 MISO
// MCP2515 CS to ESP32 GPIO5 CS
// MCP2515 GND to ESP32 GND
// MCP2515 VCC to ESP32 3.3V
ESP32 is a module that can be developed with ArduinoIDE and used with the same programs as Arduino.
ESP32 can use interrupt on all GPIOs.
The current pin assignments are as follows.
MISO 19
MOSI 23
SCK 18
CS 12
INT 16
I am confident that wiring is not a problem.
The reason for this is that I tried the example sketch code included in the mcp_can library and it displayed 8 bytes of data continuously on the serial printout with no problems. Here is the code
// CAN Receive Example
//
#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 16 // Set INT to pin 2
MCP_CAN CAN0(12); // Set CS to pin 12
void setup()
{
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_8MHZ) == 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);
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
*********************************************************************************************************/
With this in mind, we considered that the lack of interrupts was not a wiring problem, but something wrong with the setting.
We then verified that the voltage does not change from 5V to 0V. As a verification method, I tried using another Arduino's digital input to have the serial monitor display only the moment it changed, but it did not. I do not have an oscilloscope.
Any advice on what this information can tell me would be appreciated.
Thank you in advance for your time.
What ID frames that you're trying to receive has? INT doesn't go low if you don't read buffer (according to documentation). If your frames are outside filter and you did receive nothing, interrupt will not work. I've got similar problems when my buffer is running out of space.
@horace Thanks for your reply.
In the first stage I tried many things to use the MCP2562+ESP32's built-in CAN controller, but in the end I gave up.
Because the processor of the Freenove_ESP32_WROOM_ I bought is the ESP32WROOM32E and the dedicated library on Github seems to support the ESP32WROOM32D which has not been updated for 5 years and the bit rate is half the set value! I'm trying to find out if this is a Bug? Specification change?
As a beginner I found it difficult and decided to use the MCP2515 module which has a CAN controller with it.
I'll put the Github link below in case you are interested.
There is a lot written about the problems with esp32 in the issue section.
I also knew that the esp32 was 3.3V driven, so I knew that a 5V input would not be good. So I connected 3.3V to Vcc and the POW light went dark. Also, using the same program as with the 5V input, I could not receive the signal the moment I connected it to 3.3V.
I know the risk, but I am now using the 5V one which I can receive. Thanks for the advice.
Now I keep sending the same CAN pseudo signal from Arduino Nano + MCP2515 and trying to receive ESP32 + MCP2515.
When using CAN_receive in the mcp_can sketch example, it is displayed on the serial monitor without any problem. The source of CAN_receive is shown below.
// CAN Receive Example
//
#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 16 // Set INT to pin 2
MCP_CAN CAN0(12); // Set CS to pin 12
void setup()
{
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_8MHZ) == 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);
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
*********************************************************************************************************/
At this time, the serial monitor displayed the following image.
Reception is working fine. Here I thought that if I am receiving, the buffer could accumulate at least once and change int.
However, when I tried a program with interrupt handling, not a single one appeared on the serial monitor. I think this is because the int processing is not well done, so it is removed from the condition and not displayed.
I don't know what I should change, though it doesn't seem to be filtering by ID.
We were able to communicate without any problems!
Thanks! @wittrup for answering my questions and everyone else who responded. They were really helpful.
Thank you from the bottom of my heart.
From a Japanese university student
Sorry . I'm back again. I hope you can answer my question again.
I have to receive at 1000KBPS, but when I received with the following code, the memory overflowed and stopped receiving.
The following is almost identical to the code created by @wittrap.
#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_PIN 16
#define CAN0_CS_PIN 12
MCP_CAN CAN0(CAN0_CS_PIN); // Set CS to pin 10
void setup()
{
Serial.begin(9600);
while ((!Serial) && (millis() < 3000)); // Wait for Serial connection
pinMode(CAN0_INT_PIN, INPUT_PULLUP);
// Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
if(CAN0.begin(MCP_STDEXT, CAN_1000KBPS, MCP_8MHZ) == 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_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CAN0_INT_PIN), CAN0INTE, FALLING);
Serial.println("MCP2515 Library Receive Example...");
}
void loop()
{
delay(1);
}
void CAN0INTE() {
CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
Serial.print("ID: ");
if (rxId < 256) Serial.print("0");
Serial.print(rxId, HEX);
Serial.print(" Data: ");
for (int i = 0; i < len; i++) // Print each byte of the data
{
if (rxBuf[i] < 0x10) Serial.print("0");
Serial.print(rxBuf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
However, after displaying about 10 pieces of 8-byte data as it was, a mysterious string of characters appeared on the serial monitor and was not updated thereafter. I examined the string and discovered that it was a memory overflow.
As a solution, the ISR content needed to be as lightweight as possible, so the ISR content was moved into the loop function. The code is as follows.
#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_PIN 16
#define CAN0_CS_PIN 12
MCP_CAN CAN0(CAN0_CS_PIN); // Set CS to pin 12
volatile bool canMessageReceived = false;
void setup()
{
Serial.begin(115200);
while ((!Serial) && (millis() < 3000)); // Wait for Serial connection
pinMode(CAN0_INT_PIN, INPUT_PULLUP);
// Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
if (CAN0.begin(MCP_STDEXT, CAN_1000KBPS, MCP_8MHZ) == 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.
attachInterrupt(digitalPinToInterrupt(CAN0_INT_PIN), CAN0INTE, FALLING);
Serial.println("MCP2515 Library Receive Example...");
}
void loop()
{
if (canMessageReceived)
{
canMessageReceived = false; // clear flag
if (CAN0.readMsgBuf(&rxId, &len, rxBuf) == CAN_OK) // receive message
{
Serial.print("ID: ");
if (rxId < 256) Serial.print("0");
Serial.print(rxId, HEX);
Serial.print(" Data: ");
for (int i = 0; i < len; i++) // Print each byte of the data
{
if (rxBuf[i] < 0x10) Serial.print("0");
Serial.print(rxBuf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
else
{
Serial.println("Error reading CAN message");
}
}
delay(1); // timing
}
void CAN0INTE()//ISR
{
canMessageReceived = true; // Flag
}
After doing this, the mysterious string was no longer displayed on the serial monitor, but eventually it stopped receiving immediately.
Serial monitor when acquired by polling.
A dozen data are received in 0.03 seconds.
↑After receiving these 8 frames, it stops and receives nothing.
Supplementary information: Currently, the device we want to acquire CAN is from the 2010s and uses a communication method that combines RS232 and CAN. 176 bytes of data are divided into 22 frames and sent at once at a communication rate of 1000KBPS.
The picture shown on the serial monitor acquired by polling is an example.
However, since all data cannot be acquired by polling, we would like to acquire 22 frames of information continuously by interrupting. But at present, it overflows.