uniapp蓝牙BLE数据解析实战:帧结构、校验与分包处理

发布时间:2026/5/18 22:36:27

uniapp蓝牙BLE数据解析实战:帧结构、校验与分包处理 文章目录前言一、BLE数据通信基础二、帧结构解析2.1 典型协议帧结构2.2 通用解析器封装2.3 使用三、校验算法3.1 异或校验3.2 累加和校验3.3 CRC16校验四、写入数据的编码五、调试技巧5.1 console.log打印日志5.2 nRF Connect5.3 常见问题速查总结前言之前写了uniapp蓝牙连接的基本流程但回忆起自己刚开始接触蓝牙的时候碰到一个问题**连上了数据拿到了然后呢**拿到的是一坨ArrayBuffer怎么看都像乱码。这恰恰是BLE开发最坑的地方——连接只是开始数据解析才是核心。硬件工程师给你一份协议文档你能不能把它变成前端可用的数据本文从实际项目出发手把手讲清楚帧结构解析、校验算法和分包处理。一、BLE数据通信基础BLE通信的核心概念概念说明Service服务的集合一个设备可有多个ServiceCharacteristic具体数据通道分读/写/通知三种属性Notify设备主动推送数据给App最常用WriteApp向设备写入数据MTU单次传输最大字节数默认20字节协商后最大512前端读取/接收数据的格式是ArrayBuffer我们需要把它转成可操作的字节数组再做解析。// ArrayBuffer → 16进制字符串调试用functionbuf2Hex(buffer){returnArray.from(newUint8Array(buffer)).map(bb.toString(16).padStart(2,0).toUpperCase()).join( )}// 示例输出: AA 55 0E 01 00 1A 00 00 00 00 00 3C 12 34 56 78 开发阶段强烈建议先写个buf2Hex把原始数据打印出来对照协议文档逐字节分析。二、帧结构解析2.1 典型协议帧结构不同厂家的协议各不相同但大多数遵循类似的帧结构| 帧头 | 长度 | 命令字 | 数据域 | 校验 | 帧尾 | | 2B | 1B | 1B | NB | 1B | 2B |2.2 通用解析器封装// utils/bleParser.jsclassBleParser{constructor(){this.buffer[]// 数据缓冲区this.frameHead[0xAA,0x55]// 帧头this.minFrameLen6// 最小帧长度}/** * 喂入原始数据 * param {ArrayBuffer} data - 蓝牙通知回调的原始数据 * returns {Array} 解析出的完整帧列表 */feed(data){constbytesnewUint8Array(data)// 将新数据追加到缓冲区for(leti0;ibytes.length;i){this.buffer.push(bytes[i])}returnthis._parse()}/** 从缓冲区中提取完整帧 */_parse(){constframes[]while(this.buffer.lengththis.minFrameLen){// 1. 寻找帧头constheadIndexthis._findHead()if(headIndex-1){this.buffer[]break}// 丢弃帧头之前的垃圾数据if(headIndex0){this.buffer.splice(0,headIndex)}// 2. 读取长度字段第3字节constdataLenthis.buffer[2]consttotalLen211dataLen12// 帧头长度命令数据校验帧尾// 数据不完整等下一次if(this.buffer.lengthtotalLen)break// 3. 提取一帧constframethis.buffer.splice(0,totalLen)// 4. 校验if(this._verify(frame)){frames.push(this._decode(frame))}else{console.warn([BLE] 校验失败:,buf2Hex(newUint8Array(frame).buffer))}}returnframes}/** 查找帧头位置 */_findHead(){for(leti0;ithis.buffer.length-1;i){if(this.buffer[i]this.frameHead[0]this.buffer[i1]this.frameHead[1]){returni}}return-1}/** 校验示例用异或校验 */_verify(frame){// 从长度字段到校验前一位做异或letxor0for(leti2;iframe.length-3;i){xor^frame[i]}returnxorframe[frame.length-3]}/** 解码帧数据 */_decode(frame){constcmdframe[3]constdataLenframe[2]constdataBytesframe.slice(4,4dataLen)return{cmd,dataLen,raw:dataBytes,parsed:this._parseByCmd(cmd,dataBytes)}}/** 按命令字解析数据域 */_parseByCmd(cmd,data){constviewnewDataView(newUint8Array(data).buffer)switch(cmd){case0x01:// 温湿度return{temperature:view.getInt16(0)/10,// 有符号2字节humidity:view.getUint8(5),// 1字节}case0x02:// 设备状态return{battery:view.getUint8(0),signal:view.getUint8(1),online:view.getUint8(2)1,}default:return{rawHex:buf2Hex(newUint8Array(data).buffer)}}}}exportdefaultBleParser2.3 使用importBleParserfrom/utils/bleParserconstparsernewBleParser()// 蓝牙通知回调onBLECharacteristicValueChange(res){constframesparser.feed(res.value)frames.forEach(frame{console.log(命令: 0x${frame.cmd.toString(16)},frame.parsed)// 输出示例: 命令: 0x1 { temperature: 26, humidity: 60 }this.handleFrame(frame)})}三、校验算法常见校验方式三种3.1 异或校验functionxorCheck(bytes){letresult0for(leti0;ibytes.length;i){result^bytes[i]}returnresult}3.2 累加和校验functionsumCheck(bytes){letresult0for(leti0;ibytes.length;i){result(resultbytes[i])0xFF// 取低8位}returnresult}3.3 CRC16校验functioncrc16(bytes){letcrc0xFFFFfor(leti0;ibytes.length;i){crc^bytes[i]for(letj0;j8;j){if(crc0x0001){crc(crc1)^0xA001}else{crc1}}}returncrc} 校验算法必须和硬件端一致不确定就问硬件同事要他们的C代码直接翻译成JS。四、写入数据的编码解析是读写入是反向操作——把JS对象编码成ArrayBufferfunctionbuildFrame(cmd,data){// 构建数据域constdataBytes[]// 示例写入温度阈值if(cmd0x10){consttempMath.round(data.threshold*10)dataBytes.push((temp8)0xFF,temp0xFF)}// 组装完整帧constframe[0xAA,0x55,dataBytes.length,cmd,...dataBytes]// 计算校验letxor0for(leti2;iframe.length;i){xor^frame[i]}frame.push(xor,0x0D,0x0A)// 校验 帧尾// 转ArrayBufferconstbuffernewArrayBuffer(frame.length)constviewnewDataView(buffer)frame.forEach((b,i)view.setUint8(i,b))returnbuffer}// 发送writeBLECharacteristicValue({deviceId:this.deviceId,serviceId:this.serviceId,characteristicId:this.writeCharId,value:buildFrame(0x10,{threshold:30.5})})五、调试技巧5.1 console.log打印日志// 包一层日志开发环境自动打印constrawNotifyuni.onBLECharacteristicValueChange.bind(uni)uni.onBLECharacteristicValueChange(callback){rawNotify((res){if(process.env.NODE_ENVdevelopment){console.log([BLE RX]${res.characteristicId}:,buf2Hex(res.value))}callback(res)})}5.2 nRF Connect调试BLE必备工具我一般手机装一个nRF Connect手动连设备收发数据对照协议文档验证比在代码里猜效率高10倍。5.3 常见问题速查现象原因解决收到数据全是0读错Characteristic检查notify属性是否开启只收到第一包没开启notify订阅调用notifyBLECharacteristicValueChange数据断断续续没做缓冲拼接用BleParser流式解析校验总是失败字节序不一致确认大端/小端问硬件要示例数据验证iOS能收Android收不到MTU差异安卓协商MTU或按20字节兼容总结BLE数据解析的核心就三步缓冲拼接用feed流式处理解决分包问题帧结构解析找帧头→读长度→提取数据→校验→解码字节级操作DataView读写多字节值注意大小端建议把BleParser封装成通用组件不同项目只需替换协议配置帧头、校验算法、命令字解析解析器本身不用改。

相关新闻