From bdf90674c72e4e7d4a92823c4d49b90ee1484b28 Mon Sep 17 00:00:00 2001 From: Yannis Chatzikonstantinou Date: Tue, 23 Apr 2024 03:03:45 +0300 Subject: [PATCH 1/5] add back arduino uno example --- .../arduino_uno_mcp2515.ino | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino diff --git a/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino b/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino new file mode 100644 index 0000000..3e6138d --- /dev/null +++ b/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino @@ -0,0 +1,201 @@ +/* + * This is a minimal example of the Tinymovr-Arduino library + * functionality, using the Arduino Uno with MCP2515 to connect + * to CAN bus. The example allows sending a few basic commands, + * and supports reading back information. + * + * For CAN endpoint reference check out: + * https://tinymovr.readthedocs.io/en/latest/api/guide.html#api-reference + */ + + +#include "Arduino.h" +#include // "CAN Adafruit Fork" library +#include + +// --------------------------------------------------------------- +// REQUIRED CAN HARDWARE INTERFACE CODE +// ADAPT BELOW TO YOUR CAN ADAPTER HARDWARE + +// The send_cb and recv_cb functions need to be implemented +// according to the CAN bus hardware you use. Below an example +// usable with MCP2515 type breakouts + +/* + * Function: send_cb + * -------------------- + * Is called to send a CAN frame + * + * arbitration_id: the frame arbitration id + * data: pointer to the data array to be transmitted + * data_size: the size of transmitted data + * rtr: if the ftame is of request transmit type (RTR) + */ +void send_cb(uint32_t arbitration_id, uint8_t *data, uint8_t data_size, bool rtr) +{ + CAN.beginExtendedPacket(arbitration_id, data_size, rtr); + for (int i=0; i 0) { + *data_size = packetSize; + for (int i = 0; i < packetSize; i++) { + int r = CAN.read(); + if (r == -1) return false; + data[i] = (uint8_t)r; + } + *arbitration_id = CAN.packetId(); + return true; + } + return false; +} + +/* + * Function: delay_us_cb + * -------------------- + * Is called to perform a delay + * + * us: the microseconds to wait for + */ +void delay_us_cb(uint32_t us) +{ + delayMicroseconds(us); +} +// --------------------------------------------------------------- + +// --------------------------------------------------------------- +// EXAMPLE CODE +// ADAPT BELOW TO YOUR PROGRAM LOGIC + +// The Tinymovr object +Tinymovr tinymovr(1, &send_cb, &recv_cb, &delay_us_cb, 100); + +/* + * Function: setup + * -------------------- + * Perform hardware initialization + */ +void setup() +{ + Serial.begin(115200); + CAN.setPins(12, 13); + + // Most MCP2515 breakouts have a 8MHz crystal, this needs + // to be specified here + CAN.setClockFrequency(8e6); + + // start the CAN bus at 1Mbps + if (!CAN.begin(1000E3)) { + Serial.println("Starting CAN failed!"); + while (1); + } + + // NOTE: You NEED to enable filtering using this pattern, + // otherwise the library will not function correctly, + // especially with a lot of Tinymovr units on the bus + if (!CAN.filterExtended(0x0, 0x700)) + { + Serial.println("Setting CAN filters failed!"); + while (1); + } + + // As a final step check that the hash returned by the node + // is the same as the hash stored by the Tinymovr library. + // This is crucial to prevent potential mismatches in commands. + if (tinymovr.get_protocol_hash() != avlos_proto_hash) + { + Serial.println("Wrong device spec!"); + while (1); + } +} + +/* + * Function: loop + * -------------------- + * Program loop. + * Listen for commands coming from serial and + * transmit to Tinymovr. + */ +void loop() +{ + if (Serial.available() > 0) { + uint8_t receivedChar = Serial.read(); + if (receivedChar == 'Q') + { + Serial.println("Received Calibration command"); + tinymovr.controller.set_state(1); + } + else if (receivedChar == 'A') + { + Serial.println("Received Closed Loop command"); + tinymovr.controller.set_state(2); + tinymovr.controller.set_mode(2); + } + else if (receivedChar == 'Z') + { + Serial.println("Received Idle command"); + tinymovr.controller.set_state(0); + } + else if (receivedChar == 'R') + { + Serial.println("Received reset command"); + tinymovr.reset(); + } + else if (receivedChar == '<') + { + Serial.println("Received L turn command"); + float pos_estimate = tinymovr.sensors.user_frame.get_position_estimate(); + Serial.println(pos_estimate); + tinymovr.controller.position.set_setpoint(pos_estimate - 8192.0f); + } + else if (receivedChar == '>') + { + Serial.println("Received R turn command"); + float pos_estimate = tinymovr.sensors.user_frame.get_position_estimate(); + Serial.println(pos_estimate); + tinymovr.controller.position.set_setpoint(pos_estimate + 8192.0f); + } + else if (receivedChar == 'I') + { + // Print board information + Serial.print("Device ID: "); + Serial.print(tinymovr.comms.can.get_id()); + Serial.print(", Temp:"); + Serial.print(tinymovr.get_temp()); + Serial.print(", State:"); + Serial.print(tinymovr.controller.get_state()); + Serial.print(", Mode:"); + Serial.print(tinymovr.controller.get_mode()); + Serial.print("\n"); + Serial.print("Position estimate: "); + Serial.print(tinymovr.sensors.user_frame.get_position_estimate()); + Serial.print(", Velocity estimate: "); + Serial.print(tinymovr.sensors.user_frame.get_velocity_estimate()); + Serial.print("\n"); + Serial.print("Iq estimate: "); + Serial.print(tinymovr.controller.current.get_Iq_estimate()); + Serial.print(", Iq setpoint: "); + Serial.print(tinymovr.controller.current.get_Iq_setpoint()); + Serial.print("\n"); + Serial.println("---"); + } + } + delay(50); +} +// --------------------------------------------------------------- From 546419bf3769803028857ec4fb4b7ba190742ffc Mon Sep 17 00:00:00 2001 From: Yannis Chatzikonstantinou Date: Tue, 23 Apr 2024 03:12:55 +0300 Subject: [PATCH 2/5] update readme --- readme.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index e6d477b..9a38569 100644 --- a/readme.md +++ b/readme.md @@ -34,11 +34,13 @@ Functions are called with their name and required parameters. Even though the library itself is hardware-agnostic (by means of dependency injection), the examples we provide depend on the [CAN Adafruit Fork](https://github.com/adafruit/arduino-CAN) library for low-level hardware access and configuration. Why this library? Because it is the only one offering robust extended frame hardware filtering configuration. There are a ton of CAN libraries for Arduino and embedded in general, but most have only the basic functionality working and fully tested, not the advanced features that Tinymovr requires. -However, not all hardware is compatible with the CAN Adafruit Fork library. Namely, the MCP2515 breakout boards (the blue ones) with 8MHz crystal, of which there are plenty to be found from Chinese sources, are incompatible. The CAN Adafruit Fork library has the crystal frequency harcoded to 16MHz, which breaks the CAN timing configuration. Unfortunately, Adafruit has archived the library repository so it is impossible to raise an issue on this matter. +~~However, not all hardware is compatible with the CAN Adafruit Fork library. Namely, the MCP2515 breakout boards (the blue ones) with 8MHz crystal, of which there are plenty to be found from Chinese sources, are incompatible. The CAN Adafruit Fork library has the crystal frequency harcoded to 16MHz, which breaks the CAN timing configuration. Unfortunately, Adafruit has archived the library repository so it is impossible to raise an issue on this matter. -As we cannot afford to maintain another low-level CAN library fork, this situation will most probably remain as is for the foreseeable future. If you use this MCP2515 breakout our advice would be to switch to something like a CAN BUS Shield with a 16 MHz crystal. Alternatively, you may try to fork the CAN Adafruit Fork library, or implement your own `send_cb`, `recv_cb` and `delay_us_cb` routines. In both cases, you can ask for community support in our [Discord server](https://discord.gg/vNvmpfthug), but these options are not officially supported. +As we cannot afford to maintain another low-level CAN library fork, this situation will most probably remain as is for the foreseeable future. If you use this MCP2515 breakout our advice would be to switch to something like a CAN BUS Shield with a 16 MHz crystal. Alternatively, you may try to fork the CAN Adafruit Fork library, or implement your own `send_cb`, `recv_cb` and `delay_us_cb` routines. In both cases, you can ask for community support in our [Discord server](https://discord.gg/vNvmpfthug), but these options are not officially supported.~~ -We've tested this library with an Arduino MKR CAN Bus Shield. Other boards/shields should be compatible. +There was an undocumented function that allows setting the MCP2515 crystal frequency; thus the MCP2515 breakout boards (the blue ones) with 8MHz crystal are compatible after all. Hooray!! + +We've tested this library with an Arduino Uno with an MCP2515 breakout, and an Arduino MKR WiFi 1010 with a CAN Bus Shield. Other boards/shields should be compatible. ## License From c13bf6a5869e83f9c9a1fe724dcddf7581370be2 Mon Sep 17 00:00:00 2001 From: Yannis Chatzikonstantinou Date: Tue, 23 Apr 2024 20:54:47 +0300 Subject: [PATCH 3/5] add unsigned long type specifiers to defines --- src/helpers.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers.hpp b/src/helpers.hpp index 4428625..65135ea 100644 --- a/src/helpers.hpp +++ b/src/helpers.hpp @@ -22,11 +22,11 @@ #endif #define CAN_EP_SIZE (12) -#define CAN_EP_MASK ((1 << CAN_EP_SIZE) - 1) +#define CAN_EP_MASK ((1UL << CAN_EP_SIZE) - 1) #define CAN_SEQ_SIZE (9) -#define CAN_SEQ_MASK (((1 << CAN_SEQ_SIZE) - 1) << CAN_EP_SIZE) +#define CAN_SEQ_MASK (((1UL << CAN_SEQ_SIZE) - 1) << CAN_EP_SIZE) #define CAN_DEV_SIZE (8) -#define CAN_DEV_MASK (((1 << CAN_DEV_SIZE) - 1) << (CAN_EP_SIZE + CAN_SEQ_SIZE)) +#define CAN_DEV_MASK (((1UL << CAN_DEV_SIZE) - 1) << (CAN_EP_SIZE + CAN_SEQ_SIZE)) typedef void (*send_callback)(uint32_t arbitration_id, uint8_t *data, uint8_t dlc, bool rtr); typedef bool (*recv_callback)(uint32_t *arbitration_id, uint8_t *data, uint8_t *dlc); From 9d4ab2bf1d3c57f6935383d13013140476161a03 Mon Sep 17 00:00:00 2001 From: Yannis Chatzikonstantinou Date: Tue, 23 Apr 2024 20:55:01 +0300 Subject: [PATCH 4/5] improve init --- examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino b/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino index 3e6138d..abca7a3 100644 --- a/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino +++ b/examples/arduino_uno_mcp2515/arduino_uno_mcp2515.ino @@ -94,7 +94,7 @@ Tinymovr tinymovr(1, &send_cb, &recv_cb, &delay_us_cb, 100); void setup() { Serial.begin(115200); - CAN.setPins(12, 13); + while (!Serial); // Most MCP2515 breakouts have a 8MHz crystal, this needs // to be specified here @@ -118,10 +118,15 @@ void setup() // As a final step check that the hash returned by the node // is the same as the hash stored by the Tinymovr library. // This is crucial to prevent potential mismatches in commands. - if (tinymovr.get_protocol_hash() != avlos_proto_hash) + uint32_t got_hash = tinymovr.get_protocol_hash(); + if (got_hash != avlos_proto_hash) { Serial.println("Wrong device spec!"); - while (1); + Serial.print("Got: "); + Serial.println(got_hash); + Serial.print("Need: "); + Serial.println(avlos_proto_hash); + while(1); } } From 9f6aa1a171a2f843f2ad7e725935656b7eaa94ca Mon Sep 17 00:00:00 2001 From: Yannis Chatzikonstantinou Date: Tue, 23 Apr 2024 21:00:28 +0300 Subject: [PATCH 5/5] explicit cast of node id --- src/helpers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers.hpp b/src/helpers.hpp index 65135ea..4e76723 100644 --- a/src/helpers.hpp +++ b/src/helpers.hpp @@ -48,7 +48,7 @@ class Node { uint8_t _dlc; uint32_t get_arbitration_id(uint32_t cmd_id) { - return ((this->can_node_id << (CAN_EP_SIZE + CAN_SEQ_SIZE)) & CAN_DEV_MASK) | (cmd_id & CAN_EP_MASK); + return ((((uint32_t)this->can_node_id) << (CAN_EP_SIZE + CAN_SEQ_SIZE)) & CAN_DEV_MASK) | (cmd_id & CAN_EP_MASK); } void send(uint32_t cmd_id, uint8_t *data, uint8_t data_size, bool rtr) {