// 安卓平台上,在调用 notifyBLECharacteristicValueChange 成功后立即调用 writeBLECharacteristicValue 接口,在部分机型上会发生 10008 系统错误 const cmd = { heartBeat: 'heart', // 心跳包 unlock: 'unlock', // 开锁命令 lock: 'lock', // 开锁命令 bell: 'bellBike', // 关锁命令 temporaryUnlockBike: 'temporaryUnlockBike', // 关锁命令 temporaryLockBike: 'temporaryLockBike', // 关锁命令 batteryUnlock: 'batteryUnlock', // 关锁命令 batteryLock: 'batteryLock', // 关锁命令 login: 'login', // 关锁命令 status: 'status', // 关锁命令 ack: 'ack', // 关锁命令 } const errorCode = { no_connection: 10006, //失去连接 no_service: 10004, //没有找到指定服务 apdater_no_avilable: 10001, //当前蓝牙适配器不可用 discovery_timeOut: 1100, //扫描失败 connet_timeOut: 10003 //连接超时 } const GetBluUrl = 'https://api.weilaibike.com/api/blu/get-key' let util = require('../utils/bluUtil.js') let dataTransition = require('../utils/dataTransition.js') class BluetoothManager { constructor() { this._connectDeviceName = 'TBIT-WA209D' this._isFoundDevice = false this._deviceId = '' this._serviceId = '' this._bikeMainId = '' this._sequenceId = 10 this._characteristicId = '' this._characteristicId_write = '' this._discovering = false this.discoveryTimeOut = 10 * 1000 this.isConnecting = false this.isAuth = false this.connnectTimeOut = 5 * 1000 //连接超时时间 this.hardwareCallbackTimeOut = 5 * 1000 //硬件回调超时时间 this.adapterStateChangeCallBack = undefined this.onConnectionStateChange = undefined this.heartTimer = '' // 处理接受数据 this._receiveLength = 0 this._receiveData = '' // this.initialNotification() this.readyStopDiscovery = false //标记准备关闭扫描 } /*******初始化各种回调**********/ initialNotification() { console.log('initialNotification') // 监听蓝牙适配器状态改变 wx.onBluetoothAdapterStateChange( res => { console.log('onBluetoothAdapterStateChange', res) if (res.available && !res.discovering) { // this._connectDeivece() } if (this.adapterStateChangeCallBack) { // if(!res.discovering && !this.isFinishDiscover){ // this.isFinishDiscover = false this.adapterStateChangeCallBack(res) // } } } ) // // 监听扫描到的设备 wx.onBluetoothDeviceFound( res => { if (this._isFoundDevice) return if (res.devices[0].name) console.log('onFound:', res.devices[0].name, 'DeviceName', this._connectDeviceName) for (var i = 0; i < res.devices.length; i++) { const device = res.devices[i] if (!this._isFoundDevice && device.name && device.name == this._connectDeviceName) { // 获取车辆中控编号 let machineNO = util.encrypt(util.ab2hex(device.advertisData).slice(4, 13)); if (machineNO == this._bikeMainId) { console.log('-------found ---', device.name, machineNO) this._isFoundDevice = true this._deviceId = device.deviceId if (this.deviceFoundBlock) { this.deviceFoundBlock(device.deviceId) } return } } } } ) // 监听蓝牙连接状态 wx.onBLEConnectionStateChange( res => { console.log('onBLEConnectionStateChange', res, this._deviceId) this.isConnecting = res.connected if (res.deviceId == this._deviceId && res.connected == false) { console.log('onBLEConnectionStateChange fail', this._connectionFailBlock); if (this._connectionFailBlock) { // wx.showModal({ // title: '提示', // content: '蓝牙连接超时', // showCancel: false // }) wx.hideLoading() this._connectionFailBlock(res) this._connectionFailBlock = undefined } if (this.onConnectionStateChange) { this.onConnectionStateChange(res) } wx.closeBLEConnection({ deviceId: this._deviceId, success: function (res) { console.log('closeBLEConnection', res) }, }) } else if (res.deviceId == this._deviceId && res.connected) { // 按需求决定是否重新获取服务,获取特征值通道 //console.log('onBLEConnectionStateChange : connect succes ',res) } } ) // 监听特征值变化 wx.onBLECharacteristicValueChange( characteristic => { // console.log('onBLECharacteristicValueChange', characteristic) if (characteristic.value) { let res = characteristic.value let resData = util.ab2hex(res.value); if (this._receiveLength == 0) { this._receiveLength = parseInt(resData.slice(8, 12), 16) } this._receiveData += resData if (this._receiveData.length === (this._receiveLength * 2 + 16)) { let _receiveData = this._receiveData console.log(_receiveData, '_receiveData') let flay = _receiveData.slice(0, 4) let crc16 = _receiveData.slice(12, 16); let systemState = _receiveData.slice(4, 6); //4c let sequenceId_16 = _receiveData.slice(6, 8); //0a let body = _receiveData.slice(16) let contentArr = util.addFlagBeforeArr(util.strAverage2Arr(body, 2)); //校检数据 if (parseInt(dataTransition.getCRC16(contentArr), 16) == parseInt(crc16, 16)) { let value = util.hexStringToArrayBuffer(`aa12${systemState}${sequenceId_16}00000000`); if (flay === 'aa10') { if (body.indexOf('aa10550b') > -1) { if (this.getRandomSuccessBlock) { this.getRandomSuccessBlock(body) this.getRandomSuccessBlock = undefined } } console.log('指令发送成功', body) } else if (flay === 'aa00') { } else if (flay === 'aa30') { console.log('校检错误') } else { this.writeBLECharacteristicValue(value) that.analysisBLEContent(body) } } // 返回ACK this._receiveLength = 0 this._receiveData = '' } } } ) } //解析蓝牙发送内容 analysisBLEContent(content) { let that = this console.log('解析数据数据***************', content); if(content=='') return false; if (content.indexOf('0500020101') > -1) { this.isAuth = true if (that.authLoginSuccessBlock) { setTimeout(() => { // that.authLoginSuccessBlock() that.authLoginSuccessBlock = undefined }, 50) } console.log('授权成功') } else if (content.indexOf('040085') > -1) { console.log('心跳包') } else if (content.indexOf('0300820100') > -1) { if (that.unlockSuccessBlock) { that.unlockSuccessBlock() that.unlockSuccessBlock = undefined console.log('开锁成功'); } } else if (content.indexOf('0300840100') > -1) { if (that.bellSuccessBlock) { that.bellSuccessBlock() that.bellSuccessBlock = undefined console.log('响铃成功') } } else if (content.indexOf('0300810100') > -1) { if (that.lockSuccessBlock) { that.lockSuccessBlock() that.lockSuccessBlock = undefined console.log('关锁成功'); } } else if (content.indexOf('0300850100') > -1) { that.batterySuccessBlock = undefined if (that.batterySuccessBlock) { that.batterySuccessBlock = undefined console.log('操作电池锁成功'); } } else if (content.indexOf('04008524') > -1) { that.heartSuccessBlock = undefined if (that.heartSuccessBlock) { that.heartSuccessBlock = undefined console.log('心跳包成功'); } } else if (content.indexOf('020100') > -1) { this.isAuth = false if (that.authLoginSuccessBlock) { that.authLoginSuccessBlock = undefined } console.log(授权失败) } else { wx.showToast({ title: content === '0300810102' ? '运动中不能上锁!' : '蓝牙操作失败,请重试!', mask: true, icon: 'none', duration: 5000 }) if(content=='0300810102'){ } } if (that.heartSuccessBlock) { that.heartSuccessBlock = undefined } clearInterval(this.heartTimer) that.heartBeat().then(res => { this.closeConnection() }) } /*******初始化各种回调**********/ /***********初始化接口***************/ connectDeivece(bikeMainId) { let bike_id = bikeMainId // 初始化蓝牙 if (bike_id == this._bikeMainId && this.isConnecting) { console.log('已连接') return this.getRandom() } this._bikeMainId = bike_id this.initialNotification() return this.openAdapter().then(res => { return this.startDiscovery() }).then( res => { console.log('startDiscovery---------') this._discovering = true // 等待发现设备 return this.waitForDeviceFound() }, reject => { console.log(reject) } ).then( res => { console.log('stopDiscovery---------') this.readyStopDiscovery = true //关闭扫描 return this.stopDiscovery() } ).then( res => { this.readyStopDiscovery = false this._discovering = false console.log('createConnection---------', res) //开始连接设备 return this.createConnection(this._deviceId) } ).then( res => { console.log('getServices---------', res) // 获取服务 return this.getServices(this._deviceId) } ).then( res => { console.log('getCharacteristics---------', res) // 获取特征值 return this.getCharacteristics(this._deviceId, this._serviceId) } ).then( res => { console.log('openNotifyChsValueChange---------', res) // 开启notify通道 console.log(this._deviceId, '=>', this._serviceId, '<++', this._characteristicId) return this.openNotifyChsValueChange(this._deviceId, this._serviceId, this._characteristicId) } ).then( res => { return this.onBLECharacteristicValueChange() } ).then(res => { return this.getSecretKey() }).then(res => { return this.bluetoothAuthLogin(res) }) } // 监听特征值变化 onBLECharacteristicValueChange() { let that = this wx.onBLECharacteristicValueChange( characteristic => { console.log('onBLECharacteristicValueChange', characteristic) if (characteristic.value) { let res = characteristic.value let resData = util.ab2hex(res); if (this._receiveLength == 0) { this._receiveLength = parseInt(resData.slice(8, 12), 16) } this._receiveData += resData if (this._receiveData.length === (this._receiveLength * 2 + 16)) { let _receiveData = this._receiveData console.log(_receiveData) let flay = _receiveData.slice(0, 4) let crc16 = _receiveData.slice(12, 16); let systemState = _receiveData.slice(4, 6); //4c let sequenceId_16 = _receiveData.slice(6, 8); //0a let dcArr = []; let body = _receiveData.slice(16) let contentArr = util.addFlagBeforeArr(util.strAverage2Arr(body, 2)); //校检数据 if (parseInt(dataTransition.getCRC16(contentArr), 16) == parseInt(crc16, 16)) { let response = util.hexStringToArrayBuffer(`aa12${systemState}${sequenceId_16}00000000`); if (flay === 'aa10') { console.log(this.getRandomSuccessBlock, 'getRandomSuccessBlock') if (this.getRandomSuccessBlock) { this.getRandomSuccessBlock(body) this.getRandomSuccessBlock = undefined } console.log('指令发送成功', body) } else if (flay === 'aa00') { } else if (flay === 'aa30') { console.log('CRC校验失败', body); } else { //响应数据 that.writeBLECharacteristicValue(response, undefined) that.analysisBLEContent(body) } } this._receiveLength = 0 this._receiveData = '' } } } ) } //蓝牙认证 bluetoothAuthLogin(key) { return new Promise( (resolve, reject) => { console.log('---auth---') this.sendCommand(cmd.login, { key: key }, reject); this.authLoginSuccessBlock = resolve // resolve() setTimeout( () => { console.log('---认证-超时回调ing--', this.authLoginSuccessBlock) if (this.authLoginSuccessBlock) { console.log('---认证-超时了--') this.authLoginSuccessBlock = undefined reject({ errCode: errorCode.connet_timeOut, errMsg: '硬件校验超时' }) } }, this.hardwareCallbackTimeOut ) } ) } //获取秘钥 getSecretKey(box_no) { if (!box_no) box_no = this._bikeMainId return new Promise(function (res, rej) { let param = { box_no: box_no, //我公司是通过设备编号获取的密钥 }; console.log('获取蓝牙密钥') wx.request({ url: GetBluUrl, data: param, header:{ 'Authorization': wx.getStorageSync('token'), 'content-type': 'application/x-www-form-urlencoded', }, success: (resp) => { res(resp.data.key); console.log('获取蓝牙密钥') } }) }); } waitForDeviceFound() { return new Promise( (resolve, reject) => { this.deviceFoundBlock = resolve console.log('waitForDeviceFound ----') console.log(this._discovering, '<==>', !this._isFoundDevice) setTimeout(() => { console.log('waitForDeviceFound ++++', this._discovering, this._isFoundDevice) // 全局断了,_discovering 变为false(发心跳包失败,closeApater引起) console.log(this._discovering, '==>', !this._isFoundDevice) if (this._discovering && !this._isFoundDevice) { console.log('waitForDeviceFound 扫描超时00', 'isconnecting :', this.isConnecting) reject({ errCode: errorCode.discovery_timeOut }) } if (this._discovering) { this.stopDiscovery() } }, this.discoveryTimeOut); } ) } disconnecDevice() { return this.closeConnection(this._deviceId).then( res => { return this.closeAdapter() } ) } /***********初始化接口***************/ /***********蓝牙处理*************/ // 打开适配器 openAdapter() { return new Promise( (resolve, reject) => { wx.openBluetoothAdapter({ success: resolve, fail: reject }) } ) } // 关闭适配器 closeAdapter() { this._isFoundDevice = false this._discovering = false this.isConnecting = false this.adapterStateChangeCallBack = undefined this.onConnectionStateChange = undefined return new Promise( (resolve, reject) => { this.isOpenAdpater = false this.isConnecting = false wx.closeBluetoothAdapter({ success: resolve, fail: reject }) } ) } // 开启扫描 startDiscovery() { return new Promise( (resolve, reject) => { wx.startBluetoothDevicesDiscovery({ services: ['FEF6'], allowDuplicatesKey: true, success: resolve, fail: reject }) } ) } // 关闭扫描 stopDiscovery() { this._isFoundDevice = false return new Promise( (resolve, reject) => { wx.stopBluetoothDevicesDiscovery({ success: resolve, fail: reject }) } ) } // 创建连接 createConnection(deviceId) { return new Promise( (resolve, reject) => { this._connectionFailBlock = undefined this._connectionFailBlock = reject // this._needToToastConnectError = true wx.createBLEConnection({ deviceId: deviceId, timeout: this.connnectTimeOut, success: res => { console.log('--------createConnection success', res) // 创建成果并不代表发指令成功,有可能连接连接创建成功了,但是发指令的时候连接中断了 resolve(res) }, fail: error => { console.log('--------createConnection fail', error) this._connectionFailBlock = undefined reject(error) } }) } ) } // 关闭连接 closeConnection(deviceId) { return new Promise( (resolve, reject) => { wx.closeBLEConnection({ deviceId: deviceId, success: resolve, fail: reject }) } ) } // 获取服务 getServices(deviceId) { let that = this return new Promise( (resolve, reject) => { this._connectionFailBlock = undefined this._connectionFailBlock = reject wx.getBLEDeviceServices({ deviceId: deviceId, success: res => { console.log('getServices success', res) if (res.errCode === 0) { res.services.forEach(function (value, index, array) { console.log("设备所有的UUId", value.uuid); if (value.uuid.indexOf(that._serviceId) > -1) { //找到serviceId包含FEF6的服务 that._serviceId = array[index].uuid; // resolve(serviceId) } }) } resolve(res) }, fail: error => { console.log('getServices fail', error) reject(error) } }) } ) } // 获取特征值 getCharacteristics(deviceId, serviceId) { let that = this return new Promise( (resolve, reject) => { this._connectionFailBlock = undefined this._connectionFailBlock = reject wx.getBLEDeviceCharacteristics({ deviceId: deviceId, serviceId: serviceId, success: res => { console.log('getCharacteristics success', res) for (let i = 0; i < res.characteristics.length; i++) { if (res.characteristics[i].properties.notify && !res.characteristics[i].properties.write) that._characteristicId = res.characteristics[i].uuid; //读的uuid if (res.characteristics[i].properties.write) that._characteristicId_write = res.characteristics[i].uuid; //写的uuid } resolve(res) }, fail: error => { console.log('getCharacteristics fail', error) reject(error) } }) } ) } // 开启notify功能,订阅特征值 openNotifyChsValueChange(deviceId, serviceId, characteristicId) { let that = this return new Promise( (resolve, reject) => { this._connectionFailBlock = undefined this._connectionFailBlock = reject wx.notifyBLECharacteristicValueChange({ deviceId: deviceId, serviceId: serviceId, characteristicId: characteristicId, state: true, success: res => { console.log('openNotifyChsValueChange success', res) resolve(res) }, fail: error => { console.log('openNotifyChsValueChange fail', error) reject(error) } }) } ) } /***********蓝牙处理*************/ /*********action**********/ //获取随机数 getRandom() { return new Promise( (resolve, reject) => { console.log('---getRandom---') resolve(true) // this.sendCommand(cmd.status, undefined, reject); // this.getRandomSuccessBlock = resolve // setTimeout( // () => { // console.log('---getRandom-超时回调ing--') // if (this.getRandomSuccessBlock) { // console.log('---getRandom-超时了--') // this.getRandomSuccessBlock = undefined // this.isConnecting = false // reject({ // errMsg: '硬件校验超时' // }) // } // }, this.hardwareCallbackTimeOut // ) } ) } bellBike() { return new Promise( (resolve, reject) => { // this.sendCommand(0x11, this.randomArray); this.bellSuccessBlock = resolve this.sendCommand(cmd.bell, '', reject); wx.showToast({ title: '找车成功', icon: 'none' }) setTimeout( () => { console.log('---bellBike-超时回调') if (this.bellSuccessBlock) { this.bellSuccessBlock = undefined console.log('---bellBike-超时了--') this.isConnecting = false reject({ errCode: errorCode.connet_timeOut, errMsg: '寻铃超时' }) } }, this.hardwareCallbackTimeOut ) } ) } //开锁 unlockBike() { return new Promise( (resolve, reject) => { this.unlockSuccessBlock = resolve this.sendCommand(cmd.unlock, '', reject); setTimeout( () => { console.log('---unlockBike-超时回调') if (this.unlockSuccessBlock) { this.unlockSuccessBlock = undefined console.log('---unlockBike-超时了--') this.isConnecting = false reject({ errCode: errorCode.connet_timeOut, errMsg: '开启电门超时' }) } }, this.hardwareCallbackTimeOut ) } ) } //发送关锁指令 lockBike() { return new Promise( (resolve, reject) => { console.log('---lockBike---') this.lockSuccessBlock = resolve this.sendCommand(cmd.lock, undefined, reject); setTimeout( () => { console.log('---lockBike-超时回调') // 硬件回调后 会把 lockSuccessBlock 清空,n秒后还没清空,就当指令发送失败 if (this.lockSuccessBlock) { console.log('---lockBike-超时了--') this.isConnecting = false this.lockSuccessBlock = undefined reject({ errCode: errorCode.connet_timeOut, errMsg: '关闭电门超时' }) } }, this.hardwareCallbackTimeOut ) } ) } temporaryUnlockBike() { return new Promise( (resolve, reject) => { this.temporaryUnlockSuccessBlock = resolve this.sendCommand(cmd.temporaryUnlockBike, '', reject); setTimeout( () => { console.log('---unlockBike-超时回调') if (this.temporaryUnlockSuccessBlock) { this.temporaryUnlockSuccessBlock = undefined console.log('---unlockBike-超时了--') this.isConnecting = false reject({ errCode: errorCode.connet_timeOut, errMsg: '开启电门超时' }) } }, this.hardwareCallbackTimeOut ) } ) } //发送关锁指令 temporaryLockBike() { return new Promise( (resolve, reject) => { console.log('---lockBike---') this.temporaryLockSuccessBlock = resolve this.sendCommand(cmd.temporaryLockBike, undefined, reject); setTimeout( () => { console.log('---lockBike-超时回调') // 硬件回调后 会把 lockSuccessBlock 清空,n秒后还没清空,就当指令发送失败 if (this.temporaryLockSuccessBlock) { console.log('---lockBike-超时了--') this.isConnecting = false this.temporaryLockSuccessBlock = undefined reject({ errCode: errorCode.connet_timeOut, errMsg: '关闭电门超时' }) } }, this.hardwareCallbackTimeOut ) } ) } batteryUnlockBike() { return new Promise( (resolve, reject) => { this.batterySuccessBlock = resolve this.sendCommand(cmd.batteryUnlock, '', reject); setTimeout( () => { console.log('---batteryUnlockBike-超时回调') if (this.batterySuccessBlock) { this.batterySuccessBlock = undefined console.log('---batteryUnlockBike-超时了--') this.isConnecting = false reject({ errCode: errorCode.connet_timeOut, errMsg: '开启电池锁超时' }) } }, this.hardwareCallbackTimeOut ) } ) } batteryLockBike() { return new Promise( (resolve, reject) => { this.batterySuccessBlock = resolve this.sendCommand(cmd.batteryLock, '', reject); setTimeout( () => { console.log('---batteryLockBike-超时回调') if (this.batterySuccessBlock) { this.batterySuccessBlock = undefined console.log('---batteryLockBike-超时了--') this.isConnecting = false reject({ errCode: errorCode.connet_timeOut, errMsg: '关启电池锁超时' }) } }, this.hardwareCallbackTimeOut ) } ) } // 发送心跳包 heartBeat() { let that = this return new Promise( (resolve, reject) => { that.heartSuccessBlock = resolve that.heartTimer = setTimeout( () => { if (that.heartSuccessBlock) { that.heartSuccessBlock = undefined reject({ errCode: errorCode.connet_timeOut, errMsg: '超时' }) } }, 30 * 1000 ) } ) } /*********action**********/ /*******util*********/ //发送指令 sendCommand(commandCode, dataArray, reject) { let data = this.generateCommandData(commandCode, dataArray); this._connectionFailBlock = undefined this._connectionFailBlock = reject var dataLen = Math.ceil(data.length / 40); console.log(dataLen, 'datalen') if (dataLen > 1) { //3 for (let i = 0; i < data.length; i += 40) { let value = util.hexStringToArrayBuffer(data.slice(i, i + 40)); console.log("分包发送的数据", data.slice(i, i + 40)) //使用了重发机制,在此不做定时处理 this.writeBLECharacteristicValue(value, reject); } } else { let value = util.hexStringToArrayBuffer(data); this.writeBLECharacteristicValue(value, reject); } } //发送数据 writeBLECharacteristicValue(data, reject) { console.log(this._deviceId, this._serviceId, this._characteristicId_write, '发送参数') wx.writeBLECharacteristicValue({ deviceId: this._deviceId, serviceId: this._serviceId, characteristicId: this._characteristicId_write, value: data, success: res => { console.log('writeBLECharacteristicValue success', res) }, fail: err => { // console.log('writeBLECharacteristicValue fail', err, this._deviceId, this._serviceId, this._characteristicId) // if(){ this.isConnecting = false; // } // reject(err) this.closeConnection(this._deviceId) } }) } /** * 根据指令码生成指令帧数据 * commandCode --> 指令码 * array --> 额为携带的数据 */ generateCommandData(commandCode, array) { let sequenceId_16 = dataTransition.getSequenceId(this._sequenceId); this._sequenceId++; let sendData = ''; switch (commandCode) { case cmd.unlock: sendData = '03 00 02 01 00'; break case cmd.lock: sendData = '03 00 01 01 01'; break; case cmd.bell: sendData = '03 00 04 01 01'; break; case cmd.batteryUnlock: sendData = '03 00 05 01 01'; break; case cmd.batteryLock: sendData = '03 00 05 01 00'; break; case cmd.temporaryUnlockBike: sendData = '03 00 02 01 00 07 01 01'; break case cmd.temporaryLockBike: sendData = '03 00 07 01 08'; break; case cmd.login: let secretKey = array.key.toString().trim().toLowerCase(); let c = secretKey.toString().replace(/\s+/g, ""); let cLength = dataTransition.getSecretKeyLength(c); //发送内容 let send = `02 00 01 ${cLength}`; //02 连接命令 01连接请求 cLength秘钥长度。 sendData = `${send} ${secretKey}`; console.log(sendData, 'sendValue') break; case cmd.status: sendData = '04 00 0b 01 00' break case cmd.ack: sendData = array.data break; } let header = dataTransition.header(sendData, 0, '00', sequenceId_16); if (commandCode == 'responseAck') { header = '' } let data = header + sendData.replace(/\s+/g, ""); console.log(`发送${commandCode}指令`, data); return data; } /*******util*********/ } // export { // BluetoothManager // } module.exports = { BluetoothManager: BluetoothManager, BtErrorCode: errorCode }