ccan-busfreertosarduino-esp32

Esp32 sn65hvd230: I can't get it to read in real time


I am working on a project based on the ESP32 processor and Arduino framework, which communicates with another board via CANBUS protocol.

The bitrate is 125k so it is presumably "fast".

I am using the Sandeep Mistry library or also known as adafruit_CAN which is compatible with a Texas Instruments SN65HVD230 CAN transceiver.

I use freeRTOS for a method that will receive frames per socket, and write them to the board.

Then the library itself provides a callback attached to the ISR to receive the responses.

This is where my problem begins, if I send it a frame per second, everything is wonderful, but when I start to send them every 200ms, I can see (with a can analyzer connected to the bus from the pc) that the frames are arriving, they are receiving on the card and it is answering, but my transceiver does not trigger the callback.

I have tried forgetting about the callback and using another RTOS method for reading, with the same result.

This method is iterating through a task, and if the vector contains a CAN_message_t object it writes it to the CAN, this works correctly.

void vTaskCANBUSWriteLoop(void *p)
{
  Serial.print("Writer Task");
 
  for (;;)
  {
    if (!my_global_vector.empty() )
    {
      response_broker_t myObject = my_global_vector.front();
      my_global_vector.pop();

      
      writePlot(myObject);
     
    }
    vTaskDelay(200 / portTICK_PERIOD_MS);
  }
}

This other method, is according to the documentation, and it works well if the program does not exceed a send per second, if it exceeds it, the program continues writing but the callback is not fired. There is a check if it matches an id, in the parser I see that this id is the one that is going in.

void canCallback(int packetSize)
{
  static uint32_t lastTime = 0;
  if (can_reader.packetId() == BOARD_ANSWER1 || can_reader.packetId() == BOARD_ANSWER2)
  {

    if (packetSize)
    {
      uint8_t buf[8];
      
      while (can_reader.available())
      {
         CAN.readBytes(buf, sizeof(buf));
      }
      uint8_tArrayToHexString2(buf, sizeof(buf), rapidResponse);
     
    }
  }
}

This is a method I use to convert to hexString it's not relevant but it may bring more clarity

void uint8_tArrayToHexString2(uint8_t *data, size_t len, char *output)
{
  static const char hexChars[] = "0123456789ABCDEF";
  for (size_t i = 0; i < len; ++i)
  {
    output[i * 3] = hexChars[(data[i] >> 4) & 0xF];
    output[i * 3 + 1] = hexChars[data[i] & 0xF];
    output[i * 3 + 2] = ' ';
  }
  output[len * 3 - 1] = '\0';
}

Solution

  • Although the Adafruit library is wonderful, the problem lay in its use. When you shoot many messages at the bus very quickly, it ends up becoming unstable.

    I have used the IDF CAN library of the ESP32 and it has nothing to do with it, it is like night and day.

    I leave a minimal implementation for the Arduino framework in case it helps someone.

    #include <Arduino.h>
    #include "driver/gpio.h"
    #include "driver/can.h"
    
    TaskHandle_t can_receive_task_handle;
    
    void can_receive_task(void *arg)
    {
      can_message_t rx_msg;
      for(;;)
      {
        if (can_receive(&rx_msg, pdMS_TO_TICKS(100)) == ESP_OK)
        {
          printf("Received message: ID = 0x%08X, Data: ", rx_msg.identifier);
          for (int i = 0; i < rx_msg.data_length_code; i++)
          {
            printf("%02X ", rx_msg.data[i]);
          }
          printf("\n");
        }
      }
    }
    
    void setup()
    {
    
      Serial.begin(115200);
    
      //? Configure GPIO pins for CAN must be 4/5 or 16/17
      gpio_install_isr_service(0);
      gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT);
      gpio_set_direction(GPIO_NUM_16, GPIO_MODE_INPUT);
    
      //! Configure CAN controller
      can_general_config_t can_config = {
          .mode = CAN_MODE_NORMAL,
          .tx_io = GPIO_NUM_17,
          .rx_io = GPIO_NUM_16,
          .clkout_io = GPIO_NUM_NC,
          .bus_off_io = GPIO_NUM_NC,
          .tx_queue_len = 5,
          .rx_queue_len = 5,
          .alerts_enabled = CAN_ALERT_NONE,
          .clkout_divider = 0};
      can_timing_config_t timing_config = CAN_TIMING_CONFIG_125KBITS();
      can_filter_config_t filter_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
    
      if (can_driver_install(&can_config, &timing_config, &filter_config) == ESP_OK &&
          can_start() == ESP_OK)
      {
        Serial.println("CAN controller initialized successfully");
      }
      else
      {
        Serial.println("Error initializing CAN controller");
        return;
      }
    
      //! Create RTOS task for receiving CAN messages
      xTaskCreate(can_receive_task, "can_receive_task", 2048, NULL, 5, &can_receive_task_handle);
    }
    
    void loop()
    {
     
    }
    

    Special thanks to the ESP32 Arduino forum: Using the official CAN driver.