JavaScript向BLE设备写入长十六进制数组报GATT错误如何解决
结论
完全可以通过JavaScript向BLE设备发送长十六进制数组,触发NotSupportedError: GATT Error Unknown报错的原因不是JavaScript语言能力限制,是未遵循BLE GATT传输规则导致的。
报错原因
BLE链路初始连接时默认ATT MTU(最大传输单元)为23字节,扣除3字节ATT协议包头,单包写入最多仅支持20字节有效业务数据:
- 测试用的短配置指令共7字节,远小于20字节单包上限,可直接发送成功
- 文档给出的开灯指令从Byte0到Byte20共21字节,超出默认单包载荷上限,一次性传入完整数组就会触发底层GATT传输错误
- 示例中传入的长数组首字节为
0x32,遗漏了指令规定的Byte0字段0x01,即便传输逻辑正确,设备也会因指令格式非法拒绝响应
实现方案
1. 协商MTU提升单包容量
GATT服务连接成功后,可主动向外设申请更大的MTU,减少分包数量:
// GATT连接建立后调用 const negotiatedMtu = await gattServer.requestMtu(512); // 扣除3字节ATT协议头,得到单包最大可承载业务数据长度 const maxChunkSize = negotiatedMtu - 3;
若设备不支持MTU协商,直接使用默认20字节作为单包长度兜底即可。
2. 分包顺序发送长数据
禁止一次性传入完整长数组,将数据按单包最大长度切片,逐包顺序发送,上一包写入完成后再发送下一包,避免并发写入触发错误:
async function writeLongBleData(characteristic, fullData) { // 未协商到有效MTU时默认用20字节单包长度 const chunkSize = maxChunkSize || 20; for (let offset = 0; offset < fullData.length; offset += chunkSize) { const currentChunk = fullData.slice(offset, offset + chunkSize); const payload = Uint8Array.from(currentChunk); // 优先使用无响应写入提升传输效率,不支持时回退为带响应写入 if (characteristic.properties.writeWithoutResponse) { await characteristic.writeValueWithoutResponse(payload); } else { await characteristic.writeValueWithResponse(payload); } // 老旧设备若仍报错可开启10~20ms包间隔延时 // await new Promise(resolve => setTimeout(resolve, 15)); } } // 调用示例:传入完整合规的开灯指令 const lightOnCommand = [0x01, 0x32, 0x5A, 0x04, 0x2A, 0x00, 0x24, /* 补全Byte5-18对应字节 */, 0x2A, 0xB2, 0x1F]; await writeLongBleData(targetCharacteristic, lightOnCommand);
3. 前置核对项
- 确认多字节字段字节序:例如Byte3-4的
0x042A,需核对设备要求为大端序(先发0x04再发0x2A)还是小端序(先发0x2A再发0x04),字节序错误会导致设备无响应或返回错误 - 确认目标特征值写入权限:部分特征值对写入指令长度有强制要求,需严格匹配设备文档规定
内容的提问来源于stack exchange,提问作者Nguyen Cao Hoang Nam
相关产品推荐
相关产品推荐

