Skip to content

Commit 4ea6242

Browse files
authored
Merge pull request #20 from odriverobotics/esp32-twai-support
Add support for ESP32 boards using the native TWAI (CAN) driver.
2 parents c6d4731 + 366b780 commit 4ea6242

File tree

5 files changed

+124
-3
lines changed

5 files changed

+124
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# [0.10.9]
2+
- Add support for ESP32 TWAI
3+
14
# [0.10.8]
25
- Removed delay while waiting for heartbeat message, causing infinite/nondeterministic delays on some Teensy-based systems with multiple ODrives.
36

examples/SineWaveCAN/SineWaveCAN.ino

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
// #define IS_ARDUINO_BUILTIN // Arduino boards with built-in CAN interface (e.g. Arduino Uno R4 Minima)
2222
// #define IS_MCP2515 // Any board with external MCP2515 based extension module. See below to configure the module.
2323
// #define IS_STM32_BUILTIN // STM32 boards with built-in CAN interface (e.g. STM32F4 Discovery).
24+
// #define IS_ESP32_TWAI // ESP32 boards with built-in TWAI (CAN) interface. Directly uses the ESP-IDF TWAI driver.
2425

2526

2627
/* Board-specific includes ---------------------------------------------------*/
2728

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

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

69+
#ifdef IS_ESP32_TWAI
70+
// See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/twai.html
71+
#include "driver/twai.h"
72+
#include "ODriveESP32TWAI.hpp"
73+
#endif // IS_ESP32_TWAI
74+
6875

6976

7077
/* Board-specific settings ---------------------------------------------------*/
@@ -160,6 +167,53 @@ bool setupCan() {
160167
#endif // IS_STM32_BUILTIN
161168

162169

170+
/* ESP32 boards with built-in TWAI (CAN) */
171+
172+
#ifdef IS_ESP32_TWAI
173+
174+
// Pins used to connect to CAN bus transceiver
175+
#define ESP32_TWAI_TX_PIN 26
176+
#define ESP32_TWAI_RX_PIN 25
177+
178+
ESP32TWAIIntf can_intf;
179+
180+
bool setupCan() {
181+
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(
182+
(gpio_num_t)ESP32_TWAI_TX_PIN,
183+
(gpio_num_t)ESP32_TWAI_RX_PIN,
184+
TWAI_MODE_NORMAL
185+
);
186+
187+
twai_timing_config_t t_config;
188+
switch (CAN_BAUDRATE) {
189+
case 1000000: t_config = TWAI_TIMING_CONFIG_1MBITS(); break;
190+
case 800000: t_config = TWAI_TIMING_CONFIG_800KBITS(); break;
191+
case 500000: t_config = TWAI_TIMING_CONFIG_500KBITS(); break;
192+
case 250000: t_config = TWAI_TIMING_CONFIG_250KBITS(); break;
193+
case 125000: t_config = TWAI_TIMING_CONFIG_125KBITS(); break;
194+
case 100000: t_config = TWAI_TIMING_CONFIG_100KBITS(); break;
195+
case 50000: t_config = TWAI_TIMING_CONFIG_50KBITS(); break;
196+
case 25000: t_config = TWAI_TIMING_CONFIG_25KBITS(); break;
197+
default: t_config = TWAI_TIMING_CONFIG_250KBITS(); break;
198+
}
199+
200+
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
201+
202+
if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) {
203+
return false;
204+
}
205+
206+
if (twai_start() != ESP_OK) {
207+
twai_driver_uninstall();
208+
return false;
209+
}
210+
211+
return true;
212+
}
213+
214+
#endif // IS_ESP32_TWAI
215+
216+
163217
/* Example sketch ------------------------------------------------------------*/
164218

165219
// Instantiate ODrive objects

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# https://arduino.github.io/arduino-cli/0.33/library-specification/
22
name=ODriveArduino
3-
version=0.10.8
3+
version=0.10.9
44
author=ODrive Robotics Inc. <info@odriverobotics.com>
55
maintainer=ODrive Robotics Inc. <info@odriverobotics.com>
66
sentence=Library to control ODrive motor controllers

platformio.ini

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,10 @@ build_flags =
4646
; See https://github.com/pazi88/STM32_CAN
4747
-DHAL_CAN_MODULE_ENABLED
4848
lib_deps =
49-
https://github.com/pazi88/STM32_CAN.git#1.2.0
49+
https://github.com/pazi88/STM32_CAN.git#1.2.0
50+
51+
[env:esp32]
52+
platform = espressif32
53+
board = esp32dev
54+
framework = arduino
55+
build_flags = -DIS_ESP32_TWAI

src/ODriveESP32TWAI.hpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// CAN glue layer for ESP32 platforms using the native TWAI driver.
2+
// See ODriveHardwareCAN.hpp for documentation.
3+
4+
#pragma once
5+
6+
#include "ODriveCAN.h"
7+
#include "driver/twai.h"
8+
9+
// Simple struct to hold TWAI interface state. Unlike other platforms, ESP32's
10+
// TWAI driver uses global functions rather than a class instance.
11+
struct ESP32TWAIIntf {};
12+
13+
struct CanMsg {
14+
uint32_t id;
15+
uint8_t len;
16+
uint8_t buffer[8];
17+
};
18+
19+
// Must be defined by the application
20+
void onCanMessage(const CanMsg& msg);
21+
22+
static bool sendMsg(ESP32TWAIIntf& intf, uint32_t id, uint8_t length, const uint8_t* data) {
23+
(void)intf;
24+
twai_message_t tx_msg = {};
25+
tx_msg.identifier = id;
26+
tx_msg.data_length_code = length;
27+
tx_msg.extd = (id & 0x80000000) ? 1 : 0;
28+
tx_msg.rtr = (data == nullptr) ? 1 : 0;
29+
30+
if (data) {
31+
for (int i = 0; i < length; ++i) {
32+
tx_msg.data[i] = data[i];
33+
}
34+
}
35+
36+
return twai_transmit(&tx_msg, 0) == ESP_OK;
37+
}
38+
39+
static void onReceive(const CanMsg& msg, ODriveCAN& odrive) {
40+
odrive.onReceive(msg.id, msg.len, msg.buffer);
41+
}
42+
43+
static void pumpEvents(ESP32TWAIIntf& intf, int max_events = 100) {
44+
(void)intf;
45+
// max_events prevents an infinite loop if messages come at a high rate
46+
twai_message_t rx_msg;
47+
while (twai_receive(&rx_msg, 0) == ESP_OK && max_events--) {
48+
CanMsg msg;
49+
msg.id = rx_msg.identifier;
50+
msg.len = rx_msg.data_length_code;
51+
for (int i = 0; i < rx_msg.data_length_code && i < 8; ++i) {
52+
msg.buffer[i] = rx_msg.data[i];
53+
}
54+
onCanMessage(msg);
55+
}
56+
}
57+
58+
CREATE_CAN_INTF_WRAPPER(ESP32TWAIIntf)

0 commit comments

Comments
 (0)