166 lines
5.9 KiB
Python
Executable File
166 lines
5.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from sensor import Sensor
|
|
from sys import argv,exit
|
|
|
|
def print_help():
|
|
print( \
|
|
f'''{argv[0]} ADDR [BAUDRATE] [{{read [REGISTER ...] | write VALUE REGISTER | reset}}]
|
|
where:
|
|
- ADDR is Modbus address (use 0 for broadcast; note that only write can be broadcast)
|
|
- BAUDRATE is (optional) baud rate; default value is 19200
|
|
- action can be "read", "write", "reset" or it can be omitted completely (defaults to "read all")
|
|
- REGISTER can be either human-friendly register name (e.g. "CO2") or register number (30001)
|
|
- read may have list of REGISTER arguments (combining both register numbers and names)
|
|
- VALUE is 16-bit integer in decimal format
|
|
- to get list of human-friendly names just read all registers from sensor (run script with address only)
|
|
|
|
EXAMPLES
|
|
|
|
Read all registers from sensor 247, default baud rate:
|
|
{argv[0]} 247
|
|
Read only CO2 register from sensor 123, baudrate 4800:
|
|
{argv[0]} 123 4800 read CO2
|
|
Read CO2, T_SHT4x and RH_SCD4x from sensor 222, default baudrate:
|
|
{argv[0]} 222 read CO2 T_SHT4x RH_SCD4x
|
|
Read register 30010 from sensor 247, default baudrate:
|
|
{argv[0]} 247 read 30010
|
|
Read registers 30011, 30012, 30020 from sensor 247, baudrate 19200:
|
|
{argv[0]} 247 19200 read 30011 30012 30020
|
|
Turn LED off for sensor 247, baud 115200:
|
|
{argv[0]} 247 115200 write LED_on 0
|
|
Set brightness of all connected sensors to 50%, default baudrate (broadcast message):
|
|
{argv[0]} 0 write LED_brightness 50
|
|
Reset device 247, baud 19200:
|
|
{argv[0]} 247 19200 reset
|
|
Reset all devices, default baudrate:
|
|
{argv[0]} 0 reset
|
|
''')
|
|
|
|
# default values
|
|
DEFAULT_BAUDRATE = 19200
|
|
DEFAULT_ACTION = 'all'
|
|
|
|
# first positional argument (ADDR) is necessary
|
|
arg_index = 1
|
|
try:
|
|
addr = int(argv[arg_index])
|
|
except IndexError:
|
|
print('Argument needed: ADDR')
|
|
print_help()
|
|
exit(-1)
|
|
except ValueError:
|
|
print(f'{argv[arg_index]} is invalid form for argument ADDR')
|
|
print_help()
|
|
exit(-2)
|
|
# second argument might be either BAUDRATE, read or write (or nothing)
|
|
arg_index += 1
|
|
action = None
|
|
register_number = []
|
|
register_name = []
|
|
baudrate = None
|
|
value = None
|
|
while arg_index < len(argv):
|
|
if not baudrate:
|
|
try:
|
|
baudrate = int(argv[arg_index])
|
|
except ValueError:
|
|
baudrate = DEFAULT_BAUDRATE
|
|
continue
|
|
elif not action:
|
|
action = argv[arg_index]
|
|
else:
|
|
# if action is write, last argument should be value
|
|
if action == 'write' and arg_index == len(argv) - 1:
|
|
try:
|
|
value = int(argv[arg_index])
|
|
except ValueError:
|
|
print(f'Wrong format for argument "value"')
|
|
exit(-3)
|
|
else:
|
|
# rest of the arguments should be register numbers/names
|
|
try:
|
|
register_number.append(int(argv[arg_index]))
|
|
except ValueError:
|
|
register_name.append(argv[arg_index])
|
|
arg_index += 1
|
|
|
|
|
|
# sanity check
|
|
if not action:
|
|
action = 'all'
|
|
if action != 'read' and action != 'write' and action != 'all' and action != 'reset':
|
|
print(f'Unknown action {action}')
|
|
exit(-4)
|
|
if action == 'write' and value == None:
|
|
print(f'Action "write" needs value')
|
|
exit(-5)
|
|
if action == 'write' and len(register_number) + len(register_name) != 1:
|
|
print(f'Action "write" needs exactly one register to write to')
|
|
exit(-6)
|
|
if not baudrate:
|
|
baudrate = DEFAULT_BAUDRATE
|
|
if action != 'write' and len(register_name) + len(register_number) == 0:
|
|
input_registers = [ x for x in Sensor.input_registers.keys() ]
|
|
holding_registers = [ x for x in Sensor.holding_registers.keys() ]
|
|
register_name = input_registers + holding_registers
|
|
if addr == 0 and action != 'write' and action != 'reset':
|
|
print(f'Cannot broadcast action "{action}"')
|
|
exit(-12)
|
|
|
|
# Query device
|
|
print('---- Query device ----')
|
|
print(f'Address: {addr if addr != 0 else "0 (broadcast)"}\nBaudrate: {baudrate}\nAction: {action}')
|
|
if action == 'write':
|
|
print(f'Value to be written: {value}')
|
|
# open device
|
|
s = Sensor(address=addr, baudrate=baudrate)
|
|
all_registers = Sensor.input_registers.copy()
|
|
all_registers.update(Sensor.holding_registers)
|
|
all_register_numbers = {}
|
|
for name, number in all_registers.items():
|
|
all_register_numbers[number] = name
|
|
if action == 'read' or action == 'all':
|
|
print('---- Register readout ----')
|
|
for register in register_name + register_number:
|
|
signed = False
|
|
if isinstance(register, str):
|
|
reg_name = register
|
|
if reg_name in all_registers:
|
|
reg_number = all_registers[reg_name]
|
|
else:
|
|
print(f'Register name {register} not known')
|
|
exit(-7)
|
|
else:
|
|
reg_number = register
|
|
# try reverse lookup
|
|
reg_name = all_register_numbers[reg_number] if reg_number in all_register_numbers else ''
|
|
try:
|
|
signed = '_signed' in reg_name
|
|
result = int(s.read_register(reg_number, signed=signed))
|
|
except:
|
|
result = 'N/A'
|
|
print(f'{reg_number : <10} {result : <10} {reg_name}')
|
|
elif action == 'write':
|
|
if len(register_name) > 0:
|
|
if register_name[0] not in Sensor.holding_registers:
|
|
print(f'Register {register_name[0]} does not exist or is not holding register')
|
|
exit(-9)
|
|
reg_number = Sensor.holding_registers[register_name[0]]
|
|
elif len(register_number) > 0:
|
|
reg_number = register_number[0]
|
|
print('---- Register write ----')
|
|
try:
|
|
s.write_register(reg_number, value)
|
|
print(f'{reg_number : <10} {int(value) : <10}')
|
|
except ValueError:
|
|
print(f'Register number {reg_number} cannot be written')
|
|
exit(-9)
|
|
elif action == 'reset':
|
|
if not s.reset() and addr != 0:
|
|
# reset should throw NoResponse; if it doesn't, reset failed (broadcast shouldn't return anything)
|
|
print('Device reset failed!')
|
|
else:
|
|
print('---- Device reset ----')
|
|
|
|
exit(0)
|