2026-09-08 21:46:14 +08:00
|
|
|
# -*- 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)
|
|
|
|
|
|
2026-09-08 22:14:18 +08:00
|
|
|
# 1) BLE-MIDI framed (stable path)
|
2026-09-08 21:46:14 +08:00
|
|
|
await ble_write(
|
|
|
|
|
"midi-framed",
|
|
|
|
|
MIDI,
|
|
|
|
|
bytes.fromhex("8080F0600101F7"),
|
|
|
|
|
[MIDI, UARTN, EFF2],
|
|
|
|
|
)
|
|
|
|
|
await asyncio.sleep(2.0)
|
|
|
|
|
|
2026-09-08 22:14:18 +08:00
|
|
|
# E49A raw write often drops link / stops advertising — skip by default
|
|
|
|
|
if "--with-uartw" in sys.argv:
|
|
|
|
|
await ble_write(
|
|
|
|
|
"uartw-raw",
|
|
|
|
|
UARTW,
|
|
|
|
|
bytes.fromhex("F0600101F7"),
|
|
|
|
|
[UARTN, MIDI, EFF2],
|
|
|
|
|
)
|
|
|
|
|
await asyncio.sleep(2.0)
|
2026-09-08 21:46:14 +08:00
|
|
|
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())
|