ESP32-VROOM and CAN for NMEA 2000

Hi All...

I have a project that requires Wifi and CAN (for N2K) and the Wifi has to run a web server. BT/BLE would be nice as well but not required.

From what I have read the ATMega chips don't have enough horsepower to accept and process the entire message stream. The ESP32-VROOM seems like a fine choice, but... I have looked at the datasheet and various modules and I can't figure out which pins CAN H and CAN L are. The datasheet clearly says it has a CAN interface in the peripheral list and block diagram, and then it never seems to mention it again. I assume it shares with some GPIO pins.

Can someone explain?

Thanks...

ESP32 pins are often re-assignable to just about any of the GPIO pins.

Check the libraries \examples folder for some examples of how to assign them

From the Library...

The library defines as default Tx pin to GPIO 16 and Rx pint to GPIO 4. You can change these with defines:

#define ESP32_CAN_TX_PIN GPIO_NUM_16
#define ESP32_CAN_RX_PIN GPIO_NUM_4

The ESP32 does not have H and L.

For that you will need something like a Waveshare SN65HVD230 CAN Board.

You will want to add #include <CAN_config.h> and you might want to use the #include <ESP32CAN.h>. Also, in the declaration section you would want to add:
CAN_device_t CAN_cfg;
You may or may not want to add,
// CAN_frame_t rx_frame;
And these are useful links:

You might want to come to understand the ESP32CAN examples. It sure helped me out.

In setup() you make some setup and configuration choice:

/* set CAN pins and baudrate */
  //// CAN_cfg.speed = CAN_SPEED_100KBPS;
  ////  CAN_cfg.speed = CAN_SPEED_250KBPS;
  //// CAN_cfg.speed = CAN_SPEED_500KBPS;
  ////CAN_cfg.speed = CAN_SPEED_800KBPS;
  CAN_cfg.speed = CAN_SPEED_1000KBPS;
  CAN_cfg.tx_pin_id = GPIO_NUM_12; // green wire
  CAN_cfg.rx_pin_id =  GPIO_NUM_13; // white wire
  CAN_cfg.rx_queue = xQueueCreate(3, sizeof(CAN_frame_t));
  //start CAN Module
  ESP32Can.CANInit();

Oh, forgot you’d want to declare:
#include “esp_system.h” //This inclusion configures the peripherals in the ESP system.
#include “freertos/FreeRTOS.h”
#include “freertos/task.h”
#include “freertos/timers.h”
#include “freertos/event_groups.h”

as you will NEED a Queue. I found a queue size of over 7 to actually slow down data communications.

You will want a task to send/ receive CAN messages:

void fSendCAN_Buss( void *pvParameters )
{
  stuSERVO_Message pxServo_Message;
  for ( ;; )
  {
    xEventGroupWaitBits (eg, evtSendCAN_Buss, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( xSemaphoreTake( sema__Send_CAN_Bus, xTicksToWait0 ) == pdTRUE ) // grab semaphore no wait
    {
      CAN_frame_t rx_frame;
      // Serial.println ( " fSendCAN_Buss 2 "  );
      if ( (CAN_cfg.rx_queue != NULL) && (uxQueueMessagesWaiting(CAN_cfg.rx_queue)) ) // if queue not null and something is waiting in queue
      {
        // Serial.println ( uxQueueMessagesWaiting(CAN_cfg.rx_queue) );
        if (xQueueReceive( CAN_cfg.rx_queue, &rx_frame , xTicksToWait0) == pdTRUE )
        {
          if ( rx_frame.MsgID == 2 )
          {
            // Serial.println ( rx_frame.data.u8[0] );
            if ( rx_frame.data.u8[0] == '6' )
            {
              EOT = false;
            }
            if ( rx_frame.data.u8[0] == '4' )
            {
              EOT = false;
              xSemaphoreGive ( sema_HexaPodAdjustment ); // begin walking forward task
            }
          } // if ( rx_frame.MsgID == 2 )
        } // if (xQueueReceive( CAN_cfg.rx_queue, &rx_frame , xTicksToWait5) == pdTRUE )
      } // if ( (CAN_cfg.rx_queue != NULL) && (uxQueueMessagesWaiting(CAN_cfg.rx_queue)) )
      //      // ack the initialization
      if ( xQueueReceive ( xQ_SERVO_Message, &pxServo_Message, QueueReceiveDelayTime ) == pdTRUE )
      {
        rx_frame.FIR.B.FF = CAN_frame_std;
        rx_frame.MsgID = pxServo_Message.MsgID;
        rx_frame.FIR.B.DLC = pxServo_Message.DLC;
        rx_frame.data.u8[0] = pxServo_Message.Instruction;
        rx_frame.data.u8[1] = pxServo_Message.p1;
        rx_frame.data.u8[2] = pxServo_Message.p2;
        rx_frame.data.u8[3] = pxServo_Message.p3;
        rx_frame.data.u8[4] = pxServo_Message.p4; // end of torque to position
        rx_frame.data.u8[5] = pxServo_Message.p5;
        rx_frame.data.u8[6] = pxServo_Message.p6;
        rx_frame.data.u8[7] = pxServo_Message.p7;
//                Serial.print ( " message in sendCanBus " );
//                Serial.print ( ", " );
//                Serial.print ( rx_frame.data.u8[0] );
//                Serial.print ( ", " );
//                Serial.print ( rx_frame.data.u8[1] );
//                Serial.print ( ", " );
//                Serial.print (rx_frame.data.u8[1] );
//                Serial.print ( ", " );
//                Serial.print ( rx_frame.data.u8[3] );
//                Serial.print ( ", " );
//                Serial.print ( rx_frame.data.u8[4] );
//                Serial.println ( " message end." );
        ESP32Can.CANWriteFrame(&rx_frame);
      }
      //
      xSemaphoreGive ( sema__Send_CAN_Bus );
      ////
      // Serial.print( "fSendCAN_Buss " );
      // Serial.print(uxTaskGetStackHighWaterMark( NULL ));
      // Serial.println();
      // Serial.flush();
    }  // if ( xSemaphoreTake( sema__Send_CAN_Bus, xTicksToWait0 ) == pdTRUE ) // grab semaphore no wait
  } // the for loop
  vTaskDelete( NULL );
} // void fSendCAN_Buss( void *pvParameters )

And on the receiver end, which will also be its own sender, you will need to write parsing code.