Make BLE SysEx UID and fw-code MIDI-safe for BLE-MIDI.

Encode 01 07 UID as hex ASCII and use 01 0F (keep 01 FF alias) so high-bit bytes are not dropped as timestamps.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-08 22:02:06 +08:00
parent 7b08f5e098
commit 0aedb45e6e
3 changed files with 62 additions and 18 deletions

View File

@ -112,7 +112,8 @@ static const BLE_SysExCmdItem bleSysExCmdTable[] =
{{0x01, 0x0A}, handleAutoPowerOff}, /* auto power-off read (minutes) */
{{0x01, 0x0C}, handleUserFwVer}, /* user fw version */
{{0x01, 0x11}, handleAutoPowerOffSet}, /* auto power-off set (minutes) */
{{0x01, 0xFF}, handleFwCode}, /* fw code */
{{0x01, 0x0F}, handleFwCode}, /* fw code (MIDI/BLE-safe; replaces 01 FF) */
{{0x01, 0xFF}, handleFwCode}, /* fw code alias (raw UART only; 0xFF illegal in BLE-MIDI SysEx) */
/*======== 2. chord map 0x02 ========*/
{{0x02, 0x01}, handleReadRhythmMap}, /* read chord/pitch map */
@ -646,10 +647,22 @@ static void handleOtherInfo(uint8_t *data)
static void handleDevCode(uint8_t *data)
{
uint8_t resp[17] = {0xF0, 0x60, 0x01, 0x07};
/* 96-bit UID as 24 hex ASCII — SysEx data must stay 7-bit for BLE-MIDI */
static const char hex[] = "0123456789ABCDEF";
uint8_t resp[29];
const uint8_t *uid = (const uint8_t *)AT32_UID_BASE;
uint8_t i;
(void)data;
memcpy(&resp[4], (const void *)AT32_UID_BASE, 12);
resp[16] = 0xF7;
resp[0] = 0xF0;
resp[1] = 0x60;
resp[2] = 0x01;
resp[3] = 0x07;
for (i = 0; i < 12u; i++)
{
resp[4u + 2u * i] = (uint8_t)hex[uid[i] >> 4];
resp[4u + 2u * i + 1u] = (uint8_t)hex[uid[i] & 0x0Fu];
}
resp[28] = 0xF7;
USART4_SendData(resp, sizeof(resp));
}
@ -686,14 +699,19 @@ static void handleAutoPowerOffSet(uint8_t *data)
static void handleFwCode(uint8_t *data)
{
/* 01 FF 固件编码: product string TBD; interim reply uses NVM fwname ("BRS08L") */
/* 固件编码: 请求 01 0F推荐或 01 FF仅裸串口应答统一 01 0F + ASCII避免 BLE-MIDI 吃掉 0xFF */
uint8_t resp[16];
uint8_t len = (uint8_t)strlen(fwname);
(void)data;
resp[0] = 0xF0; resp[1] = 0x60; resp[2] = 0x01; resp[3] = 0xFF;
if (len > 10u)
len = 10u;
resp[0] = 0xF0;
resp[1] = 0x60;
resp[2] = 0x01;
resp[3] = 0x0F;
memcpy(&resp[4], fwname, len);
resp[4 + len] = 0xF7;
USART4_SendData(resp, (uint16_t)(4 + len + 1));
USART4_SendData(resp, (uint16_t)(4u + len + 1u));
}
/*-------- 2. chord map 0x02 --------*/

View File

@ -163,12 +163,21 @@ def run_tests(sim, allow_poweroff=False):
R.add("01 06 其他信息", "PASS" if ok else "FAIL", d)
r = sim.query(build(0x01, 0x07))
ok, d = expect(r, (0x60, 0x01, 0x07), 17)
ok, d = expect(r, (0x60, 0x01, 0x07), 29) # 12B UID → 24 hex ASCII
if ok:
hx = bytes(r[4:-1])
ok = len(hx) == 24 and all(c in b"0123456789ABCDEF" for c in hx)
d += f" uid_hex={hx.decode('ascii', 'replace')}"
R.add("01 07 设备编码(UID)", "PASS" if ok else "FAIL", d)
r = sim.query(build(0x01, 0xFF))
ok, d = expect(r, (0x60, 0x01, 0xFF))
R.add("01 FF 固件编码", "PASS" if ok else "FAIL", d)
# 01 0FBLE-MIDI 安全子命令(原 01 FF 的 0xFF 非法出现在 SysEx 数据中)
r = sim.query(build(0x01, 0x0F))
ok, d = expect(r, (0x60, 0x01, 0x0F))
if ok:
code = bytes(r[4:-1])
ok = len(code) > 0 and all(0x20 <= c < 0x7F for c in code)
d += f" code={code!r}"
R.add("01 0F 固件编码", "PASS" if ok else "FAIL", d)
# 自动关机: 读 -> 设15 -> 读验证 -> 恢复原值
r0 = sim.query(build(0x01, 0x0A))

View File

@ -61,12 +61,28 @@ def ble_midi_encode_sysex(frame: bytes, max_payload: int):
def ble_midi_decode_packet(payload: bytes) -> bytes:
if len(payload) < 2:
"""Strip BLE-MIDI header/timestamp bytes; keep App SysEx (F0…F7).
- Framed: [header ts][optional ts][F0 ts F7]
- Raw (some ATS2853 notifies): [F0 F7] must not treat F0 as header
(F0/F7 also have bit7=1).
"""
if not payload:
return b""
i = 1
if payload[i] & 0x80:
i = 0
if payload[0] not in (0xF0, 0xF7) and (payload[0] & 0x80):
i = 1 # BLE-MIDI header
out = bytearray()
while i < len(payload):
b = payload[i]
i += 1
return bytes(payload[i:])
if b in (0xF0, 0xF7):
out.append(b)
elif b & 0x80:
continue # timestamp inside/around SysEx
else:
out.append(b)
return bytes(out)
class BleMidiSim:
@ -292,7 +308,8 @@ def write_report(R: Results, info: dict, path: str, notes: list[str] | None = No
for note in notes or []:
lines.append(f"- {note}")
lines += [
"- `01 03` 为 MCU 实际版本编码;`01 FF` 暂 `BRS08L`(文档待定)。",
"- `01 03` 为 MCU 实际版本;`01 0F` 固件编码暂 `BRS08L`(原 `01 FF`BLE-MIDI 下 0xFF 非法)。",
"- `01 07` UID 为 24 字符 hex ASCII96-bit保证 SysEx 7-bit 安全。",
"- `04 xx`/`06 xx` 无应答SENT 表示已发送。",
"- `FD 01` 升级指令不在本协议范围。",
]
@ -369,8 +386,8 @@ def main():
)
notes = [
f"transport={sim.info.get('transport')}",
"若 FAIL 且 timeout: 检查手机 App 是否占用、Windows 是否残留配对(--unpair)、吉他是否需完全断电重启",
"实测: 标准 BLE-MIDI 可稳定连接但 SysEx 无应答; 自定义 uart 写 raw SysEx 会断连",
"若 FAIL 且 timeout: 检查手机 App 是否占用、Windows 是否残留配对(--unpair)",
"BLE-MIDI Notify 中的时间戳字节已在解码时剥离",
]
try:
R = smoke(sim) if args.smoke else run_tests(sim, allow_poweroff=args.allow_poweroff)