--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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GpePKZiEZWbGo9HrfuML6U
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""CLI tool for talking to a Hendi induction plate over serial - see
|
|
components/actor/hendiCtrl.py for the protocol/HendiCtrl class this wraps."""
|
|
|
|
import argparse as ap
|
|
import time
|
|
|
|
from components.actor.hendiCtrl import HendiCtrl
|
|
|
|
DEFAULT_PORT = '/dev/ttyUSB0'
|
|
DEFAULT_BAUDRATE = 115200
|
|
|
|
|
|
def print_version(hendi):
|
|
print(f"{hendi.getSoftwareIdentifier()}, F/W-Version: {hendi.getSoftwareVersion()}")
|
|
|
|
|
|
def print_capabilities(hendi):
|
|
print("Capabilities:", hendi.getCapabilties())
|
|
|
|
|
|
def test_converter(hendi):
|
|
print("Test converters")
|
|
for pwr in range(500, 3600, 100):
|
|
digits = hendi.toDigits(pwr)
|
|
power = hendi.toWatts(digits)
|
|
print(f"pwr={pwr} digits={digits} power={power}")
|
|
|
|
|
|
def test_heater(hendi):
|
|
print("Test Hendi")
|
|
with hendi.remote_open():
|
|
hendi.setSwitch(1)
|
|
time.sleep(1.0)
|
|
for pwr in range(500, 3600, 100):
|
|
hendi.setPowerWatts(pwr)
|
|
pw = hendi.getPowerWatts()
|
|
print("power_w", pw)
|
|
time.sleep(1.0)
|
|
|
|
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)
|
|
|
|
|
|
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:
|
|
print_capabilities(hendi)
|
|
if args.test_converter:
|
|
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)
|
|
|
|
print("End of program.")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|