Created
April 23, 2025 20:05
-
-
Save BrechtSerckx/6d23b859b3192aa284aa145c497aafe5 to your computer and use it in GitHub Desktop.
All cyclic messages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#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. | |
// #define IS_STM32_BUILTIN // STM32 boards with built-in CAN interface (e.g. STM32F4 Discovery). | |
/* Board-specific includes ---------------------------------------------------*/ | |
#if defined(IS_TEENSY_BUILTIN) + defined(IS_ARDUINO_BUILTIN) + defined(IS_MCP2515) + defined(IS_STM32_BUILTIN) != 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 <Arduino_CAN.h> | |
#include <ODriveHardwareCAN.hpp> | |
#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 <FlexCAN_T4.h> | |
#include "ODriveFlexCAN.hpp" | |
struct ODriveStatus; // hack to prevent teensy compile error | |
#endif // IS_TEENSY_BUILTIN | |
#ifdef IS_STM32_BUILTIN | |
// See https://github.com/pazi88/STM32_CAN | |
#include <STM32_CAN.h> | |
#include "ODriveSTM32CAN.hpp" | |
#endif // IS_STM32_BUILTIN | |
/* Board-specific settings ---------------------------------------------------*/ | |
/* Teensy */ | |
#ifdef IS_TEENSY_BUILTIN | |
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> 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 | |
/* MCP2515-based extension modules -*/ | |
#ifdef IS_MCP2515 | |
MCP2515Class& can_intf = CAN; | |
// chip select pin used for the MCP2515 | |
#define MCP2515_CS 10 | |
// 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 | |
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; | |
} | |
CAN.onReceive(receiveCallback); | |
return true; | |
} | |
#endif // IS_MCP2515 | |
/* Arduinos with built-in CAN */ | |
#ifdef IS_ARDUINO_BUILTIN | |
HardwareCAN& can_intf = CAN; | |
bool setupCan() { | |
return can_intf.begin((CanBitRate)CAN_BAUDRATE); | |
} | |
#endif | |
/* STM32 boards with built-in CAN */ | |
#ifdef IS_STM32_BUILTIN | |
STM32_CAN Can1( CAN1 ); | |
STM32_CAN& can_intf = Can1; | |
bool setupCan() { | |
can_intf.begin(); | |
can_intf.setBaudRate(CAN_BAUDRATE); | |
return true; | |
} | |
#endif // IS_STM32_BUILTIN | |
/* Example sketch ------------------------------------------------------------*/ | |
// Instantiate ODrive objects | |
ODriveCAN odrv0(wrap_can_intf(can_intf), ODRV0_NODE_ID); // Standard CAN message ID | |
ODriveCAN* odrives[] = {&odrv0}; // Make sure all ODriveCAN instances are accounted for here | |
struct ODriveUserData { | |
Heartbeat_msg_t last_heartbeat; | |
bool received_heartbeat = false; | |
}; | |
// Keep some application-specific user data for every ODrive. | |
ODriveUserData odrv0_user_data; | |
// Called every time a Heartbeat message arrives from the ODrive | |
void onHeartbeat(Heartbeat_msg_t& msg, void* user_data) { | |
ODriveUserData* odrv_user_data = static_cast<ODriveUserData*>(user_data); | |
odrv_user_data->last_heartbeat = msg; | |
odrv_user_data->received_heartbeat = true; | |
} | |
void onFeedback(Get_Encoder_Estimates_msg_t& msg, void* user_data) { | |
Serial.print("Feedback - Pos_Estimate: "); | |
Serial.print(msg.Pos_Estimate); | |
Serial.print(", Vel_Estimate: "); | |
Serial.println(msg.Vel_Estimate); | |
} | |
void onTorques(Get_Torques_msg_t& msg, void* user_data) { | |
Serial.print("Torques - Torque_Target: "); | |
Serial.print(msg.Torque_Target); | |
Serial.print(", Torque_Estimate: "); | |
Serial.println(msg.Torque_Estimate); | |
} | |
void onTemperature(Get_Temperature_msg_t& msg, void* user_data) { | |
Serial.print("Temperature - FET_Temperature: "); | |
Serial.print(msg.FET_Temperature); | |
Serial.print(", Motor_Temperature: "); | |
Serial.println(msg.Motor_Temperature); | |
} | |
void onBusVI(Get_Bus_Voltage_Current_msg_t& msg, void* user_data) { | |
Serial.print("BusVI - Voltage: "); | |
Serial.print(msg.Bus_Voltage); | |
Serial.print(", Current: "); | |
Serial.println(msg.Bus_Current); | |
} | |
void onCurrents(Get_Iq_msg_t& msg, void* user_data) { | |
Serial.print("Currents - Iq_Setpoint: "); | |
Serial.print(msg.Iq_Setpoint); | |
Serial.print(", Iq_Measured: "); | |
Serial.println(msg.Iq_Measured); | |
} | |
void onError(Get_Error_msg_t& msg, void* user_data) { | |
Serial.print("Error - Active_Errors: "); | |
Serial.print(msg.Active_Errors); | |
Serial.print(", Disarm_Reason: "); | |
Serial.println(msg.Disarm_Reason); | |
} | |
// Called for every message that arrives on the CAN bus | |
void onCanMessage(const CanMsg& msg) { | |
for (auto odrive: odrives) { | |
onReceive(msg, *odrive); | |
} | |
} | |
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); | |
} | |
delay(200); | |
Serial.println("Starting ODriveCAN demo"); | |
// Register callbacks for the cyclic messages | |
odrv0.onFeedback(onFeedback, &odrv0_user_data); | |
odrv0.onStatus(onHeartbeat, &odrv0_user_data); | |
odrv0.onTorques(onTorques, &odrv0_user_data); | |
odrv0.onTemperature(onTemperature, &odrv0_user_data); | |
odrv0.onBusVI(onBusVI, &odrv0_user_data); | |
odrv0.onCurrents(onCurrents, &odrv0_user_data); | |
odrv0.onError(onError, &odrv0_user_data); | |
// Configure and initialize the CAN bus interface. This function depends on | |
// your hardware and the CAN stack that you're using. | |
if (!setupCan()) { | |
Serial.println("CAN failed to initialize: reset required"); | |
while (true); // spin indefinitely | |
} | |
Serial.println("Waiting for ODrive..."); | |
while (!odrv0_user_data.received_heartbeat) { | |
pumpEvents(can_intf); | |
delay(100); | |
} | |
Serial.println("found ODrive"); | |
} | |
void loop() { | |
pumpEvents(can_intf); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment