HendiCtrl.cmd() previously raised a bare "Communication error" for any non-OK response, discarding the firmware's actual reply. It now includes the raw echo/answer bytes, and --debug prints every request/response. Using this, the rejected R1 during lockout turned out to be an explicit "ERR:Invalid state" reply, not a timeout. Also flashed and retested v1.16: plain reconnect (no --reset) now recovers from the lockout reliably (4/4 trials), fixing the intermittent behavior seen on v1.15. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GpePKZiEZWbGo9HrfuML6U
4.0 KiB
Hendi remote-control lockout findings
Reproduced and verified on real hardware using scripts/hendi_ctrl_app.py's
--trigger-error-state, --reset and --test-heater flags, across four
firmware versions (getSoftwareVersion() / I?+V? via
components/actor/hendiCtrl.py).
Trigger
Calling remoteEnable(True) (R1) and then disconnecting/exiting without a
matching remoteEnable(False) (R0) - i.e. an ungraceful disconnect while
remote control is enabled - puts the device into a locked-out state.
Recovery attempts - v1.12 vs v1.14 vs v1.15 vs v1.16
v1.14 flashed via --update-firmware HendiCtrl_0114.srec; v1.15 via
HendiCtrl_0115.srec; v1.16 via HendiCtrl_0116.srec. getSoftwareVersion()
confirmed each version after flashing.
| Recovery method | Mechanism | v1.12 (original) | v1.14 (firmware fix) | v1.15 (firmware fix) | v1.16 (firmware fix) |
|---|---|---|---|---|---|
Plain reconnect, no --reset |
Fresh serial connection only, no DTR/RTS toggle | Fails | Fails - unchanged, a bare reconnect alone still does not clear the lockout | Intermittent - recovered on 1 of 3 isolated trigger/reconnect cycles, failed the other 2; not a reliable recovery path | Recovers - reproducible across 4 of 4 independent trigger/reconnect/verify cycles |
--reset, isolated process |
Soft reset via DTR/RTS toggle, no power loss | Fails (one fluke recovery when combined with --test-heater in the same process/connection - not reproducible in isolation) |
Recovers - reproducible across two independent trigger/reset/verify cycles | Recovers - reproducible across all trigger/reset/verify cycles run | Not retested (superseded by plain reconnect working) |
| Physical power cycle (device knob) | Full power-off/on of the unit | Recovers | Not retested (superseded by --reset working) |
Not retested (superseded by --reset working) |
Not retested (superseded by plain reconnect working) |
Symptom while locked out
I?/V?(software identifier/version queries) still respond normally.R1(remoteEnable(True)) is the only rejected command; it blocks any command path that needs remote control enabled (e.g.--test-heater).R0(remoteEnable(False)) still succeeds (OK:0) even while locked out.- With
hendi_ctrl_app.py --debug(raw protocol trace added tocomponents/actor/hendiCtrl.py'scmd()), the actual wire exchange for a rejectedR1on v1.15 is:So this isn't a timeout or garbled response - the firmware actively replies withreq='R1' echo=b'R1\r\n' answer=b'\rERR:Invalid state\r\n'ERR:Invalid state.HendiCtrl.cmd()previously only checked for"OK"in the response and raised a genericHendiException("Communication error")for anything else, discarding this actual error text; it now includes the raw echo/answer in the exception message.
Conclusion
On firmware v1.12, only a physical power cycle clears the lockout; neither a serial soft-reset nor a fresh serial reconnect does reliably.
Firmware v1.14 fixes this partially: a soft reset (--reset, i.e.
toggling the DTR/RTS line) now reliably clears the lockout, confirmed across
two independent trigger/reset/verify cycles - no physical power cycle
needed. However, a plain reconnect without --reset still does not clear
it, so the fix specifically ties recovery to the DTR/RTS reset line rather
than to any new connection.
Firmware v1.15 keeps --reset working reliably, same as v1.14. It also
introduces an intermittent plain-reconnect recovery (1 of 3 isolated trials)
that v1.14 didn't show at all - but since it fails the majority of the time,
it should be treated as a remaining bug/race condition rather than a fix,
and --reset is still the recommended recovery path.
Firmware v1.16 appears to fully fix the lockout: a plain reconnect with
no --reset at all recovered on 4 of 4 independent trigger/reconnect/verify
cycles - the intermittent v1.15 behavior looks to have become fully
reliable. --reset was not retested since it's no longer needed as a
workaround.