From 32d90928502a54ced2dd2b0bbb2205d0e43de3b2 Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Fri, 7 Feb 2025 18:40:13 -0500 Subject: [PATCH 01/10] Add files via upload --- examples/SineWaveCAN/SineWaveCAN.ino | 250 +++++++++++++++------------ 1 file changed, 137 insertions(+), 113 deletions(-) diff --git a/examples/SineWaveCAN/SineWaveCAN.ino b/examples/SineWaveCAN/SineWaveCAN.ino index 1cfbdc2..eb1dab2 100644 --- a/examples/SineWaveCAN/SineWaveCAN.ino +++ b/examples/SineWaveCAN/SineWaveCAN.ino @@ -1,141 +1,165 @@ - -#include #include "ODriveCAN.h" - // Documentation for this example can be found here: // https://docs.odriverobotics.com/v/latest/guides/arduino-can-guide.html - /* Configuration of example sketch -------------------------------------------*/ -// CAN bus baudrate. Make sure this matches for every device on the bus -#define CAN_BAUDRATE 250000 - -// ODrive node_id for odrv0 -#define ODRV0_NODE_ID 0 - -// Uncomment below the line that corresponds to your hardware. -// See also "Board-specific settings" to adapt the details for your hardware setup. - -// #define IS_TEENSY_BUILTIN // Teensy boards with built-in CAN interface (e.g. Teensy 4.1). See below to select which interface to use. -// #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. - - -/* Board-specific includes ---------------------------------------------------*/ - -#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) != 1 -#warning "Select exactly one hardware option at the top of this file." - -#if CAN_HOWMANY > 0 || CANFD_HOWMANY > 0 -#define IS_ARDUINO_BUILTIN -#warning "guessing that this uses HardwareCAN" -#else -#error "cannot guess hardware version" -#endif - -#endif + // CAN bus baudrate. Make sure this matches for every device on the bus + #define CAN_BAUDRATE 250000 -#ifdef IS_ARDUINO_BUILTIN -// See https://github.com/arduino/ArduinoCore-API/blob/master/api/HardwareCAN.h -// and https://github.com/arduino/ArduinoCore-renesas/tree/main/libraries/Arduino_CAN + // ODrive node_id for odrv0 + #define ODRV0_NODE_ID 31 -#include -#include -#endif // IS_ARDUINO_BUILTIN -#ifdef IS_MCP2515 -// See https://github.com/sandeepmistry/arduino-CAN/ -#include "MCP2515.h" -#include "ODriveMCPCAN.hpp" -#endif // IS_MCP2515 + // Uncomment below the line that corresponds to your hardware. + // See also "Board-specific settings" to adapt the details for your hardware setup. -#ifdef IS_TEENSY_BUILTIN -// See https://github.com/tonton81/FlexCAN_T4 -// clone https://github.com/tonton81/FlexCAN_T4.git into /src -#include -#include "ODriveFlexCAN.hpp" -struct ODriveStatus; // hack to prevent teensy compile error -#endif // IS_TEENSY_BUILTIN + // #define IS_TEENSY_BUILTIN // Teensy boards with built-in CAN interface (e.g. Teensy 4.1). See below to select which interface to use. + // #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_ESP32_TWAI // Any board with external MCP2515 based extension module. See below to configure the module. +/* Board-specific includes ---------------------------------------------------*/ + #if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_ESP32_TWAI) != 1 + #warning "Select exactly one hardware option at the top of this file." + + #if CAN_HOWMANY > 0 || CANFD_HOWMANY > 0 + #define IS_ARDUINO_BUILTIN + #warning "guessing that this uses HardwareCAN" + #else + #error "cannot guess hardware version" + #endif + + #endif + + #ifdef IS_ARDUINO_BUILTIN + // See https://github.com/arduino/ArduinoCore-API/blob/master/api/HardwareCAN.h + // and https://github.com/arduino/ArduinoCore-renesas/tree/main/libraries/Arduino_CAN + + #include + #include + #endif // IS_ARDUINO_BUILTIN + + #ifdef IS_MCP2515 + // See https://github.com/sandeepmistry/arduino-CAN/ + #include "MCP2515.h" + #include "ODriveMCPCAN.hpp" + #endif // IS_MCP2515 + + #ifdef IS_TEENSY_BUILTIN + // See https://github.com/tonton81/FlexCAN_T4 + // clone https://github.com/tonton81/FlexCAN_T4.git into /src + #include + #include "ODriveFlexCAN.hpp" + struct ODriveStatus; // hack to prevent teensy compile error + #endif // IS_TEENSY_BUILTIN + + #ifdef IS_ESP32_TWAI + // See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/twai.html + // https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/TWAI + + #define RX_PIN 35 + #define TX_PIN 36 + #define TRANSMIT_RATE_MS 50 + #define POLLING_RATE_MS 50 + #include "driver/twai.h" + //probably not the best place to put this, but it is needed to be defined before calling ODriveESP32TWAI.hpp *******************move to ODriveESP32.hpp? + // Interval: + + #include "ODriveESP32TWAI.hpp" + //****vérifier si requis pour ESP32****// struct ODriveStatus; // hack to prevent teensy compile error + #endif // IS_ESP32_TWAI /* Board-specific settings ---------------------------------------------------*/ + /* Teensy */ + #ifdef IS_TEENSY_BUILTIN + + FlexCAN_T4 can_intf; + + bool setupCan() { + can_intf.begin(); + can_intf.setBaudRate(CAN_BAUDRATE); + can_intf.setMaxMB(16); + can_intf.enableFIFO(); + can_intf.enableFIFOInterrupt(); + can_intf.onReceive(onCanMessage); + return true; + } + #endif // IS_TEENSY_BUILTIN -/* Teensy */ - -#ifdef IS_TEENSY_BUILTIN + /* MCP2515-based extension modules -*/ + #ifdef IS_MCP2515 -FlexCAN_T4 can_intf; + MCP2515Class& can_intf = CAN; -bool setupCan() { - can_intf.begin(); - can_intf.setBaudRate(CAN_BAUDRATE); - can_intf.setMaxMB(16); - can_intf.enableFIFO(); - can_intf.enableFIFOInterrupt(); - can_intf.onReceive(onCanMessage); - return true; -} + // chip select pin used for the MCP2515 + #define MCP2515_CS 10 -#endif // IS_TEENSY_BUILTIN + // interrupt pin used for the MCP2515 + // NOTE: not all Arduino pins are interruptable, check the documentation for your board! + #define MCP2515_INT 2 + // freqeuncy of the crystal oscillator on the MCP2515 breakout board. + // common values are: 16 MHz, 12 MHz, 8 MHz + #define MCP2515_CLK_HZ 8000000 -/* MCP2515-based extension modules -*/ -#ifdef IS_MCP2515 + static inline void receiveCallback(int packet_size) { + if (packet_size > 8) { + return; // not supported + } + CanMsg msg = {.id = (unsigned int)CAN.packetId(), .len = (uint8_t)packet_size}; + CAN.readBytes(msg.buffer, packet_size); + onCanMessage(msg); + } -MCP2515Class& can_intf = CAN; + bool setupCan() { + // configure and initialize the CAN bus interface + CAN.setPins(MCP2515_CS, MCP2515_INT); + CAN.setClockFrequency(MCP2515_CLK_HZ); + if (!CAN.begin(CAN_BAUDRATE)) { + return false; + } -// chip select pin used for the MCP2515 -#define MCP2515_CS 10 + CAN.onReceive(receiveCallback); + return true; + } -// interrupt pin used for the MCP2515 -// NOTE: not all Arduino pins are interruptable, check the documentation for your board! -#define MCP2515_INT 2 + #endif // IS_MCP2515 -// freqeuncy of the crystal oscillator on the MCP2515 breakout board. -// common values are: 16 MHz, 12 MHz, 8 MHz -#define MCP2515_CLK_HZ 8000000 + /* ESP32 board using native TWAI driver */ + #ifdef IS_ESP32_TWAI -static inline void receiveCallback(int packet_size) { - if (packet_size > 8) { - return; // not supported - } - CanMsg msg = {.id = (unsigned int)CAN.packetId(), .len = (uint8_t)packet_size}; - CAN.readBytes(msg.buffer, packet_size); - onCanMessage(msg); -} -bool setupCan() { - // configure and initialize the CAN bus interface - CAN.setPins(MCP2515_CS, MCP2515_INT); - CAN.setClockFrequency(MCP2515_CLK_HZ); - if (!CAN.begin(CAN_BAUDRATE)) { - return false; - } + //TWAIClass& can_intf = CAN; //**** not sure if i can actually comment this out. // + TWAIClass can_intf; - CAN.onReceive(receiveCallback); - return true; -} + // Pins used to connect to CAN bus transceiver: -#endif // IS_MCP2515 + bool setupCan() { + if (!can_intf.begin(CAN_BAUDRATE)) { + return false; + } + return true; + } -/* Arduinos with built-in CAN */ + #endif // IS_ESP32_TWAI -#ifdef IS_ARDUINO_BUILTIN + /* Arduinos with built-in CAN */ + #ifdef IS_ARDUINO_BUILTIN -HardwareCAN& can_intf = CAN; + HardwareCAN& can_intf = CAN; -bool setupCan() { - return can_intf.begin((CanBitRate)CAN_BAUDRATE); -} + bool setupCan() { + return can_intf.begin((CanBitRate)CAN_BAUDRATE); + } -#endif + #endif /* Example sketch ------------------------------------------------------------*/ @@ -175,16 +199,17 @@ void onCanMessage(const CanMsg& msg) { } } + + void setup() { Serial.begin(115200); - - // Wait for up to 3 seconds for the serial port to be opened on the PC side. - // If no PC connects, continue anyway. - for (int i = 0; i < 30 && !Serial; ++i) { - delay(100); + long current_millis = 0; + while(!Serial){ + if (millis() > current_millis + 3000){ //check for connection for 3 seconds + break; //Break when connection found + } } - delay(200); - + Serial.println("Serial ok"); Serial.println("Starting ODriveCAN demo"); @@ -202,7 +227,7 @@ void setup() { Serial.println("Waiting for ODrive..."); while (!odrv0_user_data.received_heartbeat) { pumpEvents(can_intf); - delay(100); + delay(50); } Serial.println("found ODrive"); @@ -222,9 +247,11 @@ void setup() { Serial.println("Enabling closed loop control..."); while (odrv0_user_data.last_heartbeat.Axis_State != ODriveAxisState::AXIS_STATE_CLOSED_LOOP_CONTROL) { + odrv0.clearErrors(); delay(1); - odrv0.setState(ODriveAxisState::AXIS_STATE_CLOSED_LOOP_CONTROL); + + odrv0.setState(ODriveAxisState::AXIS_STATE_CLOSED_LOOP_CONTROL); //test commande manuelle // Pump events for 150ms. This delay is needed for two reasons; // 1. If there is an error condition, such as missing DC power, the ODrive might @@ -240,14 +267,11 @@ void setup() { } Serial.println("ODrive running!"); + } void loop() { pumpEvents(can_intf); // This is required on some platforms to handle incoming feedback CAN messages - // Note that on MCP2515-based platforms, this will delay for a fixed 10ms. - // - // This has been found to reduce the number of dropped messages, however it can be removed - // for applications requiring loop times over 100Hz. float SINE_PERIOD = 2.0f; // Period of the position command sine wave in seconds From d98eea3bc3bc233dae5757aa849d2c474c28d84f Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Fri, 7 Feb 2025 18:41:55 -0500 Subject: [PATCH 02/10] Add files via upload created two new files to go with the OdriveArduino library. TWAI_CAN_Class.h and ODriveESP32TWAI.hpp. They enable the use of the native TWAI drivers of the Arduino core for the ESP32. It enables the use of a simple transceiver like an SN65HVD230 instead of requiring an SPI interface. Also the sine wave sketch is updated to add the corresponding board-specific include and setting sections. --- src/ODriveESP32TWAI.hpp | 48 ++++++++++++++ src/TWAI_CAN_Class.h | 134 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 src/ODriveESP32TWAI.hpp create mode 100644 src/TWAI_CAN_Class.h diff --git a/src/ODriveESP32TWAI.hpp b/src/ODriveESP32TWAI.hpp new file mode 100644 index 0000000..9408937 --- /dev/null +++ b/src/ODriveESP32TWAI.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "TWAI_CAN_Class.h" // Include your custom TWAIClass header +#include "ODriveCAN.h" + +// This is a convenience struct because the TWAIClass doesn't have a +// native message type. +struct CanMsg { + uint32_t id; + uint8_t len; + uint8_t buffer[8]; +}; + +// Must be defined by the application if you want to use defaultCanReceiveCallback(). +void onCanMessage(const CanMsg& msg); + + +static bool sendMsg(TWAIClass& can_intf, uint32_t id, uint8_t length, const uint8_t* data) { + // Send CAN message + can_intf.prepareMessage(id, length, !data); + if (data) { + for (int i = 0; i < length; ++i) { + can_intf.write(data[i], i); + } + } + return can_intf.endPacket(); +} + +static void onReceive(const CanMsg& msg, ODriveCAN& odrive) { + odrive.onReceive(msg.id, msg.len, msg.buffer); +} + +static void pumpEvents(TWAIClass& intf) { + CanMsg msg; + int length = intf.parsePacket(); // Check if a packet is available + + if (length > 0) { + msg.id = intf.packetId(); // Retrieve the ID of the received message + // Debug print to check if ID is read correctly + //Serial.print("Received Raw Frame ID: 0x"); // DEBUG PRINT uncomment to print all messages to serial console ********************************** + //Serial.println(msg.id, HEX); // DEBUG PRINT uncomment to print all messages to serial console ********************************** + msg.len = length; + intf.readBytes(msg.buffer, length); // Retrieve the data from the message + onCanMessage(msg); // Call the user-defined callback + } +} + +CREATE_CAN_INTF_WRAPPER(TWAIClass) \ No newline at end of file diff --git a/src/TWAI_CAN_Class.h b/src/TWAI_CAN_Class.h new file mode 100644 index 0000000..b5773ab --- /dev/null +++ b/src/TWAI_CAN_Class.h @@ -0,0 +1,134 @@ +#ifndef TWAI_CAN_CLASS_H +#define TWAI_CAN_CLASS_H + +//#include "driver/twai.h" + +class TWAIClass { +public: + TWAIClass() : is_initialized(false) {} + ~TWAIClass() { + if (is_initialized) { + end(); + } + } + +bool begin(long baudRate) { + if (is_initialized) return false; // Already initialized + + // Configure TWAI (CAN) settings + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)TX_PIN, (gpio_num_t)RX_PIN, TWAI_MODE_NORMAL); + //TODO ADD SWITCH CASE FOR DIFFERENT BAUDRATE**************************************************************************************************************** + twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); // Ensure it matches ODrive + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + + // Install the TWAI driver + if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { + Serial.println("❌ TWAI Driver install failed!"); + return false; + } + + // Start TWAI driver + if (twai_start() != ESP_OK) { + Serial.println("❌ TWAI Driver start failed!"); + twai_driver_uninstall(); + return false; + } + + // Configure TWAI Alerts (Monitor TX and Bus Errors) + uint32_t alerts_to_enable = TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | + TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_ERROR; + if (twai_reconfigure_alerts(alerts_to_enable, NULL) != ESP_OK) { + Serial.println("❌ Failed to configure CAN Alerts!"); + return false; + } + + Serial.println("✅ TWAI CAN Bus Started Successfully!"); + is_initialized = true; + return true; +} + + // End CAN communication + void end() { + if (is_initialized) { + twai_stop(); + twai_driver_uninstall(); + is_initialized = false; + } + } + + // Send a CAN message + int endPacket() { + if (!is_initialized || !message_ready) { + return 0; // Not initialized or no message ready + } + + if (twai_transmit(&tx_message, pdMS_TO_TICKS(100)) == ESP_OK) { + message_ready = false; + return 1; // Successfully sent message + } + + return 0; // Failed to send message + } + + // Prepare a CAN messagewrite + void prepareMessage(uint32_t id, uint8_t length, bool rtr = false) { + tx_message.identifier = id; + tx_message.data_length_code = length; + //tx_message.rtr = rtr ? TWAI_RTR : TWAI_NO_RTR; //TODO this line doesnt compile. verify if required for odrive******************************************* + + tx_message.rtr = 0; + tx_message.extd = (id > 0x7FF); // Extended frame if ID > 11 bits + message_ready = true; + } + + // Write data to the CAN message + void write(uint8_t byte, int index) { + if (index >= 0 && index < 8) { + tx_message.data[index] = byte; + } + } + + // Parse incoming CAN packet + int parsePacket() { + if (!is_initialized) { + return 0; // Not initialized + } + + if (twai_receive(&rx_message, pdMS_TO_TICKS(10)) == ESP_OK) { + return rx_message.data_length_code; // Number of bytes received + } + + return 0; // No packet received + } + + // Access packet ID + uint32_t packetId() { + return rx_message.identifier; + } + + // Access received data + void readBytes(uint8_t* buffer, uint8_t length) { + if (length > 8) length = 8; // Maximum data length + for (uint8_t i = 0; i < length; ++i) { + buffer[i] = rx_message.data[i]; + //Serial.printf(" %d = %02x,", i, rx_message.data[i]); // DEBUG PRINT uncomment to print all messages to serial console ********************************** + } + //Serial.println(""); //uncomment to print all messages to serial console ********************************** + } + + // Callback for received messages + void onReceive(void (*callback)(int)) { + receive_callback = callback; + } + +private: + bool is_initialized; + bool message_ready = false; + twai_message_t tx_message; + twai_message_t rx_message; + void (*receive_callback)(int) = nullptr; +}; + +extern TWAIClass CAN; + +#endif // TWAI_CAN_CLASS_H From 5b93388d997dbf0c81402244b8d78b1dd0190b1c Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Fri, 7 Feb 2025 18:48:35 -0500 Subject: [PATCH 03/10] Add files via upload --- examples/SineWaveCAN/SineWaveCAN.ino | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/SineWaveCAN/SineWaveCAN.ino b/examples/SineWaveCAN/SineWaveCAN.ino index eb1dab2..950a716 100644 --- a/examples/SineWaveCAN/SineWaveCAN.ino +++ b/examples/SineWaveCAN/SineWaveCAN.ino @@ -227,7 +227,7 @@ void setup() { Serial.println("Waiting for ODrive..."); while (!odrv0_user_data.received_heartbeat) { pumpEvents(can_intf); - delay(50); + delay(100); } Serial.println("found ODrive"); @@ -251,7 +251,7 @@ void setup() { odrv0.clearErrors(); delay(1); - odrv0.setState(ODriveAxisState::AXIS_STATE_CLOSED_LOOP_CONTROL); //test commande manuelle + odrv0.setState(ODriveAxisState::AXIS_STATE_CLOSED_LOOP_CONTROL); // Pump events for 150ms. This delay is needed for two reasons; // 1. If there is an error condition, such as missing DC power, the ODrive might @@ -272,7 +272,11 @@ void setup() { void loop() { pumpEvents(can_intf); // This is required on some platforms to handle incoming feedback CAN messages - + // Note that on MCP2515-based platforms, this will delay for a fixed 10ms. + // + // This has been found to reduce the number of dropped messages, however it can be removed + // for applications requiring loop times over 100Hz. + float SINE_PERIOD = 2.0f; // Period of the position command sine wave in seconds float t = 0.001 * millis(); From 8a92e328a01a743e743f88fd68aa850acfee935e Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 09:51:42 -0500 Subject: [PATCH 04/10] Update SineWaveCAN.ino As per suggestions in pull-request comments --- examples/SineWaveCAN/SineWaveCAN.ino | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/examples/SineWaveCAN/SineWaveCAN.ino b/examples/SineWaveCAN/SineWaveCAN.ino index 950a716..04f6e75 100644 --- a/examples/SineWaveCAN/SineWaveCAN.ino +++ b/examples/SineWaveCAN/SineWaveCAN.ino @@ -59,17 +59,15 @@ #ifdef IS_ESP32_TWAI // See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/twai.html // https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/TWAI - + // Pins used to connect to CAN bus transceiver: #define RX_PIN 35 #define TX_PIN 36 #define TRANSMIT_RATE_MS 50 #define POLLING_RATE_MS 50 #include "driver/twai.h" - //probably not the best place to put this, but it is needed to be defined before calling ODriveESP32TWAI.hpp *******************move to ODriveESP32.hpp? - // Interval: + #include "ODriveESP32TWAI.hpp" - //****vérifier si requis pour ESP32****// struct ODriveStatus; // hack to prevent teensy compile error #endif // IS_ESP32_TWAI /* Board-specific settings ---------------------------------------------------*/ @@ -133,18 +131,12 @@ /* ESP32 board using native TWAI driver */ #ifdef IS_ESP32_TWAI - - - //TWAIClass& can_intf = CAN; //**** not sure if i can actually comment this out. // TWAIClass can_intf; - // Pins used to connect to CAN bus transceiver: - - bool setupCan() { - if (!can_intf.begin(CAN_BAUDRATE)) { - return false; - } + if (!can_intf.begin(CAN_BAUDRATE)) { + return false; + } return true; } @@ -205,7 +197,7 @@ void setup() { Serial.begin(115200); long current_millis = 0; while(!Serial){ - if (millis() > current_millis + 3000){ //check for connection for 3 seconds + if ((millis() - current_millis) > 3000){ //check for connection for 3 seconds break; //Break when connection found } } From 46ecde3da3dada09e4fc86d3cb41fb44c4ce4e30 Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 10:05:34 -0500 Subject: [PATCH 05/10] Update SineWaveCAN.ino Updated as per suggestions from pull request review --- examples/SineWaveCAN/SineWaveCAN.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/SineWaveCAN/SineWaveCAN.ino b/examples/SineWaveCAN/SineWaveCAN.ino index 04f6e75..d001edb 100644 --- a/examples/SineWaveCAN/SineWaveCAN.ino +++ b/examples/SineWaveCAN/SineWaveCAN.ino @@ -17,7 +17,7 @@ // #define IS_TEENSY_BUILTIN // Teensy boards with built-in CAN interface (e.g. Teensy 4.1). See below to select which interface to use. // #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_ESP32_TWAI // Any board with external MCP2515 based extension module. See below to configure the module. + // #define IS_ESP32_TWAI // ESP32 boards using a external transceiver such as sn65hvd230. Uses Espressif's native TWAI driver. Connect and TX_PIN from ESP32 to D on transceiver and RX_PIN from ESP32 to R on transceiver. /* Board-specific includes ---------------------------------------------------*/ From 53d97d23b2355f0a73eac1f8dcff76e3f995334d Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 10:15:29 -0500 Subject: [PATCH 06/10] Update ODriveESP32TWAI.hpp as per suggestions --- src/ODriveESP32TWAI.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ODriveESP32TWAI.hpp b/src/ODriveESP32TWAI.hpp index 9408937..31b1240 100644 --- a/src/ODriveESP32TWAI.hpp +++ b/src/ODriveESP32TWAI.hpp @@ -15,7 +15,7 @@ struct CanMsg { void onCanMessage(const CanMsg& msg); -static bool sendMsg(TWAIClass& can_intf, uint32_t id, uint8_t length, const uint8_t* data) { +static inline bool sendMsg(TWAIClass& can_intf, uint32_t id, uint8_t length, const uint8_t* data) { // Send CAN message can_intf.prepareMessage(id, length, !data); if (data) { @@ -26,11 +26,11 @@ static bool sendMsg(TWAIClass& can_intf, uint32_t id, uint8_t length, const uint return can_intf.endPacket(); } -static void onReceive(const CanMsg& msg, ODriveCAN& odrive) { +static inline void onReceive(const CanMsg& msg, ODriveCAN& odrive) { odrive.onReceive(msg.id, msg.len, msg.buffer); } -static void pumpEvents(TWAIClass& intf) { +static inline void pumpEvents(TWAIClass& intf) { CanMsg msg; int length = intf.parsePacket(); // Check if a packet is available @@ -45,4 +45,4 @@ static void pumpEvents(TWAIClass& intf) { } } -CREATE_CAN_INTF_WRAPPER(TWAIClass) \ No newline at end of file +CREATE_CAN_INTF_WRAPPER(TWAIClass) From 70b668b1c9d492561a74f5f9bebeedfbdca8cb7c Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 10:34:10 -0500 Subject: [PATCH 07/10] Update TWAI_CAN_Class.h updated as per suggestions --- src/TWAI_CAN_Class.h | 45 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/TWAI_CAN_Class.h b/src/TWAI_CAN_Class.h index b5773ab..a6daf19 100644 --- a/src/TWAI_CAN_Class.h +++ b/src/TWAI_CAN_Class.h @@ -1,8 +1,6 @@ #ifndef TWAI_CAN_CLASS_H #define TWAI_CAN_CLASS_H -//#include "driver/twai.h" - class TWAIClass { public: TWAIClass() : is_initialized(false) {} @@ -16,20 +14,49 @@ bool begin(long baudRate) { if (is_initialized) return false; // Already initialized // Configure TWAI (CAN) settings - twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)TX_PIN, (gpio_num_t)RX_PIN, TWAI_MODE_NORMAL); - //TODO ADD SWITCH CASE FOR DIFFERENT BAUDRATE**************************************************************************************************************** - twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); // Ensure it matches ODrive + // Select baud rate using switch-case. Ensure it matches ODrive configuration + twai_timing_config_t t_config; + switch (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: + Serial.println("Unsupported baud rate! Defaulting to 250Kbps."); + t_config = TWAI_TIMING_CONFIG_250KBITS(); + break; + } twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); // Install the TWAI driver if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { - Serial.println("❌ TWAI Driver install failed!"); + Serial.println("TWAI Driver install failed!"); return false; } // Start TWAI driver if (twai_start() != ESP_OK) { - Serial.println("❌ TWAI Driver start failed!"); + Serial.println("TWAI Driver start failed!"); twai_driver_uninstall(); return false; } @@ -38,11 +65,11 @@ bool begin(long baudRate) { uint32_t alerts_to_enable = TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_ERROR; if (twai_reconfigure_alerts(alerts_to_enable, NULL) != ESP_OK) { - Serial.println("❌ Failed to configure CAN Alerts!"); + Serial.println("Failed to configure CAN Alerts!"); return false; } - Serial.println("✅ TWAI CAN Bus Started Successfully!"); + Serial.println("TWAI CAN Bus Started Successfully!"); is_initialized = true; return true; } From c0bb4a7c4b1ecaacd1360b7d75f0ce49250da7d9 Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 10:35:34 -0500 Subject: [PATCH 08/10] Update SineWaveCAN.ino default node ID for default sketch --- examples/SineWaveCAN/SineWaveCAN.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/SineWaveCAN/SineWaveCAN.ino b/examples/SineWaveCAN/SineWaveCAN.ino index d001edb..f950f8c 100644 --- a/examples/SineWaveCAN/SineWaveCAN.ino +++ b/examples/SineWaveCAN/SineWaveCAN.ino @@ -8,7 +8,7 @@ #define CAN_BAUDRATE 250000 // ODrive node_id for odrv0 - #define ODRV0_NODE_ID 31 + #define ODRV0_NODE_ID 0 // Uncomment below the line that corresponds to your hardware. From 9df8fd5d32365e0b49553f0b64e4c94749cd0719 Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 10:40:25 -0500 Subject: [PATCH 09/10] Update TWAI_CAN_Class.h standard formatting --- src/TWAI_CAN_Class.h | 221 +++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 111 deletions(-) diff --git a/src/TWAI_CAN_Class.h b/src/TWAI_CAN_Class.h index a6daf19..686830f 100644 --- a/src/TWAI_CAN_Class.h +++ b/src/TWAI_CAN_Class.h @@ -3,159 +3,158 @@ class TWAIClass { public: - TWAIClass() : is_initialized(false) {} - ~TWAIClass() { - if (is_initialized) { - end(); - } + TWAIClass() + : is_initialized(false) {} + ~TWAIClass() { + if (is_initialized) { + end(); } - -bool begin(long baudRate) { + } + + bool begin(long baudRate) { if (is_initialized) return false; // Already initialized - // Configure TWAI (CAN) settings // Select baud rate using switch-case. Ensure it matches ODrive configuration twai_timing_config_t t_config; switch (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: - Serial.println("Unsupported baud rate! Defaulting to 250Kbps."); - t_config = TWAI_TIMING_CONFIG_250KBITS(); - break; - } + 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: + Serial.println("Unsupported baud rate! Defaulting to 250Kbps."); + t_config = TWAI_TIMING_CONFIG_250KBITS(); + break; + } twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); // Install the TWAI driver if (twai_driver_install(&g_config, &t_config, &f_config) != ESP_OK) { - Serial.println("TWAI Driver install failed!"); - return false; + Serial.println("TWAI Driver install failed!"); + return false; } // Start TWAI driver if (twai_start() != ESP_OK) { - Serial.println("TWAI Driver start failed!"); - twai_driver_uninstall(); - return false; + Serial.println("TWAI Driver start failed!"); + twai_driver_uninstall(); + return false; } // Configure TWAI Alerts (Monitor TX and Bus Errors) - uint32_t alerts_to_enable = TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | - TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_ERROR; + uint32_t alerts_to_enable = TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_ERROR; if (twai_reconfigure_alerts(alerts_to_enable, NULL) != ESP_OK) { - Serial.println("Failed to configure CAN Alerts!"); - return false; + Serial.println("Failed to configure CAN Alerts!"); + return false; } Serial.println("TWAI CAN Bus Started Successfully!"); is_initialized = true; return true; -} - - // End CAN communication - void end() { - if (is_initialized) { - twai_stop(); - twai_driver_uninstall(); - is_initialized = false; - } + } + + // End CAN communication + void end() { + if (is_initialized) { + twai_stop(); + twai_driver_uninstall(); + is_initialized = false; } + } - // Send a CAN message - int endPacket() { - if (!is_initialized || !message_ready) { - return 0; // Not initialized or no message ready - } - - if (twai_transmit(&tx_message, pdMS_TO_TICKS(100)) == ESP_OK) { - message_ready = false; - return 1; // Successfully sent message - } - - return 0; // Failed to send message + // Send a CAN message + int endPacket() { + if (!is_initialized || !message_ready) { + return 0; // Not initialized or no message ready } - // Prepare a CAN messagewrite - void prepareMessage(uint32_t id, uint8_t length, bool rtr = false) { - tx_message.identifier = id; - tx_message.data_length_code = length; - //tx_message.rtr = rtr ? TWAI_RTR : TWAI_NO_RTR; //TODO this line doesnt compile. verify if required for odrive******************************************* - - tx_message.rtr = 0; - tx_message.extd = (id > 0x7FF); // Extended frame if ID > 11 bits - message_ready = true; + if (twai_transmit(&tx_message, pdMS_TO_TICKS(100)) == ESP_OK) { + message_ready = false; + return 1; // Successfully sent message } - // Write data to the CAN message - void write(uint8_t byte, int index) { - if (index >= 0 && index < 8) { - tx_message.data[index] = byte; - } - } + return 0; // Failed to send message + } - // Parse incoming CAN packet - int parsePacket() { - if (!is_initialized) { - return 0; // Not initialized - } + // Prepare a CAN messagewrite + void prepareMessage(uint32_t id, uint8_t length, bool rtr = false) { + tx_message.identifier = id; + tx_message.data_length_code = length; + //tx_message.rtr = rtr ? TWAI_RTR : TWAI_NO_RTR; //TODO this line doesnt compile. verify if required for odrive******************************************* - if (twai_receive(&rx_message, pdMS_TO_TICKS(10)) == ESP_OK) { - return rx_message.data_length_code; // Number of bytes received - } + tx_message.rtr = 0; + tx_message.extd = (id > 0x7FF); // Extended frame if ID > 11 bits + message_ready = true; + } - return 0; // No packet received + // Write data to the CAN message + void write(uint8_t byte, int index) { + if (index >= 0 && index < 8) { + tx_message.data[index] = byte; } + } - // Access packet ID - uint32_t packetId() { - return rx_message.identifier; + // Parse incoming CAN packet + int parsePacket() { + if (!is_initialized) { + return 0; // Not initialized } - // Access received data - void readBytes(uint8_t* buffer, uint8_t length) { - if (length > 8) length = 8; // Maximum data length - for (uint8_t i = 0; i < length; ++i) { - buffer[i] = rx_message.data[i]; - //Serial.printf(" %d = %02x,", i, rx_message.data[i]); // DEBUG PRINT uncomment to print all messages to serial console ********************************** - } - //Serial.println(""); //uncomment to print all messages to serial console ********************************** + if (twai_receive(&rx_message, pdMS_TO_TICKS(10)) == ESP_OK) { + return rx_message.data_length_code; // Number of bytes received } - // Callback for received messages - void onReceive(void (*callback)(int)) { - receive_callback = callback; + return 0; // No packet received + } + + // Access packet ID + uint32_t packetId() { + return rx_message.identifier; + } + + // Access received data + void readBytes(uint8_t* buffer, uint8_t length) { + if (length > 8) length = 8; // Maximum data length + for (uint8_t i = 0; i < length; ++i) { + buffer[i] = rx_message.data[i]; + //Serial.printf(" %d = %02x,", i, rx_message.data[i]); // DEBUG PRINT uncomment to print all messages to serial console ********************************** } + //Serial.println(""); //uncomment to print all messages to serial console ********************************** + } + + // Callback for received messages + void onReceive(void (*callback)(int)) { + receive_callback = callback; + } private: - bool is_initialized; - bool message_ready = false; - twai_message_t tx_message; - twai_message_t rx_message; - void (*receive_callback)(int) = nullptr; + bool is_initialized; + bool message_ready = false; + twai_message_t tx_message; + twai_message_t rx_message; + void (*receive_callback)(int) = nullptr; }; extern TWAIClass CAN; -#endif // TWAI_CAN_CLASS_H +#endif // TWAI_CAN_CLASS_H From fde3d5daa9dd3cf4e85f8d3789dec93f0850cb1a Mon Sep 17 00:00:00 2001 From: BWV999-MTL Date: Mon, 17 Feb 2025 11:00:58 -0500 Subject: [PATCH 10/10] Update TWAI_CAN_Class.h added this line back wich was deleted by accident in previous commit : twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)TX_PIN, (gpio_num_t)RX_PIN, TWAI_MODE_NORMAL); --- src/TWAI_CAN_Class.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TWAI_CAN_Class.h b/src/TWAI_CAN_Class.h index 686830f..18301b6 100644 --- a/src/TWAI_CAN_Class.h +++ b/src/TWAI_CAN_Class.h @@ -14,6 +14,7 @@ class TWAIClass { bool begin(long baudRate) { if (is_initialized) return false; // Already initialized // Configure TWAI (CAN) settings + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)TX_PIN, (gpio_num_t)RX_PIN, TWAI_MODE_NORMAL); // Select baud rate using switch-case. Ensure it matches ODrive configuration twai_timing_config_t t_config; switch (baudRate) {