-
Notifications
You must be signed in to change notification settings - Fork 29
Await acks for write SDO, filter endpoint IDs #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -323,6 +323,12 @@ class ODriveCAN { | |
| return T{}; | ||
| } | ||
|
|
||
| // Ignore a TxSdo that echoes a different endpoint than we requested. | ||
| uint16_t resp_endpoint = (uint16_t)buffer_[1] | ((uint16_t)buffer_[2] << 8); | ||
| if (resp_endpoint != endpoint_id) { | ||
| return T{}; | ||
| } | ||
|
|
||
| T ret{}; | ||
| memcpy(&ret, &buffer_[4], sizeof(T)); | ||
| return ret; | ||
|
|
@@ -334,24 +340,43 @@ class ODriveCAN { | |
| * @tparam T Type of the value from flat_endpoints.json | ||
| * @param endpoint_id Unique ID of endpoint from flat_endpoints.json | ||
| * @param value value to write to the endpoint | ||
| * @param timeout_ms Time to wait for the ODrive's TxSdo acknowledgment. | ||
| * Pass 0 to send without waiting (fire-and-forget). | ||
| * | ||
| * This function returns immediately and does not check if the ODrive | ||
| * received the CAN message. | ||
| * @return true if the ODrive acknowledged the write (or timeout_ms == 0), | ||
| * false if no matching TxSdo was received before the timeout. | ||
| * | ||
| * The ODrive replies with a TxSdo message in response to any RxSdo, | ||
| * including writes, so this blocks until that acknowledgment is received | ||
| * or the timeout is reached. | ||
| */ | ||
| template<typename T> | ||
| bool setEndpoint(uint16_t endpoint_id, T value) { | ||
| bool setEndpoint(uint16_t endpoint_id, T value, uint16_t timeout_ms = 10) { | ||
| uint8_t data[8] = {}; | ||
| data[0] = 1; // Opcode write | ||
|
|
||
| // Endpoint | ||
| data[1] = endpoint_id & 0xFF; | ||
| data[2] = (endpoint_id >> 8) & 0xFF; | ||
| // Little-endian endpoint | ||
| data[1] = (uint8_t)(endpoint_id); | ||
| data[2] = (uint8_t)(endpoint_id >> 8); | ||
|
|
||
| // Value to write | ||
| memcpy(&data[4], &value, sizeof(T)); | ||
|
|
||
| requested_msg_id_ = 0x005; // Await TxSdo acknowledgment | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's only write this when |
||
| can_intf_.sendMsg((node_id_ << ODriveCAN::kNodeIdShift) | 0x004, 8, data); | ||
| return true; | ||
|
|
||
| if (timeout_ms == 0) { | ||
| requested_msg_id_ = REQUEST_PENDING; // fire-and-forget, don't wait | ||
| return true; | ||
| } | ||
|
|
||
| if (!awaitMsg(timeout_ms)) { | ||
| return false; | ||
| } | ||
|
|
||
| // Confirm the acknowledgment echoes the endpoint we wrote to. | ||
| uint16_t acked_endpoint = (uint16_t)buffer_[1] | ((uint16_t)buffer_[2] << 8); | ||
| return acked_endpoint == endpoint_id; | ||
| } | ||
|
|
||
| private: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,9 @@ | |
| // Must be defined by the application | ||
| void onCanMessage(const CanMsg& msg); | ||
|
|
||
| // Forward declaration; defined below and used by sendMsg(). | ||
| static void pumpEvents(HardwareCAN& intf, int max_events = 100); | ||
|
|
||
| /** | ||
| * @brief Sends a CAN message over the specified platform-specific interface. | ||
| * | ||
|
|
@@ -26,12 +29,19 @@ static bool sendMsg(HardwareCAN& can_intf, uint32_t id, uint8_t length, const ui | |
| // Note: Arduino_CAN does not support the RTR bit. The ODrive interprets | ||
| // zero-length packets the same as RTR=1, but it creates the possibility of | ||
| // collisions. | ||
|
|
||
| // Flush pending RX messages before writing. On polling-based platforms (e.g. | ||
| // Arduino Uno R4) the single TX mailbox may report busy if the CAN | ||
| // controller hasn't had a chance to process events. This mirrors the | ||
| // can_intf.events() call in the FlexCAN (Teensy) adapter. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The But I have to say I find it a bit unclean in both cases, and a bit weird that the RX path is so entangled with the TX path. Was this experimentally confirmed (plz link)? Can you point to the relevant code in the platform driver stack that shows this behavior? |
||
| pumpEvents(can_intf); | ||
|
|
||
| CanMsg msg{ | ||
| (id & 0x80000000) ? CanExtendedId(id) : CanStandardId(id), | ||
| length, | ||
| data, | ||
| }; | ||
| return can_intf.write(msg) >= 0; | ||
| return can_intf.write(msg) > 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems this change is unnecessary? So we should leave it as-is, unless this is confirmed to fix an issue with a non-compliant platform (lest we break another non-compliant platform). |
||
| } | ||
|
|
||
| /** | ||
|
|
@@ -56,7 +66,7 @@ static void onReceive(const CanMsg& msg, ODriveCAN& odrive) { | |
| * @param max_events: The maximum number of events to process. This prevents | ||
| * an infinite loop if messages come at a high rate. | ||
| */ | ||
| static void pumpEvents(HardwareCAN& intf, int max_events = 100) { | ||
| static void pumpEvents(HardwareCAN& intf, int max_events) { | ||
| // max_events prevents an infinite loop if messages come at a high rate | ||
| while (intf.available() && max_events--) { | ||
| onCanMessage(intf.read()); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TxSdo ack was introduced in fw 0.6.11. From changelog:
So we should mention here that this requires fw 0.6.11, and that for older firmware, fire-and-forget can be used.