Skip to content

Commit 47b73f3

Browse files
Adds a CAN driver for arduino-esp32 that is based on the vanilla TWAI APIs (#854)
- Adds a new Arduino driver Esp32Can.hxx, which utilizes the TWAI driver instead of the low-level HAL driver. This should work better across different IDF versions, as the API is simpler and more stable. - The driver is a vanilla implementation of the available(), availableForRead(), read() and write() functions. - Updates all arduino examples, ripping out TWAI references. === * Add Esp32Can driver and update example for testing This commit introduces a new CAN driver implementation for the ESP32, named `Esp32Can`. This driver uses the ESP-IDF TWAI peripheral directly, providing a non-blocking interface that is consistent with other Arduino-style CAN drivers in the project, such as `FeatherM4Can`. The `Esp32Can` class inherits from `openmrn_arduino::Can` and implements the `available()`, `availableForWrite()`, `read()`, and `write()` methods. The driver is configured for a 125 kbit/s bus speed. To facilitate testing, the `CanLoadTest.ino` example under `arduino/examples/ESP32` has been modified to use the new `Esp32Can` driver. This allows for direct testing of the new implementation in a practical application. * Add Esp32Can driver and update example for testing This commit introduces a new CAN driver implementation for the ESP32, named `Esp32Can`. This driver uses the ESP-IDF TWAI peripheral directly, providing a non-blocking interface that is consistent with other Arduino-style CAN drivers in the project, such as `FeatherM4Can`. The `Esp32Can` class inherits from `openmrn_arduino::Can` and implements the `available()`, `availableForWrite()`, `read()`, and `write()` methods. The driver is configured for a 125 kbit/s bus speed. To facilitate testing, the `CanLoadTest.ino` example under `arduino/examples/ESP32` has been modified to use the new `Esp32Can` driver. This allows for direct testing of the new implementation in a practical application. * Fixes some compile errors. * Update includes and adds another guard. * I've refactored the ESP32 IOBoard example to align with the CanLoadTest. This change updates the `arduino/examples/ESP32/IOBoard` example to follow the same patterns and conventions as the `CanLoadTest` example. The key changes are: - Replaced the `Esp32HardwareTwai` driver with the `Esp32Can` driver for CAN bus communication. - Replaced the `USE_TWAI` and `USE_TWAI_ASYNC` macros with a single `USE_CAN` macro. - Updated the configuration in `config.h` to reflect the driver change and incremented the canonical version to ensure configuration is re-evaluated. * Update esp32c3 and esp32s2 examples as well. * Also update WifiCanBridge. * Updates comments. * Update arduino library version. * Fix missing include in mdns that causes compile errors. * Adds recovery. * fix local variable's style. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 39bac7e commit 47b73f3

File tree

20 files changed

+330
-238
lines changed

20 files changed

+330
-238
lines changed

arduino/OpenMRNLite.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,8 @@ constexpr UBaseType_t OPENMRN_TASK_PRIORITY = ESP_TASK_TCPIP_PRIO - 1;
7575
// ESP32-H2 and ESP32-C2 do not have a built-in TWAI controller.
7676
#if !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32C2)
7777

78-
// If we are using ESP-IDF v4.3 (or later) enable the usage of the TWAI device
79-
// which allows usage of the filesystem based CAN interface methods.
80-
#include "freertos_drivers/esp32/Esp32HardwareTwai.hxx"
81-
#define HAVE_CAN_FS_DEVICE
82-
83-
// The ESP-IDF VFS layer has an optional wrapper around the select() interface
84-
// when disabled we can not use select() for the CAN/TWAI driver. Normally this
85-
// is enabled for arduino-esp32.
86-
#if CONFIG_VFS_SUPPORT_SELECT
87-
#define HAVE_CAN_FS_SELECT
88-
#endif
78+
// If we are using ESP-IDF v4.3 (or later) enable the usage of the TWAI device.
79+
#include "freertos_drivers/esp32/Esp32Can.hxx"
8980

9081
#endif // NOT ESP32-H2 and NOT ESP32-C2
9182

arduino/examples/ESP32/CanLoadTest/CanLoadTest.ino

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,18 @@
4646

4747

4848
// Pick an operating mode below, if you select USE_WIFI it will expose this
49-
// node on WIFI. If USE_TWAI / USE_TWAI_ASYNC are enabled the node
50-
// will be available on CAN.
49+
// node on WIFI. If USE_CAN is enabled the node will be available on CAN.
5150
//
5251
// Enabling both options will allow the ESP32 to be accessible from
53-
// both WiFi and TWAI interfaces.
52+
// both WiFi and CAN interfaces.
5453

55-
#define USE_WIFI
56-
//#define USE_TWAI
57-
//#define USE_TWAI_ASYNC
54+
//#define USE_WIFI
55+
#define USE_CAN
5856

5957
// uncomment the line below to have all packets printed to the Serial
6058
// output. This is not recommended for production deployment.
6159
//#define PRINT_PACKETS
6260

63-
// Configuration option validation
64-
65-
// If USE_TWAI_ASYNC is enabled but USE_TWAI is not, enable USE_TWAI.
66-
#if defined(USE_TWAI_ASYNC) && !defined(USE_TWAI)
67-
#define USE_TWAI
68-
#endif // USE_TWAI_ASYNC && !USE_TWAI
69-
70-
#if defined(USE_TWAI) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,3,0)
71-
#error Esp32HardwareTwai is not supported on this version of arduino-esp32.
72-
#endif // USE_TWAI && IDF < v4.3
73-
7461
#include "config.h"
7562

7663
/// This is the node id to assign to this device, this must be unique
@@ -110,7 +97,7 @@ OVERRIDE_CONST(gridconnect_bridge_max_outgoing_packets, 2);
11097

11198
#endif // USE_WIFI
11299

113-
#if defined(USE_TWAI)
100+
#if defined(USE_CAN)
114101
/// This is the ESP32 pin connected to the SN65HVD23x/MCP2551 R (RX) pin.
115102
/// Recommended pins: 4, 16, 21.
116103
/// Note: Any pin can be used for this other than 6-11 which are connected to
@@ -126,8 +113,7 @@ constexpr gpio_num_t CAN_RX_PIN = GPIO_NUM_4;
126113
/// Note: If you are using a pin other than 5 you will likely need to adjust
127114
/// the GPIO pin definitions for the outputs.
128115
constexpr gpio_num_t CAN_TX_PIN = GPIO_NUM_5;
129-
130-
#endif // USE_TWAI
116+
#endif // USE_CAN
131117

132118
/// This is the primary entrypoint for the OpenMRN/LCC stack.
133119
OpenMRN openmrn(NODE_ID);
@@ -146,9 +132,9 @@ static constexpr openlcb::ConfigDef cfg(0);
146132
Esp32WiFiManager wifi_mgr(ssid, password, openmrn.stack(), cfg.seg().wifi());
147133
#endif // USE_WIFI
148134

149-
#if defined(USE_TWAI)
150-
Esp32HardwareTwai twai(CAN_RX_PIN, CAN_TX_PIN);
151-
#endif // USE_TWAI
135+
#if defined(USE_CAN)
136+
Esp32Can can_driver(CAN_TX_PIN, CAN_RX_PIN);
137+
#endif // USE_CAN
152138

153139
class FactoryResetHelper : public DefaultConfigUpdateListener {
154140
public:
@@ -245,10 +231,11 @@ void setup()
245231
openmrn.stack()->create_config_file_if_needed(cfg.seg().internal_config(),
246232
openlcb::CANONICAL_VERSION, openlcb::CONFIG_FILE_SIZE);
247233

248-
#if defined(USE_TWAI)
249-
twai.hw_init();
250-
#endif // USE_TWAI
251-
234+
#if defined(USE_CAN)
235+
can_driver.begin();
236+
openmrn.add_can_port(&can_driver);
237+
#endif // USE_CAN
238+
252239
// Start the OpenMRN stack
253240
openmrn.begin();
254241
openmrn.start_executor_thread();
@@ -260,14 +247,6 @@ void setup()
260247
// have performance impact.
261248
openmrn.stack()->print_all_packets();
262249
#endif // PRINT_PACKETS
263-
264-
#if defined(USE_TWAI_ASYNC)
265-
// add TWAI driver with non-blocking usage
266-
openmrn.add_can_port_async("/dev/twai/twai0");
267-
#elif defined(USE_TWAI)
268-
// add TWAI driver with select() usage
269-
openmrn.add_can_port_select("/dev/twai/twai0");
270-
#endif // USE_TWAI_ASYNC / USE_TWAI
271250
}
272251

273252
void loop()

arduino/examples/ESP32/CanLoadTest/config.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
#include "freertos_drivers/esp32/Esp32WiFiConfiguration.hxx"
1010

1111
// catch invalid configuration at compile time
12-
#if !defined(USE_TWAI) && !defined(USE_WIFI)
13-
#error "Invalid configuration detected, USE_TWAI or USE_WIFI must be defined."
12+
#if !defined(USE_CAN) && !defined(USE_WIFI)
13+
#error "Invalid configuration detected, USE_CAN or USE_WIFI must be defined."
1414
#endif
1515

1616
namespace openlcb
@@ -32,11 +32,11 @@ namespace openlcb
3232
extern const SimpleNodeStaticValues SNIP_STATIC_DATA = {
3333
4,
3434
"OpenMRN",
35-
#if defined(USE_WIFI) && !defined(USE_TWAI)
35+
#if defined(USE_WIFI) && !defined(USE_CAN)
3636
"Arduino IO Board (WiFi)",
37-
#elif defined(USE_TWAI) && !defined(USE_WIFI)
37+
#elif defined(USE_CAN) && !defined(USE_WIFI)
3838
"Arduino IO Board (CAN)",
39-
#elif defined(USE_TWAI) && defined(USE_WIFI)
39+
#elif defined(USE_CAN) && defined(USE_WIFI)
4040
"Arduino IO Board (WiFi/CAN)",
4141
#else
4242
"Arduino IO Board",

arduino/examples/ESP32/IOBoard/IOBoard.ino

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,13 @@
4242
#include "utils/GpioInitializer.hxx"
4343

4444
// Pick an operating mode below, if you select USE_WIFI it will expose this
45-
// node on WIFI. If USE_TWAI / USE_TWAI_ASYNC are enabled the node
46-
// will be available on CAN.
45+
// node on WIFI. If USE_CAN is enabled the node will be available on CAN.
4746
//
4847
// Enabling both options will allow the ESP32 to be accessible from
4948
// both WiFi and CAN interfaces.
5049

5150
#define USE_WIFI
52-
#define USE_TWAI
53-
//#define USE_TWAI_ASYNC
51+
#define USE_CAN
5452

5553
// uncomment the line below to have all packets printed to the Serial
5654
// output. This is not recommended for production deployment.
@@ -63,7 +61,7 @@
6361
//#define FACTORY_RESET_GPIO_PIN 22
6462

6563
// Uncomment FIRMWARE_UPDATE_BOOTLOADER to enable the bootloader feature when
66-
// using the TWAI device.
64+
// using the CAN device.
6765
//
6866
// Since many ESP32 DevKit boards do not have an on-board LED, there are no
6967
// LED indicators enabled by default. If indicator LEDs are desired they can be
@@ -74,12 +72,8 @@
7472
// the Arduino IDE.
7573
//#define FIRMWARE_UPDATE_BOOTLOADER
7674

77-
#if defined(USE_TWAI_ASYNC) && !defined(USE_TWAI)
78-
#define USE_TWAI
79-
#endif // USE_TWAI_ASYNC && !USE_TWAI
80-
81-
#if defined(FIRMWARE_UPDATE_BOOTLOADER) && !defined(USE_TWAI)
82-
#error FIRMWARE_UPDATE_BOOTLOADER requires USE_TWAI or USE_TWAI_SELECT.
75+
#if defined(FIRMWARE_UPDATE_BOOTLOADER) && !defined(USE_CAN)
76+
#error FIRMWARE_UPDATE_BOOTLOADER requires USE_CAN.
8377
#endif
8478

8579
#include "config.h"
@@ -118,7 +112,7 @@ const char *hostname = "esp32mrn";
118112

119113
#endif // USE_WIFI
120114

121-
#if defined(USE_TWAI)
115+
#if defined(USE_CAN)
122116
/// This is the ESP32 pin connected to the SN65HVD23x/MCP2551 R (RX) pin.
123117
/// Recommended pins: 4, 16, 21.
124118
/// Note: Any pin can be used for this other than 6-11 which are connected to
@@ -135,7 +129,7 @@ constexpr gpio_num_t CAN_RX_PIN = GPIO_NUM_4;
135129
/// the GPIO pin definitions for the outputs.
136130
constexpr gpio_num_t CAN_TX_PIN = GPIO_NUM_5;
137131

138-
#endif // USE_TWAI
132+
#endif // USE_CAN
139133

140134
#if defined(FIRMWARE_UPDATE_BOOTLOADER)
141135
// Include the Bootloader HAL implementation for the ESP32. This should only
@@ -160,9 +154,9 @@ static constexpr openlcb::ConfigDef cfg(0);
160154
Esp32WiFiManager wifi_mgr(ssid, password, openmrn.stack(), cfg.seg().wifi());
161155
#endif // USE_WIFI
162156

163-
#if defined(USE_TWAI)
164-
Esp32HardwareTwai twai(CAN_RX_PIN, CAN_TX_PIN);
165-
#endif // USE_TWAI
157+
#if defined(USE_CAN)
158+
Esp32Can can_driver(CAN_TX_PIN, CAN_RX_PIN);
159+
#endif // USE_CAN
166160

167161
// Declare output pins
168162
// NOTE: pins 6-11 are connected to the onboard flash and can not be used for
@@ -327,6 +321,9 @@ void check_for_factory_reset()
327321

328322
void setup()
329323
{
324+
#ifdef USE_WIFI
325+
//wifi_mgr.enable_verbose_logging();
326+
#endif
330327
Serial.begin(115200L);
331328
uint8_t reset_reason = Esp32SocInfo::print_soc_info();
332329
LOG(INFO, "[Node] ID: %s", uint64_to_string_hex(NODE_ID).c_str());
@@ -363,17 +360,13 @@ void setup()
363360
// before we startup the OpenMRN stack.
364361
if (request_bootloader())
365362
{
366-
esp32_bootloader_run(NODE_ID, TWAI_RX_PIN, TWAI_TX_PIN);
363+
esp32_bootloader_run(NODE_ID, CAN_RX_PIN, CAN_TX_PIN);
367364
// This line should not be reached as the esp32_bootloader_run method
368365
// will not return by default.
369366
HASSERT(false);
370367
}
371368
#endif // FIRMWARE_UPDATE_BOOTLOADER
372369

373-
#if defined(USE_TWAI)
374-
twai.hw_init();
375-
#endif // USE_TWAI
376-
377370
check_for_factory_reset();
378371

379372
// Create the CDI.xml dynamically
@@ -383,6 +376,11 @@ void setup()
383376
openmrn.stack()->create_config_file_if_needed(cfg.seg().internal_config(),
384377
openlcb::CANONICAL_VERSION, openlcb::CONFIG_FILE_SIZE);
385378

379+
#if defined(USE_CAN)
380+
can_driver.begin();
381+
openmrn.add_can_port(&can_driver);
382+
#endif // USE_CAN
383+
386384
// Start the OpenMRN stack
387385
openmrn.begin();
388386
if (reset_reason == RTCWDT_BROWN_OUT_RESET)
@@ -399,12 +397,6 @@ void setup()
399397
openmrn.stack()->print_all_packets();
400398
#endif // PRINT_PACKETS
401399

402-
#if defined(USE_TWAI_ASYNC)
403-
openmrn.add_can_port_async("/dev/twai/twai0");
404-
#elif defined(USE_TWAI)
405-
openmrn.add_can_port_select("/dev/twai/twai0");
406-
#endif // USE_TWAI_ASYNC
407-
408400
openmrn.start_executor_thread();
409401
}
410402

arduino/examples/ESP32/IOBoard/config.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
#include "freertos_drivers/esp32/Esp32WiFiConfiguration.hxx"
1010

1111
// catch invalid configuration at compile time
12-
#if !defined(USE_TWAI) && !defined(USE_WIFI)
13-
#error "Invalid configuration detected, USE_TWAI or USE_WIFI must be defined."
12+
#if !defined(USE_CAN) && !defined(USE_WIFI)
13+
#error "Invalid configuration detected, USE_CAN or USE_WIFI must be defined."
1414
#endif
1515

1616
namespace openlcb
@@ -32,12 +32,12 @@ namespace openlcb
3232
extern const SimpleNodeStaticValues SNIP_STATIC_DATA = {
3333
4,
3434
"OpenMRN",
35-
#if defined(USE_WIFI) && !defined(USE_TWAI)
35+
#if defined(USE_WIFI) && !defined(USE_CAN)
3636
"Arduino IO Board (WiFi)",
37-
#elif defined(USE_TWAI) && !defined(USE_WIFI)
38-
"Arduino IO Board (TWAI)",
39-
#elif defined(USE_TWAI) && defined(USE_WIFI)
40-
"Arduino IO Board (WiFi/TWAI)",
37+
#elif defined(USE_CAN) && !defined(USE_WIFI)
38+
"Arduino IO Board (CAN)",
39+
#elif defined(USE_CAN) && defined(USE_WIFI)
40+
"Arduino IO Board (WiFi/CAN)",
4141
#else
4242
"Arduino IO Board",
4343
#endif
@@ -56,7 +56,7 @@ using AllProducers = RepeatedGroup<ProducerConfig, NUM_INPUTS>;
5656

5757
/// Modify this value every time the EEPROM needs to be cleared on the node
5858
/// after an update.
59-
static constexpr uint16_t CANONICAL_VERSION = 0x100a;
59+
static constexpr uint16_t CANONICAL_VERSION = 0x100b;
6060

6161
/// Defines the main segment in the configuration CDI. This is laid out at
6262
/// origin 128 to give space for the ACDI user data at the beginning.

arduino/examples/ESP32/WifiCanBridge/WifiCanBridge.ino

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ static constexpr openlcb::ConfigDef cfg(0);
106106

107107
Esp32WiFiManager wifi_mgr(ssid, password, openmrn.stack(), cfg.seg().wifi());
108108

109-
// @ref Esp32HardwareTwai instance to use for this node.
110-
static Esp32HardwareTwai twai(CAN_RX_PIN, CAN_TX_PIN);
109+
// @ref Esp32Can instance to use for this node.
110+
static Esp32Can can_driver(CAN_TX_PIN, CAN_RX_PIN);
111111

112112
class FactoryResetHelper : public DefaultConfigUpdateListener
113113
{
@@ -172,10 +172,10 @@ void setup()
172172
openmrn.stack()->create_config_file_if_needed(cfg.seg().internal_config(),
173173
openlcb::CANONICAL_VERSION, openlcb::CONFIG_FILE_SIZE);
174174

175-
// Initialize the TWAI peripheral
176-
twai.hw_init();
177-
// Add TWAI port to the stack
178-
openmrn.add_can_port_select("/dev/twai/twai0");
175+
// Initialize the CAN peripheral
176+
can_driver.begin();
177+
// Add CAN port to the stack
178+
openmrn.add_can_port(&can_driver);
179179

180180
// Start the OpenMRN stack
181181
openmrn.begin();

arduino/examples/ESP32/WifiCanBridge/config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ extern const SimpleNodeStaticValues SNIP_STATIC_DATA = {
3333

3434
/// Modify this value every time the EEPROM needs to be cleared on the node
3535
/// after an update.
36-
static constexpr uint16_t CANONICAL_VERSION = 0x1008;
36+
static constexpr uint16_t CANONICAL_VERSION = 0x1009;
3737

3838
/// Defines the main segment in the configuration CDI. This is laid out at
3939
/// origin 128 to give space for the ACDI user data at the beginning.

0 commit comments

Comments
 (0)