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:
parent
7b08f5e098
commit
0aedb45e6e
|
|
@ -112,7 +112,8 @@ static const BLE_SysExCmdItem bleSysExCmdTable[] =
|
||||||
{{0x01, 0x0A}, handleAutoPowerOff}, /* auto power-off read (minutes) */
|
{{0x01, 0x0A}, handleAutoPowerOff}, /* auto power-off read (minutes) */
|
||||||
{{0x01, 0x0C}, handleUserFwVer}, /* user fw version */
|
{{0x01, 0x0C}, handleUserFwVer}, /* user fw version */
|
||||||
{{0x01, 0x11}, handleAutoPowerOffSet}, /* auto power-off set (minutes) */
|
{{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 ========*/
|
/*======== 2. chord map 0x02 ========*/
|
||||||
{{0x02, 0x01}, handleReadRhythmMap}, /* read chord/pitch map */
|
{{0x02, 0x01}, handleReadRhythmMap}, /* read chord/pitch map */
|
||||||
|
|
@ -646,10 +647,22 @@ static void handleOtherInfo(uint8_t *data)
|
||||||
|
|
||||||
static void handleDevCode(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;
|
(void)data;
|
||||||
memcpy(&resp[4], (const void *)AT32_UID_BASE, 12);
|
resp[0] = 0xF0;
|
||||||
resp[16] = 0xF7;
|
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));
|
USART4_SendData(resp, sizeof(resp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -686,14 +699,19 @@ static void handleAutoPowerOffSet(uint8_t *data)
|
||||||
|
|
||||||
static void handleFwCode(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 resp[16];
|
||||||
uint8_t len = (uint8_t)strlen(fwname);
|
uint8_t len = (uint8_t)strlen(fwname);
|
||||||
(void)data;
|
(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);
|
memcpy(&resp[4], fwname, len);
|
||||||
resp[4 + len] = 0xF7;
|
resp[4 + len] = 0xF7;
|
||||||
USART4_SendData(resp, (uint16_t)(4 + len + 1));
|
USART4_SendData(resp, (uint16_t)(4u + len + 1u));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------- 2. chord map 0x02 --------*/
|
/*-------- 2. chord map 0x02 --------*/
|
||||||
|
|
|
||||||
|
|
@ -163,12 +163,21 @@ def run_tests(sim, allow_poweroff=False):
|
||||||
R.add("01 06 其他信息", "PASS" if ok else "FAIL", d)
|
R.add("01 06 其他信息", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
r = sim.query(build(0x01, 0x07))
|
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.add("01 07 设备编码(UID)", "PASS" if ok else "FAIL", d)
|
||||||
|
|
||||||
r = sim.query(build(0x01, 0xFF))
|
# 01 0F:BLE-MIDI 安全子命令(原 01 FF 的 0xFF 非法出现在 SysEx 数据中)
|
||||||
ok, d = expect(r, (0x60, 0x01, 0xFF))
|
r = sim.query(build(0x01, 0x0F))
|
||||||
R.add("01 FF 固件编码", "PASS" if ok else "FAIL", d)
|
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 -> 读验证 -> 恢复原值
|
# 自动关机: 读 -> 设15 -> 读验证 -> 恢复原值
|
||||||
r0 = sim.query(build(0x01, 0x0A))
|
r0 = sim.query(build(0x01, 0x0A))
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,28 @@ def ble_midi_encode_sysex(frame: bytes, max_payload: int):
|
||||||
|
|
||||||
|
|
||||||
def ble_midi_decode_packet(payload: bytes) -> bytes:
|
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""
|
return b""
|
||||||
i = 1
|
i = 0
|
||||||
if payload[i] & 0x80:
|
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
|
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:
|
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 []:
|
for note in notes or []:
|
||||||
lines.append(f"- {note}")
|
lines.append(f"- {note}")
|
||||||
lines += [
|
lines += [
|
||||||
"- `01 03` 为 MCU 实际版本编码;`01 FF` 暂 `BRS08L`(文档待定)。",
|
"- `01 03` 为 MCU 实际版本;`01 0F` 固件编码暂 `BRS08L`(原 `01 FF`,BLE-MIDI 下 0xFF 非法)。",
|
||||||
|
"- `01 07` UID 为 24 字符 hex ASCII(96-bit),保证 SysEx 7-bit 安全。",
|
||||||
"- `04 xx`/`06 xx` 无应答,SENT 表示已发送。",
|
"- `04 xx`/`06 xx` 无应答,SENT 表示已发送。",
|
||||||
"- `FD 01` 升级指令不在本协议范围。",
|
"- `FD 01` 升级指令不在本协议范围。",
|
||||||
]
|
]
|
||||||
|
|
@ -369,8 +386,8 @@ def main():
|
||||||
)
|
)
|
||||||
notes = [
|
notes = [
|
||||||
f"transport={sim.info.get('transport')}",
|
f"transport={sim.info.get('transport')}",
|
||||||
"若 FAIL 且 timeout: 检查手机 App 是否占用、Windows 是否残留配对(--unpair)、吉他是否需完全断电重启。",
|
"若 FAIL 且 timeout: 检查手机 App 是否占用、Windows 是否残留配对(--unpair)。",
|
||||||
"实测: 标准 BLE-MIDI 可稳定连接但 SysEx 无应答; 自定义 uart 写 raw SysEx 会断连。",
|
"BLE-MIDI Notify 中的时间戳字节已在解码时剥离。",
|
||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
R = smoke(sim) if args.smoke else run_tests(sim, allow_poweroff=args.allow_poweroff)
|
R = smoke(sim) if args.smoke else run_tests(sim, allow_poweroff=args.allow_poweroff)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue