const common = require('../common.js'); const FMBMS = require('./FMBMS.js'); var bluetooth = require('@/common/bluetooth.js'); import permision from "@/js_sdk/wa-permission/permission.js" const readServiceID = '0000FEE7-0000-1000-8000-00805F9B34FB'; const readID = '000036F6-0000-1000-8000-00805F9B34FB'; const writeServiceID = '0000FEE7-0000-1000-8000-00805F9B34FB'; const writeID = '000036F5-0000-1000-8000-00805F9B34FB'; const MTU = 115; let subIndex = -1 let commands = []; // 升级包指令数组,每个指令是一个 ArrayBuffer let currentCommandIndex = 1; var totalLength = 0 //总包长度 var joinPack = []//接收到的数据包 var readRepeatTime = 0 //读到完整数据包的时间戳 var readRepeatTime = 0 //读到完整数据包的时间戳 function acceptDevice(device) { return device.btid ? true : false; } function isSingleBt() { console.log('是单蓝牙'); return true; } function haveBms() { console.log('是单蓝牙并且带bms'); return true; } function isDevice(device, data) { // console.log(device,data,'device111'); const advertisData = new Uint8Array(data.advertisData); // console.log(advertisData,'判断返回第几个字段是正常模式还是升级模式'); //打印返回广播 判断返回字段是正常模式还是升级模式 // console.log(advertisData.slice(4, 10).toString(),'device2222'); const mac = device.btid .split('') .map((p, i) => parseInt(p + device.btid[i + 1], 16)) .filter((p, i) => i % 2 == 0); // if ( advertisData.slice(4, 10).toString() == "095A5832") { // return true; // } //判断是否是智寻的蓝牙 const result = data.name.startsWith("ZX"); // const result = data.name.startsWith("ZN"); // if (data.name === "865416038002201") { // if (data.name === "ZX2503150000000") { if (result) { return true } return false; } //连接蓝牙后 发送登录指令 function alterConnect(device, deviceId) { const app = getApp(); readRepeatTime=0 // console.log(device,app.globalData.connectionState[deviceId],'deviceliuwei'); //登录 crc[0x1F,0x0F,0x5A,0x58,0x32,0x32,0x30,0x32,0x32,0x32,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x08,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x67,0xAC,0x53,0xE7,0x08,0x00] //登录 [[0x22,0x22,0x01,0x1F,0x0F,0x5A,0x58,0x32,0x32,0x30,0x32,0x32,0x32,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x08,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x67,0xAC,0x53,0xE7,0x08,0x00,crc_data,0xAA,0xAA]] //时间戳 0x67AC53E7 0x65,0xB3,0xED,0x6F 16:00:05 //切换正常,工厂下发指令 [[0x22,0x22,0x54,0x01,0x01,0x12,0xAA,0xAA]] //OTA 升级指令 [[0x22,0x22,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,n,0xCB,0xAA,0xAA]] // const data = [0x1F, 0x0F, ,0x5A,0x58, 0x35, 0x32, 0x30, 0x32, 0x32, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, console.log("校检---》") let data=[ 0x1F, 0x0F] // const device_name=app.globalData.connectionState[device.mac_id].deviceName const device_sn=stringToHexArray(device.mac_id) // const car_info = uni.getStorageSync('car_info') // const device_sn=stringToHexArray(car_info.car_sn) // const device_sn=stringToHexArray('865416038001109') data.push(...device_sn) data.push(0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x67, 0xAC, 0x53, 0xE7, 0x08, 0x00) // const data=[ 0x1F, 0x0F,0x38, 0x36, 0x35, 0x34, 0x31, 0x36, 0x30, 0x33, 0x38, 0x30, 0x30, 0x32, 0x32, 0x30, 0x31,0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x67, 0xAC, 0x53, 0xE7, 0x08, 0x00] // console.log(data,'datatest'); console.log(crc8IEEE8023(data).toString(16).toUpperCase(),'crc---------'); const crc_data = parseInt(crc8IEEE8023(data).toString(16).toUpperCase(),16) // const crc_data =crc8IEEE8023(data).toString(16).toUpperCase() data.push(crc_data,0xAA,0xAA) data.unshift(0x22,0x22,0x01) // // 从索引 0 开始,删除 3 个元素 const firstPart = data.splice(0, 20); // // 剩余的元素 const secondPart = data; // const threePart = data; // return [data] return [firstPart,secondPart] // return [[0x22]] } function crc8IEEE8023(data) { const poly = 0x07; // IEEE 802.3 polynomial x^8 + x^2 + x + 1 let crc = 0x00; for (let i = 0; i < data.length; i++) { crc ^= data[i]; for (let j = 0; j < 8; j++) { if (crc & 0x80) { crc = ((crc << 1) ^ poly) & 0xFF; } else { crc = (crc << 1) & 0xFF; } } } return crc; } function stringToHexArray(str) { const result = []; for (let i = 0; i < str.length; i++) { const charCode = str.charCodeAt(i); // 获取字符的 ASCII 码值 const hexValue=`0x${charCode.toString(16).toUpperCase()}` const hexDataValue=parseInt(hexValue,16) result.push(hexDataValue); } return result; } //收到硬件方向软件方发送的数据 function readData(device, value, data) { console.log('***********************test'); var test = new Uint8Array(value); // const bmsData=Array.from(test); const normalArray=Array.from(test); // console.log(bmsData,'bmsData-------------'); // const normalArray =packBmsData(bmsData) if(normalArray){ console.log(normalArray[2],'normalArray---------------'); switch(normalArray[2]){ case 0x60: //登陆指令 loginCmd(device.mac_id,normalArray) break; case 0x61||0x62||0x65: //开机指令 关机 寻车 //设置灵敏度回复 trunCarCommand(device.mac_id,normalArray) break; case 0x66: //升级指令 checkSendNextCommand(device.mac_id,normalArray) break; case 0x7C: //接近解锁指令 nearCarCmd(device.mac_id,normalArray) break; case 0x7E: //获取车辆接近解锁 是否配对 灵敏度信息 getNearCarCmdInfo(device.mac_id,normalArray) break; } } // console.log(normalArray,currentCommandIndex-1,commands[currentCommandIndex],commands[currentCommandIndex-1], 'value**************************'); // if((normalArray[0]==0x00) && (normalArray[1]==0x01) && normalArray.length==2){ // switch (normalArray[1]) { // case 0x01: // uni.showModal({ // title: '提示', // confirmText: '开始升级', // content: '登陆成功,当前处于升级模式是否开始升级', // success: function(res) { // if (res.confirm) { // currentCommandIndex=0 // otaUpgrade().then(()=>{ // //登陆成功 且当前为升级模式可以发送升级文件 // sendNextCommand(device.mac_id) // }) // } else {} // } // }); // return [1,1,2,3] // break; // } // }else if(normalArray[0]==0x00&&normalArray.length==1){ // // common.simpleToast('开机成功', 2000) // return '操作成功' // }else{ // switch(normalArray[3]){ // case 0x00: // sendNextCommand(device.mac_id) // break; // case 0x01: // uni.hideLoading(); // console.log(normalArray,commands.length,currentCommandIndex-1,commands[currentCommandIndex-3],commands[currentCommandIndex-2],commands[currentCommandIndex-1], 'value**************************'); // common.simpleToast('升级文件回复失败', 2000) // // test() 升级文件回复失败 // break; // } // } return data; } function loginCmd(mac_id,data){ const len=data.length-4 if(data[len-1]==0x00 && data[len]==0x01){ uni.showModal({ title: '提示', confirmText: '开始升级', content: '登陆成功,当前处于升级模式是否开始升级', success: function(res) { if (res.confirm) { currentCommandIndex=0 otaUpgrade().then(()=>{ //登陆成功 且当前为升级模式可以发送升级文件 sendNextCommand(device.mac_id) }) } else {} } }); }else if(data[4]==0x00 && data[4]==0x00){ // uni.hideLoading(); //正常模式 console.log('登陆成功'); // if('lockType' in app.globalData.nearLockInfo ){ // if(app.globalData.nearLockInfo.lockType==0){ // console.log('获取接近解锁指令为关闭'); // }else{ // uni.hideLoading(); // console.log('获取接近解锁指令为成功'); // } // }else{ // const getNearUnlockSetCmd=getNearUnlockSet() // console.log(getNearUnlockSetCmd,'getNearUnlockSetCmd'); // writeData(mac_id,getNearUnlockSetCmd) // } checkUnlock(mac_id,'login') } } function checkSendNextCommand(mac_id,data){ const len=data.length-4 if(data[len]==0x00){ sendNextCommand(device.mac_id) }else if(data[len]==0x01){ uni.hideLoading(); // console.log(normalArray,commands.length,currentCommandIndex-1,commands[currentCommandIndex-3],commands[currentCommandIndex-2],commands[currentCommandIndex-1], 'value**************************'); common.simpleToast('升级文件回复失败') } } function trunCarCommand(mac_id,data){ uni.hideLoading(); const len=data.length-4 if(data[len]==0x00){ common.simpleToast('操作成功') }else if(data[len]==0x01){ common.simpleToast('操作失败') } } function nearCarCmd(mac_id,data){ const app = getApp(); uni.hideLoading(); //测试 const len=data.length-4 const pData={ lockType:data[4],//接近解锁配置 } console.log(pData,data,pData.status,'pData'); app.globalData.nearLockInfo=pData if(data[len]==0x00){ common.simpleToast('操作成功') // common.simpleToast(`请在系统通知中同意与${mac_id}配对`) }else if(data[len]==0x01){ common.simpleToast('操作失败') // common.simpleToast('配对失败,请重新开始配对') } } //处理蓝牙三段回复 综合成一个数组 function packBmsData(value) { if (value.length!=0) { console.log(endTime - readRepeatTime); if (endTime - readRepeatTime > 8000) {//超过8秒,说明数据有问题,数据清空 readRepeatTime = endTime joinPack = [] } // console.log((value[0] == 0x22) && (value[1]== 0x22),joinPack,'000000'); if((value[0] == 0x22) && (value[1]== 0x22)){//当开始符为标志符 代表接收包开始 var endTime = new Date().getTime() totalLength = value[3]//包长度 joinPack=value // console.log(joinPack,value,'111111'); }else if(joinPack.length!=0 && (joinPack[0] == 0x22) && (joinPack[1]== 0x22)){ const len=joinPack.length-1 const diffJoinLength =joinPack.length-4 readRepeatTime = endTime // console.log(totalLength,diffJoinLength,value,'2222222'); if(totalLength > diffJoinLength){//内容长度符合为完整包 joinPack.push(...value) return joinPack }else if(totalLength <= diffJoinLength){ joinPack = [] } } else{ readRepeatTime = endTime joinPack = [] return joinPack } } } function mergeUint8Array(arr1, arr2) { let len1 = arr1 ? arr1.length : 0; let len2 = arr2.length; let arr = new Uint8Array(len1 + len2); for (let i = 0; i < len1; i++) { arr[i] = arr1[i]; } for (let i = 0; i < len2; i++) { arr[len1 + i] = arr2[i]; } return arr; } //发送指令封装 function sendCommand(cmd, data = []) { readRepeatTime=0 let cmdData = [0x22, 0x22] // cmdData.push(cmd) //协议号 cmd //包长度 data.length const dataLen=parseInt(data.length.toString(16).toUpperCase(), 16) //信息内容 cmd //crc校验 const pData=[dataLen,...data] const crcData=parseInt(crc8IEEE8023(pData).toString(16).toUpperCase(), 16) cmdData.push(cmd,dataLen,...data,crcData,0xAA, 0xAA) // console.log(cmdData,'cmdDatacmdData'); return cmdData } function turnOnCar(device, deviceId) { return [sendCommand(0x02,[0x02])]; } function turnOffCar(device, deviceId) { return [sendCommand(0x03,[0x03])]; } function findCarCmd(device, deviceId) { return [sendCommand(0x06,[0x06])]; } function openCarSeat(device, deviceId) { return [sendCommand(0x07,[0x07])]; } function openCarTrunk(device, deviceId) { return [sendCommand(0x08,[0x08])]; } function getCarPressure(device, deviceId) { return [sendCommand(0x4A,[0x4A])]; } function nearUnlock(device, deviceId) { return [sendCommand(0x4B,[0x02])]; } function nearCloseUnlock(device, deviceId) { return [sendCommand(0x4B,[0x00])]; } function getSensitivity(device, deviceId) { return [sendCommand(0x4F,[0x4F])]; } function setSensitivity(device, deviceId,type) { return [sendCommand(0x4E,['0x4'+type])]; } function setNearUnlock(device, deviceId,type) { return [sendCommand(0x4D,[0x4D])]; } function getNearUnlockSet(device, deviceId,type) { return [sendCommand(0x4D,[0x4D])]; } function getNearCarCmdInfo(macid,data){ console.log('getNearCarCmdInfo',macid,data); if(data[5]==0x02||data[5]==0x01){ uni.hideLoading(); const pData={ lockType:data[4],//接近解锁配置 level:data[5],//感应等级 } const app = getApp(); app.globalData.nearLockInfo=pData console.log('已开启配对'); // common.simpleToast('已开启配对',2000) // common.simpleToast('感应解锁已开启,前往设置灵敏度页面') // uni.navigateTo({ url: '/pages/bluetoothUnlock/unlockset' }) // if( pData.status == 0x01){ // common.simpleToast('配对成功') // return // }else if(pData.status==0x00){ // common.simpleToast('未配对') // } setTimeout(()=>{ checkUnlock(macid) },300) }else if(data[5]==0x00){ console.log('未开启配对') const nearUnlockCmd=nearUnlock() console.log(nearUnlockCmd,'nearUnlockCmd'); writeData(mac_id,nearUnlockCmd) } } function checkUnlock(macid,type='get'){ const res=permision.getBondedDevices() console.log(res,'test0000000000000'); let app=getApp() if(res){ app.globalData.nearLockCheck=true common.simpleToast('感应解锁已开启,前往设置灵敏度页面') uni.redirectTo({ url: '/pages/bluetoothUnlock/unlockSet' }) console.log('test'); //redirectTo 关闭当前页面 防止用户重复操作 }else{ if(type==='login'){ //下发接近解锁指令 // const nearUnlockCmd=nearUnlock() // console.log(nearUnlockCmd,'nearUnlockCmd'); // setTimeout(() => { // writeData(macid,nearUnlockCmd) // }, 500); // 获取接近解锁指令信息 const getNearUnlockSetCmd=getNearUnlockSet() console.log(getNearUnlockSetCmd,'getNearUnlockSetCmd'); setTimeout(() => { writeData(macid,getNearUnlockSetCmd) }, 200); }else{ uni.showModal({ title: '蓝牙配对确认', content: '请在系统通知栏前往同意与'+macid+'设备配对', cancelText: '取消', confirmText: '已同意', success: function(res) { if (res.confirm) { checkUnlock(macid) }else{ // uni.switchTab({ url: '/pages/index/index' }) } }, fail: function(res) {}, complete: function(res) {}, }) } } } function switchFactory(device, deviceId) { //切换正常,工厂模式指令 //切换工厂模式 [[0x22,0x22,0x54,0x01,0x01,0x12,0xAA,0xAA]] //切换正常模式 [[0x22,0x22,0x54,0x01,0x00,0x15,0xAA,0xAA]] return [[0x22,0x22,0x54,0x01,0x01,0x12,0xAA,0xAA]] // return [sendCommand([0x54, 0x01, 0x01, 0x12])]; } //升级按钮 function otaUpgrade(device){ const data = readBinBinarayToCommand() data.then(result=>{commands=result}) // console.log(data,'datatest111'); return data } async function readBinBinarayToCommand() { let res =await uni.request({ url: 'https://opt.bms16.com/ota.BIN', // 文件的网络地址 method: 'GET', responseType: 'arraybuffer', // 指定响应类型为 arraybuffer }); const arrayBuffer=res[1].data const uint8Array = new Uint8Array(arrayBuffer); // 转换为 Uint8Array const test = splitArrayIntoChunks(uint8Array, 80); // 分割成长度为 80 的数组 const data =makeArr(test) return data } function sendNextCommand(mac_id) { const app = getApp(); readRepeatTime=0 // console.log(currentCommandIndex,commands.length,'开始发送指令'); // console.log(commands[currentCommandIndex],'command'); if (currentCommandIndex >= commands.length) { uni.hideLoading(); common.simpleToast('所有指令发送完成', 2000) return; } const deviceId = Object.keys(app.globalData.connectionState).find((i) => app.globalData.connectionState[i].device.mac_id == mac_id); if (deviceId == undefined) { console.log('deviceId == undefined'); return ; } const command = [commands[currentCommandIndex]]; if(command.length==0){ return } // // 将数据转换为ArrayBuffer类型 var buffer; buffer = common.toArrayBuffer(command.shift()); // console.log(command,'command'); uni.writeBLECharacteristicValue({ deviceId: deviceId, // 替换为实际的设备 ID serviceId: writeServiceID, // 替换为实际的服务 ID characteristicId: writeID, // 替换为可写入的特征值 ID value: buffer, success() { // console.log('指令发送成功,等待设备响应',currentCommandIndex,commands[currentCommandIndex]); }, fail(err) { console.error('指令发送失败', err); } }); currentCommandIndex++; } function splitArrayIntoChunks(array, chunkSize) { const newArr=array.slice(16383) const numChunks = Math.ceil(newArr.length / chunkSize); return Array.from({ length: numChunks }, (_, i) => { return newArr.slice(i * chunkSize, (i + 1) * chunkSize); }); } // 处理分割后的数组 function makeArr(array) { const result = []; let endPackage; for (let i = 0; i < array.length; i ++) { endPackage = 0x00 if((i + 1) >= array.length){ //当前是最后一次循环 endPackage = 0x01 } // if(i==2) return //协议号 30 包长度未固定array.length+8 转16进制 固件类型0x00 升级包大小(总包大小) 125396字节-》0x00,0x01,0xE9,0xD4 let startArray = [] const packageLen=parseInt((array[i].length+8).toString(16).toUpperCase(), 16) // const totalPackageSize=[0x00,0x00,0x01,0xE9,0xD4] // const totalPackageSize=[0x00,0x00,0x01,0xeb,0xfc] const totalPackageSize=[0x00,0x00,0x01,0xAB,0xFC] startArray.push(packageLen,...totalPackageSize) //软件包id const ids=splitNumber(i) // 0x00,0x01 startArray.push(...ids) //是否为最后一个包 startArray.push(endPackage) //软件数据包 const formattedChunk = array[i].map(byte => `0x${byte.toString(16).toUpperCase()}`); // 转换为 0xXX 格式 startArray.push(...formattedChunk) // if(i>95&& i<105){ // console.log(startArray,'startArray-------'); // } //crc检验 const crcStr='0x'+crc8IEEE8023(startArray).toString(16).toUpperCase() const testCrc=parseInt(crcStr,16) startArray.push(crcStr) startArray.push(0xAA,0xAA) startArray.unshift(0x22,0x22,0x30) result.push(startArray); } // result.push([0x22,0x22,0x30,0x58,0x00,0x00,0x00,0xE9,0xD4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2C,0xAA,0xAA]) return result; } function splitNumber(num) { let highByte = (num >> 8) & 0xFF; // 对于0-255的数字,结果总是0 // 低字节 let lowByte = num & 0xFF; // 将每个字节转换为两位的十六进制字符串,并添加'0x'前缀 let highHex = highByte.toString(16).toUpperCase().padStart(2, '0'); // 确保是两位十六进制数 let lowHex = lowByte.toString(16).toUpperCase().padStart(2, '0'); // 确保是两位十六进制数 // 返回结果数组(这里实际上可以返回一个对象或字符串,根据需求调整) return ['0x' + highHex, '0x' + lowHex]; } // 定义一个函数,用于向蓝牙设备写入数据 function writeData(mac_id, data, callback = () => {}, fail = () => {}) { const app = getApp(); const deviceIds = app.globalData.connectionState[mac_id].deviceId console.log(deviceIds,'deviceIds'); if (deviceIds == undefined) { return ; } // 如果数据长度为0,则直接返回 if (data.length == 0) { return; } // 将数据转换为ArrayBuffer类型 var buffer; buffer = common.toArrayBuffer(data.shift()); // 调用uni.writeBLECharacteristicValue方法,向蓝牙设备写入数据 uni.writeBLECharacteristicValue({ deviceId: deviceIds, serviceId: writeServiceID, characteristicId: writeID, value: buffer, // 成功回调函数 success(res) { // 如果数据长度为0,则调用回调函数 if (data.length == 0) { callback(res); } else { // 否则,延时500毫秒后再次调用writeData函数 setTimeout(() => { writeData(device, data, callback, fail); }, 500); } }, // 失败回调函数 fail(res) { console.log(res); // 调用失败回调函数 fail(res); } }); } module.exports = { readServiceID: readServiceID, readID: readID, writeServiceID: writeServiceID, writeID: writeID, MTU: MTU, acceptDevice: acceptDevice, isDevice: isDevice, alterConnect: alterConnect, readData: readData, turnOffCar: turnOffCar, isSingleBt: isSingleBt, haveBms: haveBms, switchFactory: switchFactory, otaUpgrade, findCarCmd, turnOffCar, turnOnCar, getCarPressure, nearUnlock, nearCloseUnlock, getSensitivity, setSensitivity, getNearUnlockSet, setNearUnlock, };