概况:手机APP发送16进制指令至蓝牙犬牌,犬牌响应返回结果,功能为计步,犬牌电量,版本号,系统时间,灯光颜色等。

步骤

  1. 打开蓝牙
  2. 连接设备
  3. 扩大传输MTU
  4. 获取蓝牙服务
  5. 获取蓝牙设备某个服务中所有特征值(characteristic)
  6. 当找到同时有读 写 订阅 权限的特征值时启用低功耗蓝牙设备特征值变化时的 notify功能
  7. 订阅特征值开启成功后写入指令
  8. 写入成功后读取二进制数据值(此时开启的notify功能就会监听并返回设备响应数据)
  9. 接收设备响应数据

注意事项

16进制指令严格按照蓝牙协议数据包结构发送,此项目关联的包结构为帧头、协议头、版本、命令、数据区长度、数据、校验crc、帧尾。各自转成相应的十六进制,通过arraybuff数组的形式发送。

此项目crc校验规则为从帧头到校验前,字节累加。打开电脑计算器16进制累加即可。

如下图,数据长度则表示为0x01, 0x00,0x01,0x01  最后一个0x01 为占位。

关于发送一次指令 uni.onBLECharacteristicValueChange监听两次的问题,api bug官方暂未解决,解决方法,加一个变量,发送指令前置空变量,api回调中赋值,判断第二次return掉。

checkBluetooth() {
				let that = this
				uni.openBluetoothAdapter({
					success(res) { //蓝牙已打开
						uni.startBluetoothDevicesDiscovery({ //开始搜寻附近的蓝牙外围设备
							services: [],
							success(res) {
								setTimeout(() => {
									uni.onBluetoothDeviceFound(function(ret) {
										that.list = that.list.concat(ret.devices).filter(item=>item.name!="")
										// 对搜索到的蓝牙设备去重
										for(let i=0;i<that.list.length;i++) {
											for(let j=i+1;j<that.list.length;j++) {
												if(that.list[i].name==that.list[j].name) {
													that.list.splice(j,1)
												}
											}
										}
										that.isLoading = false
										if (that.inputValue == "") {
											that.equipmentList = that.list
										} else {
											that.equipmentList = that.list.filter(item =>
												item.name.indexOf(that.inputValue) > -1
												)
										}
									})
								},2000) 
							}
						})
					},
					fail(err) {
						if (err.errCode == "10001") {
							// 蓝牙未打开 提示
							that.openPhoneBluetooth()
						}
					}
				})
			},

// 连接设备
			connect(item) {
				let that = this
				console.log('--item--',item)
				uni.createBLEConnection({
					// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
					deviceId: item.deviceId,
					success(res) {
						that.deviceId = item.deviceId
						that.isLink = true
						that.updateBtnStatus()
						// 扩大传输MTU
						that.setMaxMTU()
						//获取蓝牙服务 不加延时返回结果立即为空
						that.getBLEDeviceServices(async slist => {
							for (let s of slist) {
								let bdc = await that.getBLEDeviceCharacteristics(s.uuid);
								// 找到特征值
								if (bdc.writeId && bdc.notifyId && bdc.readId) {
									that.writeUUid = bdc.writeId
									that.notifyUUid = bdc.notifyId
									that.readUUid = bdc.readId
									uni.notifyBLECharacteristicValueChange({ //启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值
										deviceId:that.deviceId,
										serviceId: s.uuid,
										characteristicId: bdc.notifyId,
										state: true,
										success: (res) => {
											that.onBLECharacteristicValueChange();
											console.log("订阅特征值开启成功")
												setTimeout(()=>{
													that.orderType = 'vision'
													that.writeBLECharacteristicValue(ORDERS.VisionInfo);
													setTimeout(()=>{
														that.orderType = 'systemtime'
														that.writeBLECharacteristicValue(ORDERS.SystemTime)
													},1500)
												},3000)
										},
										fail: (err) => {
											console.error(err)
										}
									})
									// setTimeout(()=>{
									// 	that.writeBLECharacteristicValue()
									// },2000)
									break;
								}
							}
						});
					},
					fail(err) { //失败断开连接
						that.closeBLEConnect(item.deviceId)
					}

				})
			},

 其他部分代码:

setMaxMTU() {
				let _this = this
				uni.setBLEMTU({
					deviceId: _this.deviceId,
					
					mtu: 200,
					success(res) {
						console.log("设置最大值成功")
					}
				})
			},
			//获取蓝牙设备所有服务(service)
			getBLEDeviceServices(callback) {
				let _serviceList = [];
				let deviceId = this.deviceId || uni.getStorageSync("deviceId").toString();
				setTimeout(() => { //解决app不能得到服务的bug,500-1000
					uni.getBLEDeviceServices({
						deviceId,
						success: res => {
							for (let service of res.services) {
								// console.log(service)
								if (service.isPrimary) {
									_serviceList.push(service);
								}
							}
							console.log("_serviceList: ",_serviceList);
							if (typeof callback == "function") callback(_serviceList)
						},
						fail: err => {
							console.log(err)
						}
					});
				}, 1500);
			},
			//获取蓝牙设备某个服务中所有特征值(characteristic)
			getBLEDeviceCharacteristics(serviceId) {
				let deviceId = this.deviceId;
				this.serviceUUID = serviceId;
				// console.log('serviceId', serviceId);
				//缓存服务Id
				uni.setStorageSync('serviceId', serviceId);
				let readId="", writeId="", notifyId="", indicateId = ""
				return new Promise((resolve, reject) => {
					uni.getBLEDeviceCharacteristics({
						deviceId:deviceId,
						serviceId:serviceId,
						success: res => {
							for (let _obj of res.characteristics) {
								//获取readId
								if (_obj.properties.read&&readId=="") {
									readId = _obj.uuid;
								}
								if (_obj.properties.write&&writeId=="") {
									writeId = _obj.uuid;
								}
								uni.setStorageSync('writeId', writeId);
								//获取notifyId
								if (_obj.properties.notify&&notifyId=="") {
									notifyId = _obj.uuid;
								}
								//获取indicateId
								if (_obj.properties.indicate&&indicateId=="") {
									indicateId = _obj.uuid;
								}
								uni.setStorageSync('indicateId', indicateId);
							}
							let result = {
								'readId': readId,
								'writeId': writeId,
								'notifyId': notifyId,
								'indicateId': indicateId
							};
							console.log('获取AAAA:', JSON.stringify(result));
							resolve(result)
						},
						fail: err => {
							reject(err);
						}
					})
				});
			},
			
			
			hexStringToArrayBuffer(str) {
			                if (!str) {
			                    return new ArrayBuffer(0);
			                }
			                var buffer = new ArrayBuffer(str.length);
			                let dataView = new DataView(buffer)
			                let ind = 0;
			                for (var i = 0, len = str.length; i < len; i += 2) {
			                    let code = parseInt(str.substr(i, 2), 16)
			                    dataView.setUint8(ind, code)
			                    ind++
			                }
			                return buffer;
			            },
		//写入指令
			writeBLECharacteristicValue(arrayBuffer) {
				let that = this 
				let deviceId = that.deviceId;
				let serviceId = that.serviceUUID;
				// let serviceId = '00010203-0405-0607-0809-0A0B0C0D1910';
				let characteristicId = that.writeUUid;
				const buffer = new ArrayBuffer(arrayBuffer.length)      //ArrayBuffer是字节数组
				const dataView = new DataView(buffer)                   //通过DataView对象来操作字节数组
				for (let i=0;i<arrayBuffer.length;i++){
				        dataView.setUint8(i, arrayBuffer[i]);
					}
				uni.writeBLECharacteristicValue({
					deviceId,
					serviceId,
					characteristicId,
					value: buffer,
					success(res) {
						console.log('发送指令成功:' + arrayBuffer);
						that.bugServiceId = ''
						that.readBLECharacteristicValue();
					},
					fail(err) {
						console.log('发送指令失败', JSON.stringify(err));
						//self.showToast("Sending failure", 800);
					}
				});
			},
			
			//读取设备的特征值的二进制数据值
			readBLECharacteristicValue(){
				let that = this 
				uni.readBLECharacteristicValue({
				  // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
				  deviceId:that.deviceId,
				  // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
				  serviceId:that.serviceUUID,
				  // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
				  characteristicId:that.readUUid,
				  success(res) {
				    console.log('----读取----:', res)
				  }
				})
			},
			// 监听低功耗蓝牙设备的特征值变化事件 接收到设备推送的 notification
			onBLECharacteristicValueChange() {
				let that = this 
				uni.onBLECharacteristicValueChange(res => {
					// 去除每个指令二次空监听,此为api bug
					if(that.bugServiceId) return false
					that.bugServiceId = res.serviceId
					if(that.orderType== 'quanpaidianliang'){  //犬牌电量
						let data = that.ab2hex(res.value);
						 let num = data.slice(-6,-4)
						let dianliang = that.hex2int(num)
						that.$set(that.btnList,1,{
								btn_name: '犬牌电量' + dianliang + '%',
								isDisabled: false
						})
						console.log('返回',dianliang);
					}else if(that.orderType == 'vision'){  //版本号
						that.ab2str(res.value,'vision')
					}else if(that.orderType == 'systemtime'){   //系统时间同步
						let sTime = that.ab2hex(res.value,'systemtime')
						console.log('返回',sTime);
						if(sTime){
							plus.nativeUI.toast("时间同步成功");
						}
					}else if (that.orderType == 'StepCounting'){
						let walks = that.ab2hex(res.value,'StepCounting')
						console.log('返回计步',walks);
						let useNums = walks.toString().slice(12,-4)
						let oneDay = useNums.slice(0,8)/1000000    //今天
						let twoDay = useNums.slice(8,16)/1000000    //昨天
						let threeDay = useNums.slice(16,24)/1000000    //前天
						console.log('66',oneDay,twoDay,threeDay);
						uni.$emit('walks', {
							'oneDay':oneDay,
							'twoDay':twoDay,
							'threeDay':threeDay,
						});
					}
				})
			},
			
			 // ArrayBuffer转16进度字符串示例 
			ab2hex(buffer) {
				const hexArr = Array.prototype.map.call(
					new Uint8Array(buffer),
					function(bit) {
						return ('00' + bit.toString(16)).slice(-2)
					}
				)
				return hexArr.join('')
			},

hex2int(hex) {   //16进制转10进制
			    var len = hex.length, a = new Array(len), code;
			    for (var i = 0; i < len; i++) {
			        code = hex.charCodeAt(i);
			        if (48<=code && code < 58) {
			            code -= 48;
			        } else {
			            code = (code & 0xdf) - 65 + 10;
			        }
			        a[i] = code;
			    }
			     
			    return a.reduce(function(acc, c) {
			        acc = 16 * acc + c;
			        return acc;
			    }, 0);
			},

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐