From d079db8b37a5d474ebe8894f9d6d04e1e6096c18 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Thu, 2 Jul 2026 17:35:05 +0200 Subject: [PATCH] feat: add --reset and --trigger-error-state to hendi_ctrl_app.py --trigger-error-state deliberately reproduces the Hendi's ungraceful-disconnect lockout (remoteEnable(True) without a matching disable) so it can be tested on demand. --reset toggles the reset line, but per hardware testing it does NOT clear that lockout - only the device's physical knob/power cycle does. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GpePKZiEZWbGo9HrfuML6U --- scripts/hendi_ctrl_app.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/scripts/hendi_ctrl_app.py b/scripts/hendi_ctrl_app.py index e7762d7..ae234c6 100644 --- a/scripts/hendi_ctrl_app.py +++ b/scripts/hendi_ctrl_app.py @@ -41,6 +41,24 @@ def test_heater(hendi): hendi.setSwitch(0) +def trigger_error_state(hendi): + """Enables remote control and deliberately exits without disabling it + again - reproduces the Hendi's "not closed gracefully" lockout (its + built-in security feature that fails subsequent connect attempts) + on demand, instead of waiting for an actual crash to trigger it.""" + print("Triggering error state: enabling remote control, exiting without releasing it") + hendi.remoteEnable(True) + + +def reset(hendi): + """Toggles the reset line. Confirmed on hardware that this does NOT + clear the ungraceful-disconnect lockout (see trigger_error_state()) - + only the device's physical knob/power cycle does that.""" + print("Resetting Hendi (toggling DTR/RTS)") + hendi.reset() + time.sleep(1.0) + + def update_firmware(hendi, filename): hendi.firmware_update(filename) @@ -49,15 +67,20 @@ def main(): parser = ap.ArgumentParser(description=__doc__) parser.add_argument("--serial-port", default=DEFAULT_PORT) parser.add_argument("--baudrate", type=int, default=DEFAULT_BAUDRATE) + parser.add_argument("--reset", action="store_true", help="toggle the reset line (does not clear the ungraceful-disconnect lockout - use the device's knob for that)") parser.add_argument("--version", action="store_true", help="print software identifier/version") parser.add_argument("--capabilities", action="store_true", help="print power/digit capability info") parser.add_argument("--test-converter", action="store_true", help="round-trip the watts<->digits polynomials") parser.add_argument("--test-heater", action="store_true", help="drive the heater through its power range") + parser.add_argument("--trigger-error-state", action="store_true", + help="enable remote control and exit without disabling it, to deliberately reproduce the ungraceful-disconnect lockout") parser.add_argument("--update-firmware", metavar="FILE", help="flash the given firmware file") args = parser.parse_args() hendi = HendiCtrl(args.serial_port, args.baudrate) + if args.reset: + reset(hendi) if args.version: print_version(hendi) if args.capabilities: @@ -66,6 +89,8 @@ def main(): test_converter(hendi) if args.test_heater: test_heater(hendi) + if args.trigger_error_state: + trigger_error_state(hendi) if args.update_firmware: update_firmware(hendi, args.update_firmware)