Saturday, September 2, 2017

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

18 comments:

prasangika jayawickrama said...

ESP32 Software Serial library or exampale please.

iotsharing dotcom said...

esp32 has 3 serial interfaces so you need not SoftSerial.

iotsharing dotcom said...

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

Hippydipsynakedrunner said...

Can you test it using MCP2551 ?

iotsharing dotcom said...

Sorry I do not have MCP2551 :(

Anonymous said...

can I use different esp32 pins for can tx/rx ? or they must be gpio5 gpio4

iotsharing dotcom said...

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 ?

Hippydipsynakedrunner said...

what do i need yo change to read data from car CAN BUS ?

iotsharing dotcom said...

Hi it used: xQueueReceive to receive data

Hippydipsynakedrunner said...

can you explain all the parameters in

(xQueueReceive(CAN_cfg.rx_queue,&rx_frame, 3*portTICK_PERIOD_MS)==pdTRUE) ?

Arnas Risqianto 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

Voyvoda 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.