#!/usr/bin/env python3 """GPIO_MAP 候选实证探针 v2(单 TCP/单 BLE 会话;候选间 SPOTA 复位)。 安全:max_blocks=2 不写 END;MEM_DEV FF 复位会话;断连即弃。 用法:.venv/bin/python tools/gpio_probe.py [host] [port] """ import asyncio import json import sys import time HOST = sys.argv[1] if len(sys.argv) > 1 else "192.168.61.35" PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 8971 DEVICE = "18:BC:5A:5D:BF:28" IMAGE_HEX = open("analysis/firmware/fw_tst.img", "rb").read().hex() CANDIDATES = [ ("A 现行 {00,00,06,05}", "00000605"), ("B LE(0x05060040) {40,00,06,05}", "40000605"), ("C BE(0x05060040) {05,06,00,40}", "05060040"), ] RESET_MEM_DEV = "000000ff" class Bridge: def __init__(self): self.next_id = 0 async def open(self): self.r, self.w = await asyncio.open_connection(HOST, PORT) async def rpc(self, op, timeout=120, **kw): self.next_id += 1 rid = self.next_id self.w.write(json.dumps({"id": rid, "op": op, **kw}).encode() + b"\n") await self.w.drain() events = [] while True: line = await asyncio.wait_for(self.r.readline(), timeout) if not line: raise ConnectionError("bridge EOF") msg = json.loads(line) if "event" in msg: events.append(msg) else: return msg, events async def connect_until_awake(self, tries=12): for i in range(tries): resp, _ = await self.rpc("connect", 150, address=DEVICE, timeout=30, subscribe=False) if resp.get("ok"): print(f"连接成功(第 {i + 1} 次)") return True print(f" 等待设备唤醒... ({i + 1}/{tries})") await asyncio.sleep(15) return False async def main(): b = Bridge() await b.open() if not await b.connect_until_awake(): print("设备持续未醒,退出") return for name, gh in CANDIDATES: t0 = time.time() resp, events = await b.rpc("ota", 180, image=IMAGE_HEX, gpio_map=gh, max_blocks=2) codes, confirmed = [], 0 for ev in events: st = ev.get("stage", "") if st.endswith("_status") or "info_status" in st: codes += ev.get("codes", []) if ev.get("event") == "ota_progress": confirmed = ev["block"] if confirmed: verdict = f"✅ 块{confirmed} 确认(引脚正确!)" elif resp.get("ok"): verdict = "探针停(无块确认)" else: verdict = f"❌ {resp.get('error')}" print(f"{name}: {verdict} 状态码={codes} ({time.time() - t0:.1f}s)") # SPOTA 会话复位,避免缓冲跨候选累积 await b.rpc("write", 30, char="spota_mem_dev", data=RESET_MEM_DEV, response=True) await asyncio.sleep(2) print("探针完成") asyncio.run(main())