67 lines
2.0 KiB
Python
Executable File
67 lines
2.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Build BullTron/Daly-style BLE frames observed in the Android app."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
|
|
|
|
def crc16_modbus_swapped(hex_data: str) -> str:
|
|
data = bytes.fromhex(hex_data)
|
|
crc = 0xFFFF
|
|
for byte in data:
|
|
crc ^= byte
|
|
for _ in range(8):
|
|
if crc & 1:
|
|
crc = (crc >> 1) ^ 0xA001
|
|
else:
|
|
crc >>= 1
|
|
swapped = ((crc & 0xFF00) >> 8) | ((crc & 0x00FF) << 8)
|
|
return f"{swapped:04X}"
|
|
|
|
|
|
def read_frame(start: int, count: int) -> str:
|
|
body = f"D203{start:04X}{count:04X}"
|
|
return body + crc16_modbus_swapped(body)
|
|
|
|
|
|
def write_single_frame(register: int, value: int) -> str:
|
|
body = f"D206{register:04X}{value:04X}"
|
|
return body + crc16_modbus_swapped(body)
|
|
|
|
|
|
def write_multi_frame(start: int, count: int, payload_hex: str) -> str:
|
|
payload = payload_hex.replace(" ", "").upper()
|
|
body = f"D210{start:04X}{count:04X}{payload}"
|
|
return body + crc16_modbus_swapped(body)
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
|
|
p_read = sub.add_parser("read")
|
|
p_read.add_argument("start", type=lambda x: int(x, 0))
|
|
p_read.add_argument("count", type=lambda x: int(x, 0))
|
|
|
|
p_write = sub.add_parser("write")
|
|
p_write.add_argument("register", type=lambda x: int(x, 0))
|
|
p_write.add_argument("value", type=lambda x: int(x, 0))
|
|
|
|
p_multi = sub.add_parser("write-multi")
|
|
p_multi.add_argument("start", type=lambda x: int(x, 0))
|
|
p_multi.add_argument("count", type=lambda x: int(x, 0))
|
|
p_multi.add_argument("payload_hex")
|
|
|
|
args = parser.parse_args()
|
|
if args.cmd == "read":
|
|
print(read_frame(args.start, args.count))
|
|
elif args.cmd == "write":
|
|
print(write_single_frame(args.register, args.value))
|
|
elif args.cmd == "write-multi":
|
|
print(write_multi_frame(args.start, args.count, args.payload_hex))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|