Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion examples/SineWaveCAN/SineWaveCAN.ino
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@
// #define IS_ARDUINO_BUILTIN // Arduino boards with built-in CAN interface (e.g. Arduino Uno R4 Minima)
// #define IS_MCP2515 // Any board with external MCP2515 based extension module. See below to configure the module.
// #define IS_STM32_BUILTIN // STM32 boards with built-in CAN interface (e.g. STM32F4 Discovery).
// #define IS_ESP32_TWAI // ESP32 boards with built-in TWAI (CAN) interface. Directly uses the ESP-IDF TWAI driver.


/* Board-specific includes ---------------------------------------------------*/

#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) != 1
#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) + defined(IS_ESP32_TWAI) != 1
#warning "Select exactly one hardware option at the top of this file."

#if CAN_HOWMANY > 0 || CANFD_HOWMANY > 0
Expand Down Expand Up @@ -65,6 +66,12 @@ struct ODriveStatus; // hack to prevent teensy compile error
#include "ODriveSTM32CAN.hpp"
#endif // IS_STM32_BUILTIN

#ifdef IS_ESP32_TWAI
// See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/twai.html
#include "driver/twai.h"
#include "ODriveESP32TWAI.hpp"
#endif // IS_ESP32_TWAI



/* Board-specific settings ---------------------------------------------------*/
Expand Down Expand Up @@ -160,6 +167,53 @@ bool setupCan() {
#endif // IS_STM32_BUILTIN


/* ESP32 boards with built-in TWAI (CAN) */

#ifdef IS_ESP32_TWAI

// Pins used to connect to CAN bus transceiver
#define ESP32_TWAI_TX_PIN 5
#define ESP32_TWAI_RX_PIN 4

ESP32TWAIIntf can_intf;

bool setupCan() {
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(
(gpio_num_t)ESP32_TWAI_TX_PIN,
(gpio_num_t)ESP32_TWAI_RX_PIN,
TWAI_MODE_NORMAL
);

twai_timing_config_t t_config;
switch (CAN_BAUDRATE) {
case 1000000: t_config = TWAI_TIMING_CONFIG_1MBITS(); break;
case 800000: t_config = TWAI_TIMING_CONFIG_800KBITS(); break;
case 500000: t_config = TWAI_TIMING_CONFIG_500KBITS(); break;
case 250000: t_config = TWAI_TIMING_CONFIG_250KBITS(); break;
case 125000: t_config = TWAI_TIMING_CONFIG_125KBITS(); break;
case 100000: t_config = TWAI_TIMING_CONFIG_100KBITS(); break;
case 50000: t_config = TWAI_TIMING_CONFIG_50KBITS(); break;
case 25000: t_config = TWAI_TIMING_CONFIG_25KBITS(); break;
default: t_config = TWAI_TIMING_CONFIG_250KBITS(); break;
}

twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();

if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) {
return false;
}

if (twai_start() != ESP_OK) {
twai_driver_uninstall();
return false;
}

return true;
}

#endif // IS_ESP32_TWAI


/* Example sketch ------------------------------------------------------------*/

// Instantiate ODrive objects
Expand Down
8 changes: 7 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,10 @@ build_flags =
; See https://github.com/pazi88/STM32_CAN
-DHAL_CAN_MODULE_ENABLED
lib_deps =
https://github.com/pazi88/STM32_CAN.git#1.2.0
https://github.com/pazi88/STM32_CAN.git#1.2.0

[env:esp32]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

picked up by CI and compiles, great!

platform = espressif32
board = esp32dev
framework = arduino
build_flags = -DIS_ESP32_TWAI
58 changes: 58 additions & 0 deletions src/ODriveESP32TWAI.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// CAN glue layer for ESP32 platforms using the native TWAI driver.
// See ODriveHardwareCAN.hpp for documentation.

#pragma once

#include "ODriveCAN.h"
#include "driver/twai.h"

// Simple struct to hold TWAI interface state. Unlike other platforms, ESP32's
// TWAI driver uses global functions rather than a class instance.
struct ESP32TWAIIntf {};

struct CanMsg {
uint32_t id;
uint8_t len;
uint8_t buffer[8];
};

// Must be defined by the application
void onCanMessage(const CanMsg& msg);

static bool sendMsg(ESP32TWAIIntf& intf, uint32_t id, uint8_t length, const uint8_t* data) {
(void)intf;
twai_message_t tx_msg = {};
tx_msg.identifier = id;
tx_msg.data_length_code = length;
tx_msg.extd = (id & 0x80000000) ? 1 : 0;
tx_msg.rtr = (data == nullptr) ? 1 : 0;

if (data) {
for (int i = 0; i < length; ++i) {
tx_msg.data[i] = data[i];
}
}

return twai_transmit(&tx_msg, 0) == ESP_OK;
}

static void onReceive(const CanMsg& msg, ODriveCAN& odrive) {
odrive.onReceive(msg.id, msg.len, msg.buffer);
}

static void pumpEvents(ESP32TWAIIntf& intf, int max_events = 100) {
(void)intf;
// max_events prevents an infinite loop if messages come at a high rate
twai_message_t rx_msg;
while (twai_receive(&rx_msg, 0) == ESP_OK && max_events--) {
CanMsg msg;
msg.id = rx_msg.identifier;
msg.len = rx_msg.data_length_code;
for (int i = 0; i < rx_msg.data_length_code && i < 8; ++i) {
msg.buffer[i] = rx_msg.data[i];
}
onCanMessage(msg);
}
}

CREATE_CAN_INTF_WRAPPER(ESP32TWAIIntf)