Demo 31: How to use Arduino ESP32 CAN interface

1. Introduction
- CAN stands for Controller Area Network (CAN bus). This protocol is very popular in automotive domain. In order to understand more about history, benefits, characteristics, message format of CAN, you can refer:
http://www.ni.com/white-paper/2732/en/
http://www.ti.com/lit/an/sloa101b/sloa101b.pdf
- As you knew in Introduction to ESP32, ESP32 also supports CAN interface. So I am going to make a demo for this with Arduino.
- In this demo, 2 ESP32 modules will be used: first module will send the string "hellocan" to second module. The second module will convert the string to upper case and respond it back to first module and first module will show the result in theTerminal.
2. Hardware
- ESP32 only supplies CAN controller. So you need CAN transceiver for this demo. I bought 2 CAN trasceivers here.
 Figure: ESP32 CAn transceiver
- ESP32 GPIO5 will act as CAN_Tx.
- ESP32 GPIO4 will act as CAN_Rx.
- So you can connect pins belows (ESP32_X: module ESP32 X, CAN_X: module CAN X, where X is 1 or 2 since we have 2 modules):
ESP32_1 IO5 - CAN_1 CTX 
ESP32_1 IO4 - CAN_1 CRX
CAN_1 CANH - CAN_2 CANH
CAN_1 CANL - CAN_2 CANL
ESP32_2 IO5 - CAN_2 CTX 
ESP32_2 IO4 - CAN_2 CRX
3. Software
- In order to make this demo, I used CAN driver which is made by Thomas Barth (Thanks Thomas :))
- Download the CAN library for ESP32 here. Unzip it and copy to "Arduino/libraries" folder.
- Source code for first ESP32 module: receiving string, converting to upper case and respond back:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <ESP32CAN.h>
#include <CAN_config.h>

/* the variable name CAN_cfg is fixed, do not change */
CAN_device_t CAN_cfg;

void setup() {
    Serial.begin(115200);
    Serial.println("iotsharing.com CAN demo");
    /* set CAN pins and baudrate */
    CAN_cfg.speed=CAN_SPEED_1000KBPS;
    CAN_cfg.tx_pin_id = GPIO_NUM_5;
    CAN_cfg.rx_pin_id = GPIO_NUM_4;
    /* create a queue for CAN receiving */
    CAN_cfg.rx_queue = xQueueCreate(10,sizeof(CAN_frame_t));
    //initialize CAN Module
    ESP32Can.CANInit();
}

void loop() {
    CAN_frame_t rx_frame;
    //receive next CAN frame from queue
    if(xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS)==pdTRUE){

      //do stuff!
      if(rx_frame.FIR.B.FF==CAN_frame_std)
        printf("New standard frame");
      else
        printf("New extended frame");

      if(rx_frame.FIR.B.RTR==CAN_RTR)
        printf(" RTR from 0x%08x, DLC %d\r\n",rx_frame.MsgID,  rx_frame.FIR.B.DLC);
      else{
        printf(" from 0x%08x, DLC %d\n",rx_frame.MsgID,  rx_frame.FIR.B.DLC);
        /* convert to upper case and respond to sender */
        for(int i = 0; i < 8; i++){
          if(rx_frame.data.u8[i] >= 'a' && rx_frame.data.u8[i] <= 'z'){
            rx_frame.data.u8[i] = rx_frame.data.u8[i] - 32;
          }
        }
      }
      //respond to sender
      ESP32Can.CANWriteFrame(&rx_frame);
    }
}
- Source code for second ESP32 module: sending the string that need to be converted to upper case, receiving response and show it to Terminal
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <ESP32CAN.h>
#include <CAN_config.h>

/* the variable name CAN_cfg is fixed, do not change */
CAN_device_t CAN_cfg;

void setup() {
    Serial.begin(115200);
    Serial.println("iotsharing.com CAN demo");
    /* set CAN pins and baudrate */
    CAN_cfg.speed=CAN_SPEED_1000KBPS;
    CAN_cfg.tx_pin_id = GPIO_NUM_5;
    CAN_cfg.rx_pin_id = GPIO_NUM_4;
    /* create a queue for CAN receiving */
    CAN_cfg.rx_queue = xQueueCreate(10,sizeof(CAN_frame_t));
    //initialize CAN Module
    ESP32Can.CANInit();
}

void loop() {
    CAN_frame_t rx_frame;
    //receive next CAN frame from queue
    if(xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS)==pdTRUE){

      //do stuff!
      if(rx_frame.FIR.B.FF==CAN_frame_std)
        printf("New standard frame");
      else
        printf("New extended frame");

      if(rx_frame.FIR.B.RTR==CAN_RTR)
        printf(" RTR from 0x%08x, DLC %d\r\n",rx_frame.MsgID,  rx_frame.FIR.B.DLC);
      else{
        printf(" from 0x%08x, DLC %d\n",rx_frame.MsgID,  rx_frame.FIR.B.DLC);
        for(int i = 0; i < 8; i++){
          printf("%c\t", (char)rx_frame.data.u8[i]);
        }
        printf("\n");
      }
    }
    else
    {
      rx_frame.FIR.B.FF = CAN_frame_std;
      rx_frame.MsgID = 1;
      rx_frame.FIR.B.DLC = 8;
      rx_frame.data.u8[0] = 'h';
      rx_frame.data.u8[1] = 'e';
      rx_frame.data.u8[2] = 'l';
      rx_frame.data.u8[3] = 'l';
      rx_frame.data.u8[4] = 'o';
      rx_frame.data.u8[5] = 'c';
      rx_frame.data.u8[6] = 'a';
      rx_frame.data.u8[7] = 'n';

      
      ESP32Can.CANWriteFrame(&rx_frame);
    }
}
4. Result
Figure: ESP32 CAN demo

Post a Comment

54 Comments

Unknown said…
ESP32 Software Serial library or exampale please.
esp32 has 3 serial interfaces so you need not SoftSerial.
here is it:

Demo 2: How to use multiple Serial ports on Arduino ESP32

http://www.iotsharing.com/2017/05/how-to-use-serial-arduino-esp32-print-debug.html
Can you test it using MCP2551 ?
Sorry I do not have MCP2551 :(
Anonymous said…
can I use different esp32 pins for can tx/rx ? or they must be gpio5 gpio4
Hi

Proposed connection:
ESP32 VP231 Description
GPIO5 / GPIO12 / GPIO25 D CAN TX
GPIO4 / GPIO14 / GPIO35 R CAN RX
Anonymous said…
It work with MCP2551 to, but use 5V for supply not 3.3 V.
martii said…
I was wondering how code would look like if I wanted to send 3 messages, each with different ID in one go. I was trying to do 3x the same code ending with ESP32Can.CANWriteFrame(&rx_frame); but it seems only one first ID is being sent not the following 2 with different IDs.
Anonymous said…
Can you please explain what's the difference between the CAN driver and the Arduino library and where you use each one of them ?
what do i need yo change to read data from car CAN BUS ?
Hi it used: xQueueReceive to receive data
can you explain all the parameters in

(xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS)==pdTRUE) ?
Unknown said…
Just tested with both ESP32 it's works. But using Logic Analyzer, it's unreadable. It's looks has wrong speed
sixweb said…
Hello, tested with ESP32 + SN65HVD230 module. It wasn't worked for my Astra H because the middle speed bus in GM models uses an unusual 95kbps speed. However I could figure out, in ESP32SJA1000.cpp this snipped can be added in begin() at the section "switch (baudRate)":

case (long)95E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 25);
break;


Now this works.

sixweb
Anonymous said…
Hello guys,

I flashed the ESP32 with the example code provided. However, it is not working. Nothing outs from the GPIO5 and GPIO4 pins. The library is not working.

Has someone successfully made it worked?
Dinesh said…
How to use cjmcu 1051 instead of the cjmcu 230. I cant get it to work with the same wiring schematic.

Thank you.
Dinesh said…
Got it to work with cjmcu 1051. Just have set the s pin in the transceiver low or ground. Preferably low.

Thank you.
Unknown said…
Hey there,
I followed your step. it works. If I plug the Can bus line to real Vehicle. I cannot receive any data. Does the code work for real vehicle
Unknown said…
I Don't get it working. I conected the ESP32(DOIT DEVKITV1) to SN65HVD230 by GPIO5 to CTX and GPIO4 to CRX. I downloaded the Library as ZIP and loaded it into Arduino by ZIP-Bibliothek hinzufügen. I uploaded to the first ESP32 the ESP32CAN example and to the second ESP32 the ESP32CANSEND example. But i dont get only feedback in the serial monitor with this message:

ets Jun 8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:808
load:0x40078000,len:6084
load:0x40080000,len:6696
entry 0x400802e4
E (106) spiram: SPI RAM enabled but initialization failed. Bailing out.
iotsharing.com CAN demo


No things are written like "HELLOCAN".

What is the problem, what did I wrong?
Unknown said…
I am having trouble tranmitting the CAN frame using ESP32 with cjmcu-1051 board (PIN S is grounded) .Is there a way to know the if there is a TX error or Tx err que status ? Do i need to terminate both ends using 120 Ohm ?It is rather a very short CAN wire your example didnt talk about terminations .So i assume may be this is not requied.Any helpis appreciated
Anonymous said…
Hi there,

I am only having the first CAN message. Where's the rest?
ID: 0x80 DLC: 0 Data: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
ID: 0x80 DLC: 0 Data: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
ID: 0x80 DLC: 0 Data: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
...

Should be like:
" 80";"0";"";""
"286";"8";"00 00 00 00 12 00 FE FF"
"339";"8";"00 00 00 00 00 00 9A 03"
"173";"8";"D4 7D 2A 01 25 02 24 00"
"156";"7";"FD 05 21 00 00 00 00"
"279";"8";"00 00 00 00 00 00 00 00"
" 80";"0";"";""
"286";"8";"00 00 00 00 FC FF F8 FF"
"339";"8";"00 00 00 00 00 00 9A 03"
"173";"8";"D4 7D 2A 01 25 02 24 00"
"156";"7";"FD 05 21 00 00 00 00"
"279";"8";"00 00 00 00 00 00 00 00"

Here is the simple code:
void loop() {
CAN_frame_t rx_frame;
if(xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3*portTICK_PERIOD_MS) == pdTRUE){

sprintf(msgString, "ID: 0x%.2X DLC: %1d Data: ", rx_frame.MsgID, rx_frame.FIR.B.DLC);
Serial.print(msgString);

for(int i = 0; i < 8; i++){
sprintf(dataString, "0x%.2X ", rx_frame.data.u8[i]);
Serial.print(dataString);
}
Serial.println();
}
}

Any ideas?
Dustin said…
I wish there were a way to do this in MicroPython, but only the PyBoard version seems to have a CAN class. Unsupported on ESP32... [sadface]
TonySK said…
is it possible to use 2 can lines at same time? I need to receive from 2 separate lines
mhtalha07 said…
Thanks, it works fine for me! (with SN65HVD230 CAN Board + ESP32 DOIT layout board).
The resistance across the wires was 60Ω.
Anonymous said…
Which CAN Transceiver are you using?
delta9 said…
Can we use 2 CAN channels on ESP32? Meaning two CAN transceivers.
Thanks in advance!
Unknown said…
Can anyone share the Product link of SN65HVD230 CAN transceiver. Iwant buy it online
RudolfAtRTC said…
I was hoping to start with this simple example.
As with some of the others: The example can be compiled and started via upload. It runs "error-free", but nothing happens on GPIO5! The pin shows a constant high level.
paulo_palaoro said…
espressif changed the hardware from can to twai, the libraries from can to arduino do not work.
If anyone has a library that works, please provide a link.
Anonymous said…
works well with 2 x OLIMEX ESP32-EVB ,

change RX-Pin to: CAN_cfg.rx_pin_id = GPIO_NUM_35;

also try lower speed: CAN_cfg.speed=CAN_SPEED_125KBPS;

DungPV said…
Hello, i try this code, mut not showing anything in serial monitor
when i press buttun EN in ESP32, it show

19:32:19.051 -> ets Jun 8 2016 00:22:57
19:32:19.051 ->
19:32:19.051 -> rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
19:32:19.051 -> configsip: 0, SPIWP:0xee
19:32:19.051 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
19:32:19.051 -> mode:DIO, clock div:1
19:32:19.051 -> load:0x3fff0018,len:4
19:32:19.051 -> load:0x3fff001c,len:1044
19:32:19.051 -> load:0x40078000,len:10124
19:32:19.051 -> load:0x40080400,len:5856
19:32:19.051 -> entry 0x400806a8
19:32:19.153 -> iotsharing.com CAN demo
Tott said…
Hello,

you want code for exemple receive can information of ECU MEGASQUIRT MS2 exemple :

ESP32 and SN65HDV230 3.3v version waveshare!

code basic:

#include
#include

CAN_device_t CAN_cfg;
int rpm;
int tps;
int kpa;
int temp;
int avance;

void setup() {
Serial.begin(115200);
Serial.println("iotsharing.com CAN demo");
Serial.print(" ID ");Serial.print(" | ");Serial.print(" RPM ");Serial.print(" | "),Serial.println("TPS");
CAN_cfg.speed=CAN_SPEED_500KBPS;
CAN_cfg.tx_pin_id = GPIO_NUM_5;
CAN_cfg.rx_pin_id = GPIO_NUM_4;
CAN_cfg.rx_queue = xQueueCreate(250,sizeof(CAN_frame_t));
//initialize CAN Module
ESP32Can.CANInit();
}

void loop() {
CAN_frame_t rx_frame;
//Serial.println("test");
//receive next CAN frame from queue
if(xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS)==pdTRUE){


if (rx_frame.MsgID == 1512){ // hex= 0x5F0
for(int i = 0; i < 8; i++){
if(rx_frame.data.u8[i] >= '0' && rx_frame.data.u8[i] <= '9'){
rx_frame.data.u8[i] = rx_frame.data.u8[i] - 32;
}
}
rpm = 256*rx_frame.data.u8[2]+rx_frame.data.u8[3];
tps= (256*rx_frame.data.u8[6]+ rx_frame.data.u8[7])/10;
kpa = ((256*rx_frame.data.u8[0]+ rx_frame.data.u8[1])/10);
temp = ((256*rx_frame.data.u8[4] + rx_frame.data.u8[5])/10 -32) *5/9; //CLT
//temp = ((256*msg.buf[4] + msg.buf[5])/10 -32) *5/9; //CLT
//kpa = ((256*msg.buf[0]+ msg.buf[1])/10);

Serial.print("Id: ");Serial.print(rx_frame.MsgID);Serial.print(" | ");Serial.print("Rpm: ");Serial.print(rpm);Serial.print(" | ");Serial.print("Tps: ");Serial.print(tps);
Serial.print(" | ");Serial.print("Kpa: ");Serial.print(kpa);Serial.print(" | ");Serial.print("Temp: ");Serial.println(temp);
}
if (rx_frame.MsgID == 1513){ // hex= 0x5F0
for(int i = 0; i < 8; i++){
if(rx_frame.data.u8[i] >= '0' && rx_frame.data.u8[i] <= '9'){
rx_frame.data.u8[i] = rx_frame.data.u8[i] - 32;
}
}
avance = ((256*rx_frame.data.u8[6]) + rx_frame.data.u8[7]) /10;
Serial.print("Id: ");Serial.print(rx_frame.MsgID);Serial.print(" | ");Serial.print("Avance: ");Serial.println(avance);
}
//respond to sender
//ESP32Can.CANWriteFrame(&rx_frame);
}


} // finich

the code all ok receive information, just adapt to your dream...
you have question contact me (MS2 and MS3 and offgear ECU)
Tott

Thanks iotsharring.com
shawn.t said…
In CanConfig.h, the handle to the queue is created, and then in can.c, sendQueuefromISR is called, but I don't see where the queue is created. Am I missing it somewehere?

/** \brief CAN configuration structure */
typedef struct {
CAN_speed_t speed; /**< \brief CAN speed. */
gpio_num_t tx_pin_id; /**< \brief TX pin. */
gpio_num_t rx_pin_id; /**< \brief RX pin. */
QueueHandle_t rx_queue; /**< \brief Handler to FreeRTOS RX queue. */
}CAN_device_t;
Maxim said…
sixweb said...
Hello, tested with ESP32 + SN65HVD230 module. It wasn't worked for my Astra H because the middle speed bus in GM models uses an unusual 95kbps speed. However I could figure out, in ESP32SJA1000.cpp this snipped can be added in begin() at the section "switch (baudRate)":

case (long)95E3:
modifyRegister(REG_BTR1, 0x0f, 0x0c);
modifyRegister(REG_BTR0, 0x3f, 25);
break;


Now this works.

sixweb

April 29, 2018 at 11:34 AM

Hi, I have this car, and I want to read codes from MS-CAN. I tried this code, but it is not works, can some body give the full scotch to my car, thanks :)
Anonymous said…
Hi, i copied the examples and they work fine, with the exception of the Baudrate.
I want to use a Baudrate of 1000kBit/s, but the ESP is runnnin the CANBus with only 500kBit/s.

// init CAN-Bus
Serial.println("iotsharing.com CAN demo");
/* set CAN pins and baudrate */
CAN_cfg.speed=CAN_SPEED_1000KBPS;
CAN_cfg.tx_pin_id = GPIO_NUM_2; // free Pin on TTgo T-Journal
CAN_cfg.rx_pin_id = GPIO_NUM_4; // free Pin on TTgo T-Journal
/* create a queue for CAN receiving */
CAN_cfg.rx_queue = xQueueCreate(10,sizeof(CAN_frame_t));
//initialize CAN Module
ESP32Can.CANInit();
Serial.println("setup finished");

Alex
I never stop myself to mention some thing about it.
every recommendation of your website is awesome.
Bape Hoodie said…
Extremely go through article.
Unknown said…
Hello,
I tried the code with an old Nodemcu board (ESP32 WROM-32) and it works perfectly. If I test the code with the same Nodemcu but different ESP, i.e. with an ESP32-WROM-32E. Crashes and gets stuck at this point:

ESP32Can.CANWriteFrame(&tx_frame);

If I comment out the line it keeps running.

I use GPIO4 and 5... I also used other GPIOs, nothing changes. Flashing the bootloader, too, does nothing. The proram runs the loop twice and then nothing happens

Does somebody has any idea?
Anonymous said…
Thanks for sharing this awesome blog

store
store said…
Thanks for sharing this awesome blog
hunza valley said…
Thank you so much for sharing Blog is very important
Anonymous said…
I found your blog through a friend's recommendation, and I'm so glad I did. Your posts are a breath of fresh air in the [industry/niche]. You offer unique perspectives and tackle subjects others often overlook
madhav said…
Hello sir,
Im using esp32can_mirror examples and i set CAN_cfg.speed = CAN_SPEED_500KBPS
but i received data and message ID on 250KBPS its sense.
Can you help me.
Revolutionize Your Internet Connection with Wavlink wifi Repeater: Stay Connected, Always!
Really appreciated liked your post thanks to share valuable content.
Horse Boarding said…
This blog is intresting and very imformative.
Thanks for sharing this article it was quite insightful.Hoping to see more articles.
Stylozone said…
This blog is really amazing blog. thanks for sharing with Men Leather Jackets
Halldor said…
HI I am new to Can bus but need to be able run at 100 KBPS with timing at 81.25% but I can not figure out the setting of T1,T2 and the preescaler.
All the fashionista and shopaholic females, gather up! J4Jacket has got an incredible collection of Women’s Jackets for you all. Catering to a school-going girl’s cute wishes to satisfy a grown-up woman’s desires, we have got it all. Jackets have been in the fashion