#!/usr/bin/env python3 """Sanity-check byte-level probe for EG4 LifePower4 V2 over USB-RS485. Sends the 'general status' frame and prints the raw reply. If you see bytes starting with 0x7E coming back, the protocol is confirmed and eg4_lifepower.py will decode them. Usage: python3 probe.py # auto-detect likely USB-serial port python3 probe.py /dev/tty.usbserial-XXX # explicit port python3 probe.py COM5 # Windows """ import sys import time import glob import serial # pip install pyserial BAUD = 9600 CMDS = { "general": bytes.fromhex("7E01010000FE0D"[:-2] + "0D"), # 7E 01 01 00 FE 0D "hw_ver": bytes.fromhex("7E01420000FC0D"[:-2] + "0D"), # 7E 01 42 00 FC 0D "fw_ver": bytes.fromhex("7E01330000FE0D"[:-2] + "0D"), # 7E 01 33 00 FE 0D } # (the [:-2]+"0D" dance above is just to keep the literal aligned with the # canonical 6-byte frames documented upstream; simpler form below) CMDS = { "general": bytes([0x7E, 0x01, 0x01, 0x00, 0xFE, 0x0D]), "hw_ver": bytes([0x7E, 0x01, 0x42, 0x00, 0xFC, 0x0D]), "fw_ver": bytes([0x7E, 0x01, 0x33, 0x00, 0xFE, 0x0D]), } def autodetect() -> str | None: candidates = ( glob.glob("/dev/tty.usbserial*") # macOS FTDI/CH340 + glob.glob("/dev/tty.usbmodem*") # macOS CDC-ACM + glob.glob("/dev/ttyUSB*") # Linux FTDI/CH340 + glob.glob("/dev/ttyACM*") # Linux CDC-ACM ) return candidates[0] if candidates else None def probe(port: str) -> None: print(f"opening {port} @ {BAUD} 8N1") with serial.Serial(port, BAUD, bytesize=8, parity="N", stopbits=1, timeout=1.5) as s: for name, cmd in CMDS.items(): s.reset_input_buffer() s.write(cmd) time.sleep(0.2) reply = s.read(256) print(f"\n[{name}] sent {cmd.hex(' ')}") if reply: print(f" got ({len(reply)} bytes) {reply.hex(' ')}") if reply[0] == 0x7E and reply[-1] == 0x0D: print(" -> frame looks valid (7E ... 0D)") else: print(" got (nothing — timeout)") if __name__ == "__main__": port = sys.argv[1] if len(sys.argv) > 1 else autodetect() if not port: sys.exit("no serial port found; pass one explicitly: python3 probe.py /dev/tty.usbserial-XXXX") probe(port)