uni-app蓝牙开锁篇

 步骤:

 1.初始化蓝牙,
 2.开始搜寻附近的蓝牙外围设备,
 3.监听寻找到新设备的事件,
 4.搜寻到需要的蓝牙,停止搜寻附近的蓝牙外围设备,
 5.连接低功耗蓝牙设备,
 6.获取蓝牙设备所有服务 ,
 7.获取蓝牙设备某个服务中所有特征值,
 8.启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。
    注意:必须设备的特征值支持 notify 或者 indicate 才可以成功调用,
 9.向低功耗蓝牙设备特征值中写入二进制数据

实现方式一:

          uni.openBluetoothAdapter({//首先初始化蓝牙
                    success(res) {
                      console.log(JSON.stringify(res))
                      uni.startBluetoothDevicesDiscovery({//这里是开启蓝牙搜寻
                        success: (res) => {
                          console.log('startBluetoothDevicesDiscovery success', res)
                          uni.onBluetoothDeviceFound((res) => {//这一步是监听返回的蓝牙设备
                                  console.log(JSON.stringify(res))
                            res.devices.forEach(device => {//这一步就是去筛选找到的蓝牙中,有没有你匹配的名称
                              console.log(JSON.stringify(device))
                              if (device.name == 'XiaoanTech') {
                                        this.DeviceID = device.deviceId
                                        let DeviceID = device.deviceId//这里是拿到的uuid
                                uni.stopBluetoothDevicesDiscovery({//当找到匹配的蓝牙后就关掉蓝牙搜寻,因为蓝牙搜寻很耗性能
                                  success(res) {
                                    console.log(JSON.stringify(res))
                                  }
                                })
                                console.log(DeviceID)
                                uni.createBLEConnection({//连接低功耗蓝牙设备
                                  deviceId:DeviceID,//传入刚刚获取的uuid
                                  success(res) {
                                    console.log(JSON.stringify(res))
                                    //uni.getConnectedBluetoothDevices({
                                    //success(res) {
                                    //console.log(JSON.stringify(res))
                                    //}
                                    //})

                                    setTimeout(function(){//这里为什么要用setTimeout呢,等等下面会解释
                                            uni.getBLEDeviceServices({//获取蓝牙设备所有服务
                                                deviceId:DeviceID,
                                                success(res) {//为什么要用延时,因为不用延时就拿不到所有的服务,在上一步,连接低功耗蓝牙
//设备的时候,需要一个600-1000毫秒的时间后,再去获取设备所有服务,不给延时就会一直返回错误码10004                             
                                                    console.log(JSON.stringify(res))
                                                    uni.getBLEDeviceCharacteristics({//获取蓝牙设备某个服务中所有特征值
                                                        deviceId:DeviceID,
                                                        serviceId:this.ServiceUUID,//这个serviceId可以在上一步获取中拿到,也可以在
//蓝牙文档中(硬件的蓝牙文档)拿到,我这里是通过文档直接赋值上去的,一般有两个,一个是收的uuid,一个是发的uuid,我们这边是发
                                                        success(res) {
                                                            console.log(JSON.stringify(res))
                                                uni.notifyBLECharacteristicValueChange({
                                                  state: true, // 启用 notify 功能
                                                  // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
                                                  deviceId:DeviceID,
                                                  // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                                                  serviceId:this.ServiceUUID,
                                                  // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
                                                  characteristicId:self.characteristicId,
                                                  success(res) {
                                                    console.log('notifyBLECharacteristicValueChange success', res.errMsg)
                                                            uni.showToast({
                                                                title: '开启蓝牙连接',
                                                                duration: 2000
                                                                        });
                                                },
                                                fail(res) {
                                                        console.log(JSON.stringify(res))
                                                            }
                                                })
                                                },
                                                fail(res){
                                                 console.log(JSON.stringify(res)) 
                                                }
                                                            })
                                            },
                                            fail(res){
                                                    console.log(JSON.stringify(res)) 
                                            }
                                })
                                },1000)
                                  },
                                  fail(res) {
                                    console.log(res)
                                  }
                                })                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                              }
                            })
                          })
                        }
                      })
                    }, fail(res) {
                      console.log(res)
                      if (res.errCode == 10001) {
                        uni.showToast({
                          title: '蓝牙未打开',
                          duration: 2000,
                        })
                      } else {
                        uni.showToast({
                          title: res.errMsg,
                          duration: 2000,
                        })
                      }
                    }
                  })
  • 蓝牙连接成功后就是发送指令了,发送二进制数据
SendChange: function () {//开锁
                      var self = this
                    
                      // 向蓝牙设备发送一个0x00的16进制数据
                                let buffer = new ArrayBuffer(8)
                                let dataView = new DataView(buffer)
                                dataView.setUint8(0, 0x20)//开锁指令
                                dataView.setUint8(1, 0x05)//字节
                                dataView.setUint8(2, 0x0A)//指令
                                dataView.setUint8(3, 0x0A)//指令
                                dataView.setUint8(4, 0x05)//指令
                                dataView.setUint8(5, 0x05)//指令
                                dataView.setUint8(6, 0x00)//指令
                               //算法,逢10进1,A到F(或a~f)表示,其中:A~F表示10~15
                                //20等于32,32+05+10+10+5+5+0 = 67
                                dataView.setUint8(7, 0x43)
                                uni.writeBLECharacteristicValue({
                                    // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
                                    deviceId:self.DeviceID,
                                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                                    serviceId:self.ServiceUUID,
                                    // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
                                    characteristicId:self.characteristicId,
                                    // 这里的value是ArrayBuffer类型
                                    value: buffer,
                                    success(res) {
                                        console.log('writeBLECharacteristicValue success', res.errMsg)
                                        uni.showToast({
                                                title: '开锁',
                                                duration: 2000
                                        });
                                    },
                                    fail(res) {
                                        console.log(JSON.stringify(res))
                                        console.log(JSON.stringify(buffer))
                                    }
                                })
                    },
                    CloseChange: function () {//关锁
                      var self = this
                    
                      // 向蓝牙设备发送一个0x00的16进制数据
                                let buffer = new ArrayBuffer(8)
                                let dataView = new DataView(buffer)
                                dataView.setUint8(0, 0x20)
                                dataView.setUint8(1, 0x05)
                                dataView.setUint8(2, 0x0A)
                                dataView.setUint8(3, 0x0A)
                                dataView.setUint8(4, 0x05)
                                dataView.setUint8(5, 0x05)
                                dataView.setUint8(6, 0x01)
                                dataView.setUint8(7, 0x44)
                                uni.writeBLECharacteristicValue({
                                    // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
                                    deviceId:self.DeviceID,
                                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
                                    serviceId:self.ServiceUUID,
                                    // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
                                    characteristicId:self.characteristicId,
                                    // 这里的value是ArrayBuffer类型
                                    value: buffer,
                                    success(res) {
                                        console.log('writeBLECharacteristicValue success', res.errMsg)
                                        uni.showToast({
                                                title: '关锁',
                                                duration: 2000
                                        });
                                    },
                                    fail(res) {
                                        console.log(JSON.stringify(res))
                                        console.log(JSON.stringify(buffer))
                                    }
                                })
                    },

实现方式二:

export enum BTResultEnum {
  // 适配器打开成功
  ADAPTER_OPEN_SUCCESS = 0x01,
  // 适配器打开失败
  ADAPTER_OPEN_FAILED = 0x1001,
  // 适配器关闭成功
  ADAPTER_CLOSE_SUCCESS = 0x02,
  // 适配器关闭失败
  ADAPTER_CLOSE_FAILED = 0x1002,
  
  // 搜索设备成功
  SEARCH_DEVICE_SUCCESS=0x105,
  // 搜索设备失败
  SEARCH_DEVICE_FAILED=0x805,
  // 连接成功
  CONNECT_SUCCESS = 0x03,
  // 链接失败
  CONNECT_FAILED = 0x1003,
  // 写数据成功
  WRITE_SUCCESS = 0x04,
  //写数据失败
  WRITE_FAILED = 0x1004,
  SUB_VALUE_CHANGE_SUCCESS=0x05,
  SUB_VALUE_CHANGE_FAILED=0x1005,
  CHARACTERISTIC_SUCCESS=0X06,
  CHARACTERISTIC_FAILED=0x1006,
}

export type BluetoothParam = {
	/**
	 * 设备MAC地址
	 */
	deviceMac : string;
	/**
	 *  主服务UID
	 */
	serviceUuid : string;
	/**
	 * 搜索超时时间, 默认10秒钟
	 */
	searchTimeout:number;
	/**
	 * 通知服务ID
	 */
	notifyCharacteristicId ?: string;
	/**
	 * 写服务UID
	 */
	writeCharacteristicId ?: string;
	/**
	 * 读服务UID
	 */
	readCharacteristicId ?: string;
}

export abstract class BtUpProtocol{
    abstract parse(data:Int8Array):void;
}

export abstract class BtDownProtocol{
    abstract build():Int8Array;
}

export interface BluetoothStateOptions{
    /**
     * 接口调用的回调函数
     */ 
     onStateChange: (result: any) => void;
 }
 
export interface BluetoothValueOptions{
    onValue: (result:BtUpProtocol) => void;
 }

export default AbsCodec{
    keyInfo:{[key:number]:  BtUpProtocol}={};
    secret:string = "";
    public constructor() {
        this.initProto();
    }

    /**
     * 初始化协议信息
     */
    abstract initProto():void;
    abstract decode(data:Int8Array):BtUpProtocol;
    abstract encode(proto:BtDownProtocol):Int8Array;
    abstract getCodeKey(data:Int8Array):number;
}


export function num2Hex(byte:number):string{
	let hex= byte.toString(16);
	if(hex.length<2){
		return '0'+hex;
	}
	return hex;
}
import { BTResultEnum } from "@/enums/BTResultEnum";
import type { BluetoothParam } from "@/bluetooth/BluetoothParam";
import type {
	BtDownProtocol,
	BtUpProtocol,
} from "@/bluetooth/protocol/Protocol";
import type {
	BluetoothStateOptions,
	BluetoothValueOptions,
} from "./bluetoothListener";
import type AbsCodec from "@/bluetooth/Codec";
import { num2Hex } from "@/utils/ByteConverter";

/**
 * BluetoothModule 类用于管理蓝牙连接及其相关操作。
 * 它封装了微信小程序提供的蓝牙API,用于与蓝牙设备进行通信。
 */
class BluetoothModule {
	deviceId: string = "";   // 设备MAC地址
	connected: boolean = false;   // 连接状态,表示是否已连接
	params?: BluetoothParam;    // 蓝牙连接的参数
	serviceId: string = "";   // 服务UUID,用于标识蓝牙服务
	// 特征值UUID(读、写、通知)数据
	readCharacteristicId: string = "";
	writeCharacteristicId: string = "";
	notifyCharacteristicId: string = "";
	stateListener?: BluetoothStateOptions; // 状态监听器,用于监听蓝牙连接状态变化
	valueListener?: BluetoothValueOptions; // 值监听器,用于监听蓝牙数据变化
	codec?: AbsCodec;         // 编解码器,用于对蓝牙数据进行编码和解码
	constructor() { }


	/**
	   * 设置蓝牙参数。
	   * @param params 包含蓝牙连接所需的参数(设备MAC地址、服务UUID等)。
	   */
	setParams(params: BluetoothParam) {
		//this.deviceId = params.deviceId;
		this.params = params;
		this.readCharacteristicId = "";
		this.writeCharacteristicId = "";
		this.notifyCharacteristicId = "";
	}

	/**
	  * 设置编解码器,用于对蓝牙数据进行编码和解码。
	  * @param codec 编解码器对象。
	  */
	setCodec(codec: AbsCodec) {
		this.codec = codec;
	}

	/**
	   * 设置状态监听器,用于监听蓝牙连接状态的变化。
	   * @param listener 状态监听器对象。
	   */
	setStateListener(ler: BluetoothStateOptions) {
		this.stateListener = ler;
	}

	/**
	  * 设置值监听器,用于监听蓝牙数据的变化。
	  * @param listener 值监听器对象。
	  */
	setValueListener(ler: BluetoothValueOptions) {
		this.valueListener = ler;
	}

	/**
	  * 打开蓝牙适配器,初始化蓝牙模块,成功或失败后会调用状态监听器的相应回调方法。
	  */
	async openAdapter() {
		const that = this;
		wx.openBluetoothAdapter({
			success(res) {
				// 通知状态监听器蓝牙适配器打开成功
				that.stateListener?.onStateChange(BTResultEnum.ADAPTER_OPEN_SUCCESS);
			},
			fail(res) {
				// 通知状态监听器蓝牙适配器打开失败
				that.stateListener?.onStateChange(BTResultEnum.ADAPTER_OPEN_FAILED);
			},
		});
	}
	/*   ab2hex(buffer) {
		var hexArr = Array.prototype.map.call(
		  new Uint8Array(buffer),
		  function(bit) {
			return ('00' + bit.toString(16)).slice(-2)
		  }
		)
		return hexArr.join('');
	  } */
	/**
	 * 监听发现的设备信息。
	 * 当找到设备时,会检查其MAC地址是否匹配,如果匹配则停止搜索并尝试连接该设备。
	 */
	async onFoundDevices() {
		let that = this;
		wx.onBluetoothDeviceFound(function(res) {
			var devices = res.devices;
			console.log('new device list has founded')
			console.dir(devices)
			// 设备数据转成 Unit 8 
			const avdData = new Uint8Array(devices[0].advertisData);
			// 读取MAC地址,并转换为字符串格式
			const mac: string = num2Hex(avdData[2]) + ':' + num2Hex(avdData[3]) + ':' + num2Hex(avdData[4])
				+ ':' + num2Hex(avdData[5]) + ':' + num2Hex(avdData[6]) + ':' + num2Hex(avdData[7]);
			// 如果发现的设备MAC地址与目标设备MAC地址匹配
			if (mac.toUpperCase() === that.params?.deviceMac.toUpperCase()) {
				that.deviceId = devices[0].deviceId;
				// 停止搜索其他设备。
				that.stopSearchDevices();
				//that.stateListener?.onStateChange(BTResultEnum.CONNECT_FAILED);
				// 开始连接到找到的设备
				that.connect();
			}
			//console.log(that.ab2hex(devices[0].advertisData))
		})
	}

	/**
	 * 停止搜索蓝牙设备
	 */
	async stopSearchDevices() {
		wx.stopBluetoothDevicesDiscovery({
			success(res) {

			}
		})
	}

	/**
	  * 开始搜索蓝牙设备。
	  * @param params  蓝牙参数对象(设备MAC地址、服务UUID等)
	  */
	async startSearchDevices(params: BluetoothParam) {
		let that = this;
		// 监听发现的设备信息
		this.onFoundDevices();
		// 设置蓝牙参数
		this.setParams(params);
		// 开始搜索蓝牙设备
		wx.startBluetoothDevicesDiscovery({
			services: [params.serviceUuid],
			success(res) {
			},
			fail(err) {
				// 通知状态监听器连接失败
				that.stateListener?.onStateChange(BTResultEnum.CONNECT_FAILED);
			}
		})
	}


	/**
	 * 连接到蓝牙设备,连接成功后获取设备的服务UUID和特征值UUID。
	 */
	async connect() {
		//params: BluetoothParam
		let that = this;
		// 订阅设备连接状态变化
		this.subConnectionState();
		// this.setParams(params);
		// 创建蓝牙连接

		//为什么要用延时,因为不用延时就拿不到所有的服务,在createBLEConnection中,连接低功耗蓝牙
		//设备的时候,需要一个600-1000毫秒的时间后,再去获取设备所有服务,不给延时就会一直返回错误码10004
		wx.createBLEConnection({
			deviceId: that.deviceId,
			timeout: 6000,
		}).then(
			(res) => {
				// // 连接成功后,获取设备的服务UUID
				// that.connected = true;
				// that.stateListener?.onStateChange(BTResultEnum.CONNECT_SUCCESS);
				wx.getBLEDeviceServices({
					deviceId: that.deviceId,
				}).then(
					(res) => {
						let suid = "";
						if (that.params) {
							suid = that.params.serviceUuid;
						}
						// 遍历设备的服务UUID,找到匹配的服务UUID
						for (var sid in res.services) {
							if (res.services[sid].uuid.startsWith(suid)) {
								that.serviceId = res.services[sid].uuid;
								break;
							}
						}
						// 获取服务的特征值UUID
						wx.getBLEDeviceCharacteristics({
							deviceId: that.deviceId,
							serviceId: that.serviceId,
						}).then(
							(res1) => {
								// 遍历通道特征UUID,分别匹配到读、写、通知的特征值UUID
								for (var cid in res1.characteristics) {
									var characteristic = res1.characteristics[cid];
									// 匹配到写服务ID
									if (characteristic.uuid.startsWith("000036F5") == true) {
										that.writeCharacteristicId = characteristic.uuid;
									}
									// 匹配到通知服务ID
									if (characteristic.uuid.startsWith("000036F6") == true) {
										that.notifyCharacteristicId = characteristic.uuid;
									}
								}
								//  启用通知以接收设备的特征值变化
								that.notifyValue();
								// 通知状态监听器特征值获取成功
								that.stateListener?.onStateChange(BTResultEnum.CHARACTERISTIC_SUCCESS);
							},
							(err1) => {
								// 通知状态监听器特征值获取失败
								that.stateListener?.onStateChange(BTResultEnum.CHARACTERISTIC_FAILED);
							}
						);
					},
					(err) => {
						// 通知状态监听器连接失败
						that.stateListener?.onStateChange(BTResultEnum.CONNECT_FAILED);
					}
				);
			},
			(err) => {
				// 通知状态监听器连接失败
				that.stateListener?.onStateChange(BTResultEnum.CONNECT_FAILED);
			}
		);
	}

	/**
	   * 写数据到蓝牙设备。
	   * @param data 要写入的数据,按照协议进行封装。
	   */
	async write(data: BtDownProtocol) {
		const that = this;
		// 编码数据
		const da = this.codec?.encode(data);
		if (da) {
			// 写入数据到蓝牙特征值
			wx.writeBLECharacteristicValue({
				// deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
				deviceId: that.deviceId,
				// serviceId 需要在 getBLEDeviceServices 接口中获取
				serviceId: that.serviceId,
				// characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
				characteristicId: that.writeCharacteristicId,
				// value是ArrayBuffer类型
				value: da.buffer,
			}).then(
				(res) => {
					// 通知状态监听器写入成功
					that.stateListener?.onStateChange(BTResultEnum.WRITE_SUCCESS);
				},
				(err) => {
					// 通知状态监听器写入失败
					that.stateListener?.onStateChange(BTResultEnum.WRITE_FAILED);
				}
			);
		}
	}

	/**
	 * 订阅设备连接状态监听。
	 * 监听设备连接状态的变化。
	 */
	async subConnectionState() {
		const that = this;
		wx.onBLEConnectionStateChange((res) => {
			that.connected = res.connected;
			if (that.connected) {
				// 通知状态监听器连接成功
				that.stateListener?.onStateChange(BTResultEnum.CONNECT_SUCCESS);
			} else {
				// 通知状态监听器连接断开
				that.stateListener?.onStateChange(BTResultEnum.CONNECT_FAILED);
			}
		});
	}



	/**
	  * 启用通知以接收设备的特征值变化。
	  * 监听设备特征值的变化,并处理接收到的数据。
	  */
	async notifyValue() {
		const that = this;
		wx.onBLECharacteristicValueChange((res) => {
			// 解码接收到的数据
			const decodeRes: BtUpProtocol | undefined = that.codec?.decode(
				new Int8Array(res.value)
			);
			if (decodeRes) {
				// 通知值监听器数据变化
				that.valueListener?.onValue(decodeRes);
			}
		});
		// 启用特征值通知
		wx.notifyBLECharacteristicValueChange({
			// deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
			deviceId: that.deviceId,
			// serviceId 需要在 getBLEDeviceServices 接口中获取
			serviceId: that.serviceId,
			// characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
			characteristicId: that.notifyCharacteristicId,
			state: true,
			type: "notification",
		}).then(
			(res) => {
				console.log("notifyBLECharacteristicValueChange:", res);

			},
			(err) => {
				console.log("notifyBLECharacteristicValueChange:", err);
			}
		);
	}


	/**
	 * 断开与蓝牙设备的连接。
	 */
	async disConnection() {
		let that = this;
		wx.closeBLEConnection({
			deviceId: that.deviceId,
		})
	}


	/**
	 * 关闭蓝牙适配器,释放资源。
	 */
	async closeAdapter() {
		wx.closeBluetoothAdapter();
	}
}

export default BluetoothModule;

前者

  • 优点

    • 代码简单直观,直接使用了微信小程序的API,没有封装复杂的逻辑。
    • 适合简单的蓝牙连接和操作场景。
  • 缺点

    • 缺乏结构化和模块化,代码不够灵活和可维护。
    • 对于大型项目或复杂的蓝牙操作,不易扩展和复用。

后者

  • 优点

    • 代码模块化、结构清晰,封装了蓝牙连接及其相关操作,易于维护和扩展。
    • 使用了面向对象的设计思想,方便管理蓝牙连接的状态和参数。
    • 提供了较好的解耦性,使得各个部分的逻辑更加独立。
  • 缺点

    • 代码相对复杂,需要理解类和对象的使用,可能对初学者不太友好。
    • 需要更多的前期开发工作来实现这些封装。

建议

  • 如果你的项目较为简单,只需要基本的蓝牙连接和操作,且不需要进行太多的维护和扩展,前者可能更合适。
  • 如果你的项目较为复杂,需要频繁的蓝牙操作,且需要良好的可维护性和可扩展性,后者更为合适。

总结

  • 前者适用于简单、一次性的蓝牙应用。
  • 后者适用于复杂、长期维护的蓝牙应用。
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值