#!/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("--debug", action="store_true", help="print raw request/response bytes for every command") 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, debug=args.debug) 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()