diff --git a/CHANGELOG.md b/CHANGELOG.md index 665d691..f45bb9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **Bundled firmware is now leader 1.2.7 / follower 1.2.17 (protocol V2.9).** + Both add time-shared exchange (`0x5E` leader, `0x5D` follower) so host and + gripper never transmit at the same time; an uplink frame that overlaps a host + byte loses the bytes in flight. **Host-breaking:** IMU/encoder `timestamp` moves + from DWT/250 (wraps every 17.18 s) to TIM2 1 MHz (wraps every 71 min 35 s); this + SDK already unwraps the new timebase, older host software does not. Images are + built with arm-none-eabi-gcc 10.3.1 from tc-gu-01 `8c059e9`; size, CRC32 and + notes are in `firmware/manifest.json`. Leader verified on the collector backpack + (0 bad uplink frames under load vs 17/7208 on 1.2.6 push). As before, replug the + leader's USB / power-cycle the follower's 24 V after flashing. + ### Fixed - **A lost follower exchange reply no longer loses IMU samples.** The follower diff --git a/README.md b/README.md index c530d95..ef8df66 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ motor's own firmware, checked against the required minimum. It is read-only: ```bash python python/examples/follower_status.py left -# [fw] FirmwareVersion(1.2.16.0) +# [fw] FirmwareVersion(1.2.17.0) # [motor] MotorModel(RS00, id=1, from flash, t_max=14.0Nm, v_max=33.0rad/s) # [motor fw] 0.0.3.32 (...) OK ``` diff --git a/docs/FIRMWARE.md b/docs/FIRMWARE.md index 192b770..c7179c6 100644 --- a/docs/FIRMWARE.md +++ b/docs/FIRMWARE.md @@ -126,11 +126,11 @@ inactive bank and uses the STM32H5 bank swap, so one build serves both banks. ```bash python python/examples/ota_update.py \ third_party/firmware/tc-gu-01/build/master/tc-gu-01-master.bin \ - left --target-version 1.2.6 + left --target-version 1.2.7 ``` **The two roles have independent version numbers** — at the time of writing the -leader is 1.2.6 and the follower 1.2.16, and neither is "behind" the other. Both +leader is 1.2.7 and the follower 1.2.17, and neither is "behind" the other. Both carry the same control-UART receive fix, which lives in shared code. The roles were briefly forced onto one number; that was dropped on 2026-09-24. @@ -142,13 +142,13 @@ them apart. If a leader reports 1.2.6, check `g.diagnostics.uart_stats()`: the fixed image answers with 44 bytes, so `rx_errors` / `rx_rearms` exist and are meaningful; the old one answers 32 bytes and the SDK reads both as 0 -- which is indistinguishable from a healthy zero. When in doubt, reflash -`tc-gu-01-master-1.2.6.bin` from `firmware/`; OTA compares no versions +the current leader image from `firmware/`; OTA compares no versions (`--target-version` is informational), so reflashing the same number is fine. Compare versions only within one role. Note the build output keeps the Makefile's unversioned name (`build/master/tc-gu-01-master.bin`), while the images released under -`firmware/` carry the version (`tc-gu-01-master-1.2.6.bin`). That is deliberate: +`firmware/` carry the version (`tc-gu-01-master-1.2.7.bin`). That is deliberate: a build artifact is whatever you just compiled, a release is a specific version someone may still be holding a copy of months later. **If you promote a local build into `firmware/`, rename it and update `firmware/manifest.json` in the diff --git a/firmware/README.md b/firmware/README.md index fe7dce4..9a896d1 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -5,8 +5,8 @@ part of this SDK. | Image | Role | Version | Protocol | Size | CRC32 | | --- | --- | --- | --- | --- | --- | -| `tc-gu-01-master-1.2.6.bin` | leader (SN ends **`m`**) | **1.2.6** | V2.6 | 118,300 B | `0x350393b5` | -| `tc-gu-01-slave-1.2.16.bin` | follower (SN ends **`s`**) | **1.2.16** | V2.8 | 167,544 B | `0x4a8cd360` | +| `tc-gu-01-master-1.2.7.bin` | leader (SN ends **`m`**) | **1.2.7** | V2.9 | 122,848 B | `0x19e881dd` | +| `tc-gu-01-slave-1.2.17.bin` | follower (SN ends **`s`**) | **1.2.17** | V2.9 | 180,100 B | `0xf019df36` | The follower's RobStride motor runs its own firmware; its images are listed under `motors` in the manifest (see [Motor firmware](#motor-firmware)): @@ -23,6 +23,11 @@ Leader and follower firmware are **versioned independently**; different numbers on the two halves of a pair are normal. The SDK refuses to drive a follower below **1.2.5**. +> **1.2.7 / 1.2.17 change the IMU/encoder `timestamp` timebase** (DWT/250, wrapping every +> 17.18 s → TIM2 1 MHz, wrapping every 71 min 35 s). This SDK handles both; older host +> software that unwraps with the 17.18 s period will produce wrong times. Upgrade host +> software together with the firmware. Protocol: tc-gu-01 `docs/PROTOCOL.md` V2.9. + ## Flashing ```bash @@ -53,7 +58,7 @@ To verify an image file before flashing: ```bash python -c "from xense.taccap import crc32_iso_hdlc -print(hex(crc32_iso_hdlc(open('firmware/tc-gu-01-master-1.2.6.bin','rb').read())))" +print(hex(crc32_iso_hdlc(open('firmware/tc-gu-01-master-1.2.7.bin','rb').read())))" # -> 0x350393b5 ``` diff --git a/firmware/manifest.json b/firmware/manifest.json index cff837c..988e58b 100644 --- a/firmware/manifest.json +++ b/firmware/manifest.json @@ -1,28 +1,28 @@ { "images": { "master": { - "file": "tc-gu-01-master-1.2.6.bin", - "version": "1.2.6", - "source_commit": "1336ebb", - "size_bytes": 118300, - "crc32": "0x350393b5", + "file": "tc-gu-01-master-1.2.7.bin", + "version": "1.2.7", + "source_commit": "8c059e9", + "size_bytes": 122848, + "crc32": "0x19e881dd", "sn_suffix": "m", - "protocol": "V2.6", + "protocol": "V2.9", "build": "local", - "toolchain": "arm-none-eabi-gcc (15:13.2.rel1-2) 13.2.1 20231009", - "note": "Leader line. Same fix as follower 1.2.11 in the shared receive path: the control UART (3 Mbaud, one interrupt per byte) stopped receiving for good after a single overrun, because the error callback never restarted reception -- the stream kept flowing while every command went unanswered. Reception is now restarted, and GET_UART_STATS reports rx_errors / rx_rearms. Verified on 0115m and 0116m (upgraded from 1.2.2): 15 min each of a 100 Hz encoder+IMU stream with a 100 Hz acknowledged command, ~87,000 commands, zero failures. AFTER FLASHING, unplug and replug the leader's USB cable." + "toolchain": "arm-none-eabi-gcc (15:10.3-2021.07-4) 10.3.1 20210621", + "note": "Leader time-shared exchange (STREAM_MODE_POLLED + LEADER_EXCHANGE 0x5E): the leader queues uplink records and only answers, so host and leader never transmit at the same time -- an uplink frame being sent while a host byte arrives loses the bytes in flight. Verified on the taccap-collector backpack (0029m/0030m): 0 bad uplink frames under full host load with ~20 Hz LED commands vs 17/7208 pushing on 1.2.6. HOST-BREAKING: IMU/encoder timestamp is now TIM2 1 MHz (low 32 bits, wraps every 71 min 35 s), no longer DWT/250 (17.18 s); upgrade host software together. Known: after a power-up the leader can stop answering ~0.5 s after the first stream start until the port is reopened (also on 1.2.6). AFTER FLASHING, unplug and replug the leader's USB cable." }, "slave": { - "file": "tc-gu-01-slave-1.2.16.bin", - "version": "1.2.16", - "source_commit": "a7dc07f", - "size_bytes": 167544, - "crc32": "0x4a8cd360", + "file": "tc-gu-01-slave-1.2.17.bin", + "version": "1.2.17", + "source_commit": "8c059e9", + "size_bytes": 180100, + "crc32": "0xf019df36", "sn_suffix": "s", - "protocol": "V2.8 + motion envelope", + "protocol": "V2.9 + motion envelope", "build": "local", - "toolchain": "arm-none-eabi-gcc (15:13.2.rel1-2) 13.2.1 20231009", - "note": "Stops decoding the motor's echo frames as status. A RobStride motor takes an MIT motion frame whose byte 0 equals its own CAN ID for another command and answers on 0xFD with the frame's first four bytes and zeros in bytes 4-7; byte 0 is the target position's high byte, and the factory ID 127 (0x7F) is -0.098..0 rad -- a LEFT-hand RS00's closed end. 1.2.15 decoded each echo as status (temperature 0, torque -t_max, position clamped to the far end), the envelope clamp then dragged the target toward the open end and the jaw was thrown open: force-position never closed to 0.0 on 0089s. The real fix is the CAN ID (RS00 followers: left 17, right 18, written by the factory config tool); this image is the backstop. Carries 1.2.15 (RS00 open direction from the SN's side). Verified on 0089s (left) / 0094s (right). AFTER FLASHING, unplug the 24 V power cable for ~2 s and plug it back; the USB cable can stay in." + "toolchain": "arm-none-eabi-gcc (15:10.3-2021.07-4) 10.3.1 20210621", + "note": "Follower time-shared exchange (SLAVE_EXCHANGE 0x5D, queue 32), optional 1-100 Hz IMU with accel/gyro calibration and magnetometer sessions, uplink collision detect-and-resend, cold-start homing order fix. HOST-BREAKING: IMU timestamp is now TIM2 1 MHz (wraps every 71 min 35 s). Carries 1.2.16. AFTER FLASHING, unplug the 24 V power cable for ~2 s and plug it back; the USB cable can stay in." } }, "motors": { diff --git a/firmware/tc-gu-01-master-1.2.6.bin b/firmware/tc-gu-01-master-1.2.6.bin deleted file mode 100755 index 73d812f..0000000 Binary files a/firmware/tc-gu-01-master-1.2.6.bin and /dev/null differ diff --git a/firmware/tc-gu-01-master-1.2.7.bin b/firmware/tc-gu-01-master-1.2.7.bin new file mode 100755 index 0000000..9e3cf53 Binary files /dev/null and b/firmware/tc-gu-01-master-1.2.7.bin differ diff --git a/firmware/tc-gu-01-slave-1.2.16.bin b/firmware/tc-gu-01-slave-1.2.16.bin deleted file mode 100755 index 033f7f8..0000000 Binary files a/firmware/tc-gu-01-slave-1.2.16.bin and /dev/null differ diff --git a/firmware/tc-gu-01-slave-1.2.17.bin b/firmware/tc-gu-01-slave-1.2.17.bin new file mode 100755 index 0000000..a7e732d Binary files /dev/null and b/firmware/tc-gu-01-slave-1.2.17.bin differ diff --git a/python/examples/ota_update.py b/python/examples/ota_update.py index 24c0980..ae0d18d 100644 --- a/python/examples/ota_update.py +++ b/python/examples/ota_update.py @@ -20,7 +20,7 @@ of the update, not as troubleshooting. The released images ship in this repo under `firmware/`, and their filenames -carry the version (tc-gu-01-slave-1.2.16.bin). Prefer the role selectors below: +carry the version (tc-gu-01-slave-1.2.17.bin). Prefer the role selectors below: they read the current filename from firmware/manifest.json, so they keep working across releases, while a literal filename goes stale the next time the firmware is bumped. A name you do pass resolves against firmware/ from any @@ -43,7 +43,7 @@ # An explicit image, when you mean a specific build rather than the # current release. - python python/examples/ota_update.py tc-gu-01-master-1.2.6.bin + python python/examples/ota_update.py tc-gu-01-master-1.2.7.bin # The version sent to the firmware (it writes bank metadata and the # post-install verification log with it) is taken from manifest.json by @@ -167,7 +167,7 @@ def _gripper_role(eps) -> str: def _default_firmware_for_role(role: str) -> str: """The shipped image for a role, named by firmware/manifest.json. - The filename carries the version (tc-gu-01-slave-1.2.16.bin), so it cannot be + The filename carries the version (tc-gu-01-slave-1.2.17.bin), so it cannot be derived from the role alone — the manifest is the single place the current release's filename is written, and `file` there is what this reads. That also means the manifest is load-bearing for every role-selector and --all @@ -363,7 +363,7 @@ def _resolve_firmware(path: str) -> Optional[str]: """Find the image whether `path` is relative to the cwd or to this repo. The images ship inside this repo, but the repo is usually vendored as a - submodule of something else — so `firmware/tc-gu-01-master-1.2.6.bin`, the path + submodule of something else — so `firmware/tc-gu-01-master-1.2.7.bin`, the path our docs print because it works from the SDK root, is not the path that works from the parent repo's root. Rather than making every downstream README carry its own prefix, accept both: the literal path first, then the @@ -414,7 +414,7 @@ def _identify_image(fw_bytes: bytes): """Look up the CRC32 in firmware/manifest.json; return (role, meta) or (None, None). **Identify by content, not by file name.** Released image file names carry - the version (tc-gu-01-slave-1.2.16.bin), but file names can change — copy + the version (tc-gu-01-slave-1.2.17.bin), but file names can change — copy it, rename it, download it from somewhere else, and the name starts lying. CRC32 does not: it identifies these exact bytes.