initialize
This commit is contained in:
126
eg4battery/tmp/eg4-snapshot
Executable file
126
eg4battery/tmp/eg4-snapshot
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/home/noise/.local/share/uv/tools/powermon/bin/python
|
||||
"""eg4-snapshot — single-shot register dump for correlation against the
|
||||
EG4 BMS Tool UI. Prints a 47-row table with:
|
||||
|
||||
reg | raw u16 | hex | our decode | BMS Tool UI field
|
||||
----+---------+-----+------------------------------+-------------------
|
||||
|
||||
Run while the BMS Tool is open on the same pack; fill in the BMS Tool
|
||||
column by reading the matching values off the UI. Send the result back
|
||||
and we refine `decode_eg4_modbus_regs` in `bin/eg4-battery`.
|
||||
|
||||
Usage:
|
||||
eg4-snapshot # auto-detect FTDI, query 0x40
|
||||
eg4-snapshot <port> [addr] # explicit port / address
|
||||
"""
|
||||
import glob, sys, time, serial
|
||||
|
||||
PORT = sys.argv[1] if len(sys.argv) > 1 else None
|
||||
ADDR = int(sys.argv[2], 0) if len(sys.argv) > 2 else 0x40
|
||||
|
||||
if PORT is None:
|
||||
hits = sorted(glob.glob("/dev/serial/by-id/usb-FTDI_*"))
|
||||
if not hits:
|
||||
sys.exit("no FTDI adapter visible")
|
||||
PORT = hits[0]
|
||||
|
||||
|
||||
def crc16(data):
|
||||
c = 0xFFFF
|
||||
for b in data:
|
||||
c ^= b
|
||||
for _ in range(8):
|
||||
c = (c >> 1) ^ 0xA001 if c & 1 else c >> 1
|
||||
return c
|
||||
|
||||
|
||||
def modbus_read_47(addr):
|
||||
body = bytes([addr, 0x03, 0, 0, 0, 47])
|
||||
cr = crc16(body)
|
||||
return body + bytes([cr & 0xFF, cr >> 8])
|
||||
|
||||
|
||||
def _s16(v):
|
||||
return v - 0x10000 if v & 0x8000 else v
|
||||
|
||||
|
||||
# What we THINK each register means right now. Keep in sync with
|
||||
# bin/eg4-battery::decode_eg4_modbus_regs.
|
||||
MAP = [
|
||||
# (name, decode fn)
|
||||
("Total_Voltage", lambda v, regs: f"{v/100:.2f} V"),
|
||||
("Current_I (signed)", lambda v, regs: f"{_s16(v)/100:+.2f} A"),
|
||||
("Vol_Cell01 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell02 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell03 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell04 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell05 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell06 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell07 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell08 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell09 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell10 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell11 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell12 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell13 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell14 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell15 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Vol_Cell16 (mV)", lambda v, regs: f"{v/1000:.3f} V"),
|
||||
("Temp_01", lambda v, regs: f"{v} °C"),
|
||||
("Temp_02", lambda v, regs: f"{v} °C"),
|
||||
("Temp_03", lambda v, regs: f"{v} °C"),
|
||||
("Temp_04 (MOS?)", lambda v, regs: f"{v} °C"),
|
||||
("SOC", lambda v, regs: f"{v} %"),
|
||||
("SOH", lambda v, regs: f"{v} %"),
|
||||
("Temp_PCB", lambda v, regs: f"{v} °C"),
|
||||
("? reserved-25", lambda v, regs: f"{v}"),
|
||||
("? reserved-26", lambda v, regs: f"{v}"),
|
||||
("? reserved-27", lambda v, regs: f"{v}"),
|
||||
("? reserved-28", lambda v, regs: f"{v}"),
|
||||
("? reserved-29", lambda v, regs: f"{v}"),
|
||||
("Heater/Status", lambda v, regs: f"{v}"),
|
||||
("MAX_Curren", lambda v, regs: f"{v/100:.2f} A"),
|
||||
("? reg-32", lambda v, regs: f"{v} (0x{v:04x})"),
|
||||
("Warning bits", lambda v, regs: f"0x{v:04x} ({bin(v)[2:].zfill(16)})"),
|
||||
("Protection bits", lambda v, regs: f"0x{v:04x} ({bin(v)[2:].zfill(16)})"),
|
||||
("Error_Code", lambda v, regs: f"{v}"),
|
||||
("Cell_Num", lambda v, regs: f"{v}"),
|
||||
("Capacity", lambda v, regs: f"{v/10:.1f} Ah"),
|
||||
("Remaining", lambda v, regs: f"{v/100:.2f} Ah"),
|
||||
("CycleNum", lambda v, regs: f"{v}"),
|
||||
("Battery_Mode", lambda v, regs: f"{v}"),
|
||||
("BMS_Version (hi)", lambda v, regs: f"0x{v:04x}"),
|
||||
("BMS_Version (lo)", lambda v, regs: f"0x{v:04x}"),
|
||||
("? reg-43", lambda v, regs: f"{v}"),
|
||||
("? reg-44", lambda v, regs: f"{v}"),
|
||||
("? reg-45", lambda v, regs: f"{v}"),
|
||||
("uptime counter", lambda v, regs: f"{v}"),
|
||||
]
|
||||
|
||||
assert len(MAP) == 47, f"MAP has {len(MAP)} entries, expected 47"
|
||||
|
||||
|
||||
with serial.Serial(PORT, 9600, bytesize=8, parity="N", stopbits=1, timeout=0.3) as p:
|
||||
p.reset_input_buffer()
|
||||
p.write(modbus_read_47(ADDR))
|
||||
time.sleep(0.5)
|
||||
buf = p.read(512)
|
||||
|
||||
if len(buf) < 5 + 94 or buf[0] != ADDR or buf[1] != 0x03:
|
||||
sys.exit(f"bad response ({len(buf)} B): {buf.hex(' ')[:80]}")
|
||||
|
||||
bc = buf[2]
|
||||
data = buf[3:3 + bc]
|
||||
regs = [(data[i] << 8) | data[i + 1] for i in range(0, bc, 2)]
|
||||
|
||||
print(f"# eg4-snapshot port={PORT} addr=0x{ADDR:02x} time={time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print()
|
||||
print(f"{'reg':>4} {'raw':>6} {'hex':>7} {'our decode':<34} BMS Tool UI value (fill me in)")
|
||||
print(f"{'---':>4} {'-----':>6} {'-----':>7} {'-'*34} {'-'*40}")
|
||||
for i, (label, fn) in enumerate(MAP):
|
||||
raw = regs[i]
|
||||
try:
|
||||
dec = fn(raw, regs)
|
||||
except Exception as e:
|
||||
dec = f"(decode error: {e})"
|
||||
print(f"{i:>4} {raw:>6} 0x{raw:04x} {label:<34} {dec}")
|
||||
Reference in New Issue
Block a user