Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`expected_open_reverse`) is the rule; EL05 is not covered (one unit
measured).

### Changed

- Shipped follower image is now **1.2.15**: an RS00's open direction follows
the side in its firmware SN, auto-calibration corrects a stored direction that
disagrees, and a model-record change keeps the side's direction. Verified on
0089s (left) / 0094s (right), including a deliberately reversed 0089s
corrected on the next 24 V power cycle. Leader image unchanged (1.2.6).

## [0.4.2] - 2026-10-08

### Fixed
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ worked. The bank-swap reboot is a soft reset that leaves the device looking heal
while quietly dropping status frames.

**The two roles carry independent version numbers.** At the time of writing the
leader is 1.2.6 and the follower 1.2.14; neither is behind the other, and
leader is 1.2.6 and the follower 1.2.15; neither is behind the other, and
`gripper.firmware_version` returning different numbers for the two halves of a
pair is normal. Compare versions only within a role — the floors above are
follower numbers. **A leader reporting 1.2.6 may be either of two images**: the
Expand Down Expand Up @@ -309,8 +309,9 @@ normalized position is mirrored, a closed jaw reads ~1.0 and `set_target(0.0)`
opens it. `FollowerGripper` warns on open when the stored bit disagrees with
the side; `t.expected_open_reverse(model_id, side)` gives the right bit. Fix it
with `g.set_gripper_config()` (change the flags only) and cut 24 V so
calibration re-runs. A model-record change resets the bit to the model default
again. EL05 has no rule yet.
calibration re-runs. Follower 1.2.15 does this by itself: calibration takes the
direction from the SN's side and corrects a stored bit that disagrees, and a
model-record change keeps the side's direction. EL05 has no rule yet.

### By task

Expand Down
6 changes: 3 additions & 3 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ USB 线可以不拔 —— 从爪的 MCU 和电机都靠 24V 运行,只拔 USB
主爪没有 24V:拔插它的 USB 即可。`gripper.device.heartbeat().uptime_ms` 从接近 0
重新计起,就说明真的断过电了。bank-swap 重启只是一次软复位,它会让设备看上去一切正常,却在悄悄丢状态帧。

**主从两个角色的版本号是各自独立的。** 写这段时主爪是 1.2.6、从爪是 1.2.14,
**主从两个角色的版本号是各自独立的。** 写这段时主爪是 1.2.6、从爪是 1.2.15,
谁也不比谁旧;一对夹爪的两半 `gripper.firmware_version` 读出不同的数是正常的。
**只在同一个角色内部比较版本号** —— 上面那些门槛都是从爪的数。**报 1.2.6 的主爪
可能是两个不同的镜像**:当前的这个带控制串口接收修复;两个角色曾被强行同号那段
Expand Down Expand Up @@ -264,8 +264,8 @@ python python/examples/force_position_control.py left --grasp-torque 1.1
方向原样写回,所以新的左手 RS00 第一次标定一定标反、而且不会自己纠正 —— 开度整个
颠倒,合着读成接近 1,`set_target(0.0)` 会张开。`FollowerGripper` 打开时若存的方向
与左右不符会告警;`t.expected_open_reverse(model_id, side)` 给出应有的值。修法:用
`g.set_gripper_config()` 只改 flags,再拔插 24V 让标定按新方向重跑。改型号记录会把
它重置回型号默认。EL05 暂无规则。
`g.set_gripper_config()` 只改 flags,再拔插 24V 让标定按新方向重跑。从爪 1.2.15 起固件
自己会做:标定按 SN 左右定方向、纠正存反的方向,改型号记录也保留左右方向。EL05 暂无规则。

### 按任务分

Expand Down
2 changes: 1 addition & 1 deletion docs/FIRMWARE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ python python/examples/ota_update.py \
```

**The two roles have independent version numbers** — at the time of writing the
leader is 1.2.6 and the follower 1.2.14, and neither is "behind" the other. Both
leader is 1.2.6 and the follower 1.2.15, 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.

Expand Down
11 changes: 8 additions & 3 deletions firmware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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.14.bin` | follower (SN ends **`s`**) | **1.2.14** | V2.8 + 运动安全包络 | 167,168 B | `0x267d7f67` |
| `tc-gu-01-slave-1.2.15.bin` | follower (SN ends **`s`**) | **1.2.15** | V2.8 + 运动安全包络 | 167,464 B | `0xbdbb2b4b` |

Only the current release is kept here. Older images come from this directory's
git history rather than from extra files.
Expand Down Expand Up @@ -35,8 +35,8 @@ sources bumps that role alone.
this directory's `tc-gu-01-master-1.2.6.bin` if you are not sure. See
docs/FIRMWARE.md.

The leader is at 1.2.6 while the follower is at 1.2.14 for that reason: 1.2.7,
1.2.8, 1.2.10, 1.2.12, 1.2.13 and 1.2.14 touched only follower code, and 1.2.9 changed the shared
The leader is at 1.2.6 while the follower is at 1.2.15 for that reason: 1.2.7,
1.2.8, 1.2.10, 1.2.12, 1.2.13, 1.2.14 and 1.2.15 touched only follower code, and 1.2.9 changed the shared
`storage.c`, which is what moved the leader from 1.2.4 to 1.2.5 — with no change
in its behaviour.

Expand Down Expand Up @@ -76,6 +76,11 @@ the direction are reset to the new model's defaults; a stored calibration torque
still at exactly the old 0.35 migrates on boot. A gripper OTA also no longer
refuses when the motor is not on the bus.

**Follower 1.2.15 sets an RS00's open direction from its side.** The gear train
is mirror-mounted, so a left follower (odd SN) needs Reverse 1 and a right one
(even) Reverse 0; auto-calibration now uses that and corrects a stored bit that
disagrees. Requires the SN to be burned correctly. Verified on 0089s / 0094s.

**Follower 1.2.14 records the motor's firmware version in flash** (0x5C to
write it, 0x58 falls back to it under MIT) -- see the SDK README, *RobStride
motor firmware*. Verified on 0094s: written, read back under MIT, kept across a
Expand Down
12 changes: 6 additions & 6 deletions firmware/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
"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."
},
"slave": {
"file": "tc-gu-01-slave-1.2.14.bin",
"version": "1.2.14",
"source_commit": "1336ebb",
"size_bytes": 167168,
"crc32": "0x267d7f67",
"file": "tc-gu-01-slave-1.2.15.bin",
"version": "1.2.15",
"source_commit": "4d3a3b9",
"size_bytes": 167464,
"crc32": "0xbdbb2b4b",
"sn_suffix": "s",
"protocol": "V2.8 + 运动安全包络",
"build": "local",
"toolchain": "arm-none-eabi-gcc (15:13.2.rel1-2) 13.2.1 20231009",
"note": "Records the motor's own firmware version in follower flash, so it reads under MIT too (protocol V2.8): taken automatically on a private-protocol boot, or written by a host with the new 0x5C (Motor.set_motor_fw_version; a factory SOP step, and after a motor OTA). 0x58 returns the record when the motor cannot answer and says so in its former reserved byte. Model-dependent defaults now follow the motor model: auto-calibration stall torque (EL05 0.5 / RS00 1.8 N*m, was 0.35 for all -- too little to move an RS00, so calibration stopped short and could record the open direction backwards), default open direction (EL05 reverse, RS00 not), and a motion envelope enforced when none is stored (cont = stall rating, peak = rated torque -- 1.2.13; 1.2.12 used t_max, temperature wall 90/100 C; was no limit at all). When the model record changes, 0x700B, the calibration torque and the direction are reset to the new model's defaults; a stored calibration torque still at the old 0.35 migrates on boot. A gripper OTA no longer refuses when the motor is not on the bus. Carries 1.2.11 (control-UART receive restarted after an overrun). AFTER FLASHING, unplug the 24 V power cable for ~2 s and plug it back; the USB cable can stay in."
"note": "RS00 followers are handed: the gear train is mirror-mounted, so the open direction follows the side in the firmware SN (last digit odd = left = Reverse 1, even = right = Reverse 0) instead of one default per model. Auto-calibration uses that direction and corrects a stored one that disagrees (that run approaches slowly, since the old zero is at the open end); a model-record change picks the side's direction too. EL05 and units whose SN gives no side behave as 1.2.14. Verified on 0089s (left) and 0094s (right); 0089s deliberately stored reversed was corrected on the next 24 V power cycle. Carries 1.2.14 (motor firmware version record, protocol V2.8). AFTER FLASHING, unplug the 24 V power cable for ~2 s and plug it back; the USB cable can stay in."
}
}
}
Binary file not shown.
6 changes: 3 additions & 3 deletions python/examples/ota_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.14.bin). Prefer the role selectors below:
carry the version (tc-gu-01-slave-1.2.15.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
Expand Down Expand Up @@ -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.14.bin), so it cannot be
The filename carries the version (tc-gu-01-slave-1.2.15.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
Expand Down Expand Up @@ -377,7 +377,7 @@ def _identify_image(fw_bytes: bytes):
"""CRC32 反查 firmware/manifest.json,返回 (role, meta) 或 (None, None)。

**按内容认,不按文件名认。** 发布镜像的文件名带版本
(tc-gu-01-slave-1.2.14.bin),但文件名是可以被改的 —— 拷走、重命名、从别处
(tc-gu-01-slave-1.2.15.bin),但文件名是可以被改的 —— 拷走、重命名、从别处
下载,名字就开始说谎。CRC32 不会:它认的是这一份字节。

认不出来不是错误,只是"不是我们发布的镜像"(自己编的、第三方的),调用方按
Expand Down
Loading