# -*- coding: utf-8 -*- """Flash is separate; this attaches RTT then writes BLE SysEx and prints MCU logs.""" from __future__ import annotations import asyncio import sys import threading import time import pylink from bleak import BleakClient, BleakScanner NAME = "Smart Guitar MIDI" MIDI = "7772e5db-3868-4112-a1a9-f2669d106bf3" UARTW = "e49a25e0-f69a-11e8-8eb2-f2801f1b9fd1" UARTN = "e49a28e1-f69a-11e8-8eb2-f2801f1b9fd1" EFF2 = "0000eff2-0000-1000-8000-00805f9b34fb" def open_jlink(): j = pylink.JLink() j.open() try: j.exec_command("HideDeviceSelection = 1") except Exception: pass j.set_tif(pylink.enums.JLinkInterfaces.SWD) last = None for dev in ("Cortex-M4", "AT32F403AC", "AT32F403A"): try: try: j.exec_command(f"Device = {dev}") except Exception: pass j.connect(dev) print("JLink OK", dev, flush=True) try: j.restart(halt=False) except Exception: try: j.go() except Exception: pass return j except Exception as e: last = e raise SystemExit(f"JLink fail: {last}") async def find_dev(timeout=45.0): t0 = time.time() while time.time() - t0 < timeout: d = await BleakScanner.find_device_by_filter( lambda d, a: d.name and NAME.lower() in d.name.lower(), timeout=8 ) if d: return d print("wait BLE...", flush=True) return None async def ble_write(tag, write_uuid, payload, notify_uuids): d = await find_dev() if not d: print("NO BLE", flush=True) return print(f"BLE {d.address} write {tag} {payload.hex()}", flush=True) try: c = BleakClient(d.address, timeout=20) await c.connect() try: await c.unpair() except Exception: pass await c.disconnect() except Exception: pass await asyncio.sleep(1.0) d = await find_dev() or d notifs = [] async with BleakClient(d, timeout=25) as c: def cb(_s, data): notifs.append(bytes(data)) print("GATT NOTIFY", data.hex(), flush=True) for u in notify_uuids: try: await c.start_notify(u, cb) except Exception as e: print("notify fail", u[:8], e, flush=True) await asyncio.sleep(0.2) try: await c.write_gatt_char(write_uuid, payload, response=False) print("GATT write ok, conn", c.is_connected, flush=True) except Exception as e: print("GATT write fail", e, flush=True) await asyncio.sleep(3.0) print("GATT notifs", len(notifs), "conn", c.is_connected, flush=True) async def main(): sys.stdout.reconfigure(encoding="utf-8", errors="replace") j = open_jlink() # Give RTT control block time; try start repeatedly for i in range(10): try: j.rtt_start(block_address=0x20016D68) st = j.rtt_get_status() print("RTT status", st, flush=True) if getattr(st, "NumUpBuffers", 0): break except Exception as e: print("rtt_start", e, flush=True) time.sleep(0.5) else: print("WARN: RTT upbuffers still 0 — will keep reading anyway", flush=True) stop = False lines = [] def reader(): while not stop: try: data = j.rtt_read(0, 4096) if data: s = bytes(data).decode("utf-8", "replace") lines.append(s) print("RTT>", s, end="" if s.endswith("\n") else "\n", flush=True) except Exception as e: print("rtt read err", e, flush=True) break time.sleep(0.03) th = threading.Thread(target=reader, daemon=True) th.start() print("collect 4s boot/idle logs...", flush=True) await asyncio.sleep(4.0) if not lines: print("WARNING: no RTT yet — touch screen or wait; continuing BLE test", flush=True) # 1) BLE-MIDI framed (stable path historically) await ble_write( "midi-framed", MIDI, bytes.fromhex("8080F0600101F7"), [MIDI, UARTN, EFF2], ) await asyncio.sleep(2.0) # 2) custom uart raw (may drop link) await ble_write( "uartw-raw", UARTW, bytes.fromhex("F0600101F7"), [UARTN, MIDI, EFF2], ) await asyncio.sleep(2.0) stop = True time.sleep(0.4) j.close() text = "".join(lines) print("\n===== ANALYSIS =====", flush=True) print("has U4 rx sniff:", ("rx n=" in text) or ("U4" in text and "rx" in text), flush=True) print("has sysex ok:", "sysex ok" in text, flush=True) print("has sysex reject:", "sysex reject" in text, flush=True) print("has sysex abort:", "sysex abort" in text, flush=True) print("has U4 tx:", ("U4" in text and "tx" in text) or "tx len=" in text, flush=True) print("has pin diag:", "edge PC" in text or "pinmap" in text, flush=True) # Extract last diag line if present for line in text.splitlines(): if "edge PC10=" in line: print("diag:", line.strip(), flush=True) if "sysex ok" in text and "tx len=" in text: print("VERDICT: MCU got cmd and replied on UART4 → GATT notify path broken in BLE module", flush=True) elif "rx n=" in text and "sysex ok" not in text: print("VERDICT: UART4 got bytes but frame not accepted → protocol/framing", flush=True) elif "edge PC12=" in text and "isr_rx=0" in text: # SCH puts B_UART4_RX on PC12; FW UART4 RX is PC11 print("VERDICT: activity on PC12 while UART4 ISR idle → driver pinmap vs SCH (MCU底层)", flush=True) elif "edge PC11=" in text and "isr_rx=0" in text: print("VERDICT: edges on PC11 but no UART ISR → baud/noise or not UART framing", flush=True) elif "isr_rx=0" in text and "edge PC10=0" in text and "edge PC11=0" in text and "edge PC12=0" in text: print("VERDICT: no GPIO edges + no UART ISR → ATS2853 did not drive UART (模组固件/桥接)", flush=True) elif "U4" not in text and "rx" not in text: print("VERDICT: no UART4 activity → BLE module did not forward GATT write to UART4 (or RTT dead)", flush=True) else: print("VERDICT: inconclusive — see RTT dump above", flush=True) if __name__ == "__main__": asyncio.run(main())