111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Small coil on/off tester for the BE rig.
|
|
|
|
Defaults to the PFC relay (coil 12, "PFC_relay_enable") on the relay board,
|
|
but any coil address works via -a.
|
|
|
|
Usage:
|
|
python relay_test.py -p COM13 --on # close the relay
|
|
python relay_test.py -p COM13 --off # open it
|
|
python relay_test.py -p COM13 --read # read the coil back
|
|
python relay_test.py -p COM13 --toggle 5 -n 3 # 3 on/off cycles, 5s apart
|
|
python relay_test.py -p COM4 -a 0 --on # RCU charging cmd instead
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
import time
|
|
|
|
import minimalmodbus
|
|
|
|
SLAVE_ID = 0x10
|
|
BAUDRATE = 9600
|
|
SERIAL_TIMEOUT = 1.0
|
|
|
|
RELAY_CONTROL_CMD_ADDRESS = 12 # coil - PFC_relay_enable, 1 = on
|
|
|
|
|
|
def connect(port, slave_id):
|
|
inst = minimalmodbus.Instrument(port=port, slaveaddress=slave_id,
|
|
mode='rtu')
|
|
inst.serial.baudrate = BAUDRATE
|
|
inst.serial.timeout = SERIAL_TIMEOUT
|
|
inst.close_port_after_each_call = False
|
|
return inst
|
|
|
|
|
|
def read_coil(inst, address):
|
|
"""Read the coil back with function code 1. Not every board supports it."""
|
|
try:
|
|
return inst.read_bit(address, functioncode=1)
|
|
except Exception as exc:
|
|
return "unreadable (%s)" % exc
|
|
|
|
|
|
def write_coil(inst, address, value):
|
|
inst.write_bit(address, 1 if value else 0, functioncode=5)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Toggle a Modbus coil.")
|
|
parser.add_argument("-p", "--port", required=True,
|
|
help="COM port (e.g. COM13)")
|
|
parser.add_argument("-a", "--address", type=int,
|
|
default=RELAY_CONTROL_CMD_ADDRESS,
|
|
help="Coil address (default: %(default)s, the PFC "
|
|
"relay)")
|
|
parser.add_argument("-s", "--slave", type=lambda v: int(v, 0),
|
|
default=SLAVE_ID,
|
|
help="Slave id (default: 16)")
|
|
action = parser.add_mutually_exclusive_group(required=True)
|
|
action.add_argument("--on", action="store_true", help="Write 1")
|
|
action.add_argument("--off", action="store_true", help="Write 0")
|
|
action.add_argument("--read", action="store_true", help="Read the coil")
|
|
action.add_argument("--toggle", type=float, metavar="SECONDS",
|
|
help="Alternate on/off, holding each state this long")
|
|
parser.add_argument("-n", "--cycles", type=int, default=1,
|
|
help="Number of on/off cycles for --toggle "
|
|
"(default: %(default)s)")
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
inst = connect(args.port, args.slave)
|
|
except Exception as exc:
|
|
print("Connect failed on %s: %s" % (args.port, exc))
|
|
return 1
|
|
|
|
print("%s slave %d coil %d" % (args.port, args.slave, args.address))
|
|
|
|
try:
|
|
if args.read:
|
|
print(" read = %s" % read_coil(inst, args.address))
|
|
|
|
elif args.on or args.off:
|
|
value = bool(args.on)
|
|
write_coil(inst, args.address, value)
|
|
print(" wrote = %d" % value)
|
|
print(" read = %s" % read_coil(inst, args.address))
|
|
|
|
else:
|
|
for cycle in range(1, args.cycles + 1):
|
|
for value in (1, 0):
|
|
write_coil(inst, args.address, value)
|
|
print(" cycle %d: wrote %d, read %s"
|
|
% (cycle, value, read_coil(inst, args.address)))
|
|
time.sleep(args.toggle)
|
|
|
|
except KeyboardInterrupt:
|
|
print("Interrupted.")
|
|
except Exception as exc:
|
|
print(" FAILED: %s" % exc)
|
|
return 1
|
|
finally:
|
|
inst.serial.close()
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|