Using the official CAN driver with Arduino

dmaxben
Posts: 51
Joined: Thu Nov 16, 2017 6:04 pm

Using the official CAN driver with Arduino

Postby dmaxben » Mon Oct 07, 2019 2:38 pm

Has anyone successfully used the ESP-IDF CAN driver in Arduino?

Ive tried a bunch of different things, but cant get it to compile... :?

lbernstone
Posts: 32
Joined: Mon Jul 22, 2019 3:20 pm

Re: Using the official CAN driver with Arduino

Postby lbernstone » Mon Oct 07, 2019 7:10 pm

Just the un-C++ bits:

Code: Select all

static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
//Filter all other IDs except MSG_ID
static const can_filter_config_t f_config = {.acceptance_code = (uint32_t)(MSG_ID << 21),
                                             .acceptance_mask = (uint32_t)~(CAN_STD_ID_MASK << 21),
                                             .single_filter = true};
//Set to NO_ACK mode due to self testing with single module
static const can_general_config_t g_config = {.mode = CAN_MODE_NO_ACK,
                                              .tx_io = (gpio_num_t)TX_GPIO_NUM,
                                              .rx_io = (gpio_num_t)RX_GPIO_NUM,
                                              .clkout_io = (gpio_num_t)CAN_IO_UNUSED,
                                              .bus_off_io = (gpio_num_t)CAN_IO_UNUSED,
                                              .tx_queue_len = 5,
                                              .rx_queue_len = 5,
                                              .alerts_enabled = CAN_ALERT_NONE,
                                              .clkout_divider = 0
                                              };

static SemaphoreHandle_t tx_sem;
static SemaphoreHandle_t rx_sem;
static SemaphoreHandle_t ctrl_sem;
static SemaphoreHandle_t done_sem;

/* --------------------------- Tasks and Functions -------------------------- */

static void can_transmit_task(void *arg)
{
    can_message_t tx_msg = {.flags = CAN_MSG_FLAG_SELF, .identifier = MSG_ID, .data_length_code = 1};

ESP_Dazz
Posts: 143
Joined: Fri Jun 02, 2017 6:50 am

Re: Using the official CAN driver with Arduino

Postby ESP_Dazz » Tue Oct 08, 2019 6:34 am

dmaxben wrote: Ive tried a bunch of different things, but cant get it to compile
Could explain what steps you have taken to port the driver over to Arduino, and also the compilation error log you are seeing.

MStackoverflow
Posts: 5
Joined: Mon Oct 07, 2019 4:55 pm

Re: Using the official CAN driver with Arduino

Postby MStackoverflow » Tue Oct 08, 2019 3:26 pm

Have you seen this library from miwagner?

https://github.com/miwagner/ESP32-Arduino-CAN
It is a port from the official CAN driver, it work as expected.

dmaxben
Posts: 51
Joined: Thu Nov 16, 2017 6:04 pm

Re: Using the official CAN driver with Arduino

Postby dmaxben » Wed Oct 09, 2019 3:19 pm

MStackoverflow wrote:
Tue Oct 08, 2019 3:26 pm
Have you seen this library from miwagner?

https://github.com/miwagner/ESP32-Arduino-CAN
It is a port from the official CAN driver, it work as expected.
The miwagner arduino driver and Thomas Barthe's arduino ESP-32 CAN driver are significantly different than the current ESP-IDF CAN driver. They are *NOT* direct ports.

The miwagner and Thomas Barth Arduino ESP32 CAN drivers have some big issues with queues, and it doesnt have any protection/safety with large multi-threaded/tasked programs. If you are trying to send CAN messages from several different areas of your multi-tasked code, it usually gets choked sooner or later....

And the biggest issue of all is that it doesnt have the IRAM_ATTR directive, which would allow it to try to access random parts of flash/code at ANY time....this includes during an OTA update (which disables flash-cache). If you leave the miwagner/Thomas Barth Arduino ESP32 CAN driver running during an OTA update, it was guaranteed to crash the whole ESP32.

The official ESP-IDF CAN driver doesnt have any of these issues, and is completely safe to use with any program, and have running at all times, including during an OTA update.

dmaxben
Posts: 51
Joined: Thu Nov 16, 2017 6:04 pm

Re: Using the official CAN driver with Arduino

Postby dmaxben » Wed Oct 09, 2019 3:41 pm

Also, as a update, I was able to get the official ESP-IDF CAN driver working perfectly in Arduino with help from a friend who knows MUCH more about programming and the ESP32 than I do. Ive been having some problems with getting my WiFi Manager to work properly in the ESP-IDF, otherwise I would have switched to ESP-IDF/moved away from Arduino a while ago...thats the next project now that the CAN frustrations have been fixed.

I highly recommend doing all ESP32 CAN stuff ONLY using the official ESP-IDF CAN driver. Its task/thread-safe, remains functional while OTA is running, doesnt seem to disturb the bus when starting/rebooting the ESP32...it just works perfectly! I wish I had just started out with the ESP-IDF CAN driver from the start; it would have saved me a lot of frustration and bug-tracking.

Here is some basic starter code for anyone else who wants to use the official ESP-IDF CAN driver in Arduino...

Be sure to adjust your GPIO settings, CAN bus bitrate, queue sizes, and Rx/Tx blocking (ticks) to suit your own needs/application. You can also add additional code to confirm to your program that the message was sent successfully, error checking, etc. This is just basic stuff to get anyone started.

Code: Select all

#include <driver/can.h>
#include driver/gpio.h>
#include <esp_system.h>
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"


// define your CAN messages here, OR you can define them locally...
// standard 11-bit frame ID = 0
// extended 29-bit frame ID = 1
// format:   can_message_t (name of your message) = {std/ext frame, message ID, message DLC, {data bytes here}};

can_message_t myMessageToSend = {0, 0x123, 8, {0x01, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x99}};

uint32_t previousMillis;
const uint32_t interval = 1000;

//=================================================

void setup()
{
// anything else you might need to do....
setup_can_driver();
}

void setup_can_driver()
{
 can_general_config_t general_config = {
        .mode = CAN_MODE_NORMAL,
        .tx_io = (gpio_num_t)GPIO_NUM_5,
        .rx_io = (gpio_num_t)GPIO_NUM_4,
        .clkout_io = (gpio_num_t)CAN_IO_UNUSED,
        .bus_off_io = (gpio_num_t)CAN_IO_UNUSED,
        .tx_queue_len = 100,
        .rx_queue_len = 65,
        .alerts_enabled = CAN_ALERT_NONE,
        .clkout_divider = 0};
    can_timing_config_t timing_config = CAN_TIMING_CONFIG_500KBITS();
    can_filter_config_t filter_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
    esp_err_t error;

    error = can_driver_install(&general_config, &timing_config, &filter_config);
    if (error == ESP_OK)
    {
        Serial.println("CAN Driver installation success...");
    }
    else
    {
        Serial.println("CAN Driver installation fail...");
        return;
    }

    // start CAN driver
    error = can_start();
    if (error == ESP_OK)
    {
        Serial.println("CAN Driver start success...");
    }
    else
    {
        Serial.println("CAN Driver start FAILED...");
        return;
    }
}

//=======================================================================

void loop()
{
     can_message_t rx_frame;
    if (can_receive(&rx_frame, pdMS_TO_TICKS(1000)) == ESP_OK)
    {
    //do whatever you need with your received CAN messages here
    // follow the can_message_t struct to learn how to decode/process the received frame.
    }
    
     if (millis() - previousMillis > interval)  // send out your CAN frame once every second
        { 
            previousMillis = millis();
            can_transmit(&myMessageToSend, pdMS_TO_TICKS(1000));
      }
}

ESP_Dazz
Posts: 143
Joined: Fri Jun 02, 2017 6:50 am

Re: Using the official CAN driver with Arduino

Postby ESP_Dazz » Wed Oct 09, 2019 4:44 pm

@ dmaxben Just a few pointers regarding IDF CAN driver usage

It's probably a good idea to use the member names when initializing a CAN message structure in case the order of the members change in the future versions of the driver. There are also a bunch of message flags documented here to set/check to a certain type (e.g., extended ID, RTR etc).

Code: Select all

can_message_t my_message = {
	.identifier = 0xABCD,
	.data_length_code = 8,
	.flags = CAN_MSG_FLAG_EXTD | CAN_MSG_FLAG_SS,
	.data = {1, 2 , 3 , 4 ,5 ,6 ,7 ,8}
};
The CAN driver was designed to be used in a multi-threaded manner, so general practice is to split receiving and transmitting into separate tasks (threads). This way, the RX queue can be emptied and the TX queue filled simultaneously by the two threads, and you won't need such a large TX and RX queue. Default queue length setting by the driver is 5, a length of 100 seems abnormally long.

You should still be able to directly call FreeRTOS functions and types to create tasks in Arduino. I suggest you take a look at the Self-Test example for a start. You should be able to easily adapt that to Arduino. In theory, it should work by just copying the code, renaming app_main() to setup(), and leaving loop() empty.

dmaxben
Posts: 51
Joined: Thu Nov 16, 2017 6:04 pm

Re: Using the official CAN driver with Arduino

Postby dmaxben » Thu Oct 10, 2019 5:52 pm

ESP_Dazz wrote:
Wed Oct 09, 2019 4:44 pm
@ dmaxben Just a few pointers regarding IDF CAN driver usage

It's probably a good idea to use the member names when initializing a CAN message structure in case the order of the members change in the future versions of the driver. There are also a bunch of message flags documented here to set/check to a certain type (e.g., extended ID, RTR etc).

Code: Select all

can_message_t my_message = {
	.identifier = 0xABCD,
	.data_length_code = 8,
	.flags = CAN_MSG_FLAG_EXTD | CAN_MSG_FLAG_SS,
	.data = {1, 2 , 3 , 4 ,5 ,6 ,7 ,8}
};
The CAN driver was designed to be used in a multi-threaded manner, so general practice is to split receiving and transmitting into separate tasks (threads). This way, the RX queue can be emptied and the TX queue filled simultaneously by the two threads, and you won't need such a large TX and RX queue. Default queue length setting by the driver is 5, a length of 100 seems abnormally long.

You should still be able to directly call FreeRTOS functions and types to create tasks in Arduino. I suggest you take a look at the Self-Test example for a start. You should be able to easily adapt that to Arduino. In theory, it should work by just copying the code, renaming app_main() to setup(), and leaving loop() empty.
I agree on using the actual member names when defining the messsages...however my project has hundreds of CAN messages, and I didnt want to type everything out by hand, making my program hundreds of lines longer haha. :lol:

Good advice on splitting things into separate tasks, thanks!

Ben

Who is online

Users browsing this forum: No registered users and 6 guests