介绍:
本文采用uni-app框架来创建一个简单的用户界面,用于搜索、连接和发送命令给蓝牙设备。
1.打开蓝牙适配器
function openBluetooth() {uni.openBluetoothAdapter({success() {uni.offBluetoothDeviceFound();// 监听新设备发现事件uni.onBluetoothDeviceFound((res) => {res.devices.forEach(device => {if (device.name && device.name.startsWith('one-parking')) {const existingDevice = devicesInfo.value.find(d => d.deviceId === device.deviceId);if (!existingDevice) {devicesInfo.value.push(device);}}});});// 开始搜索蓝牙设备uni.startBluetoothDevicesDiscovery({success() {console.log('开始搜索蓝牙设备...');// 设置一个定时器,在一定时间后停止搜索setTimeout(stopDiscovery, 10000);},fail(err) {console.error('开始搜索失败', err);}});},fail() {uni.showToast({title: '请打开蓝牙',icon: 'none'});}});}
2.连接蓝牙
function connectBluetooth( data : any ) {bluetoothInfo.value = data;bluetoothInfo.value.deviceId = data.deviceId;uni.createBLEConnection({deviceId : data.deviceId,success() {// 判断连接的状态和断开重新连接checkSttusOrReconnect(data);// 获取蓝牙的服务ServiceIDuni.getBLEDeviceServices({deviceId : data.deviceId,success(servicesRes : any) {if(servicesRes.services.length > 0) {bluetoothInfo.value.serviceId = servicesRes.services[0].uuid;// 获取蓝牙的特征值characteristicIduni.getBLEDeviceCharacteristics({deviceId : data.deviceId,serviceId : servicesRes.services[0].uuid,success(resCharacter) {if(resCharacter.characteristics.length > 0) {bluetoothInfo.value.characteristics = resCharacter.characteristics;// 接受通知notify();let macAddress = data.name.split('-')[1];let password = calculatePassword(macAddress);let bondCommand = `phbond${password}`;sendMsg(bondCommand);}}})}}});}})}
提示:这里根据蓝牙的设备id连接后,一起获取了服务和特征id,用于方便后面蓝牙进行通信,其中的特征值是一个重点,不同的特征值id会对应不同的操作,不然就会操作不了其他的api,比如uni.notifyBLECharacteristicValueChange(OBJECT)这个就需要有notify 或者 indicate 才可以成功调用
3.监听蓝牙的通知
// 接收蓝牙端发送的通知function notify() {uni.notifyBLECharacteristicValueChange({state: true,// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接deviceId : bluetoothInfo.value.deviceId,serviceId: bluetoothInfo.value.serviceId,characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.notify).uuid,success() {monitorMsg();},fail(res) {console.log('不支持这个notify' + JSON.stringify(res))}})}function monitorMsg() {uni.onBLECharacteristicValueChange((res : any) => {console.log("发送过来的数据---------", res);let resHex = ab2ascii(res.value)console.log("转换后的简易数据----------",resHex);});}
提示: 刚刚在上面说到的点就是特征值id有多个,需要有对应的特征权限
4.发送数据
// 向蓝牙写数据function sendMsg( msg : string ) {uni.writeBLECharacteristicValue({deviceId : bluetoothInfo.value.deviceId,serviceId: bluetoothInfo.value.serviceId,characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.write).uuid,value: asciiToArrayBuffer(msg) as any,writeType: 'write',success(res) {console.log('消息发送成功', res)},fail(res) {console.log('消息发送失败', res)}});}
注意:其中写数据需要向设备写的是二进制,所以需要转换一下
// 二进制转化工具function ab2ascii(buffer : ArrayBuffer) {var str = Array.prototype.map.call(new Uint8Array(buffer),function(bit : any) {return String.fromCharCode(bit);})return str.join('');}function asciiToArrayBuffer(str : string) {if (!str) {return new ArrayBuffer(0);}var buffer = new ArrayBuffer(str.length);var bufView = new Uint8Array(buffer);for (var i = 0, strLen = str.length; i < strLen; i++) {bufView[i] = str.charCodeAt(i);}return buffer;}
5.监听蓝牙的状态并发现断开重连
function checkSttusOrReconnect( data : any ) {uni.onBLEConnectionStateChange(function (state) {if (!state.connected) {console.warn('与设备' + data.name + '的连接已断开');if(breakCount.value < 2) {reconnectionTimer = setTimeout(()=>{connectBluetooth(bluetoothInfo.value);},3000);}breakCount.value += 1;} else {console.log('与设备 ' + data.name + ' 的连接已建立');if(reconnectionTimer) { clearTimeout(reconnectionTimer) }breakCount.value = 0;}});
}
6.完整的demo代码(设备是车辆地锁)
<template><view class="pageBox"><view style="font-size: 22px;text-align: center;margin-top: 32px;">蓝牙测试用例</view><view style="margin: 12px;display: flex;flex-direction: column;"><radio-group style="margin: 12px;gap: 26px;display: flex;flex-wrap: wrap;padding: 6px;"><radio value="phup" @click="cmdText = 'phup'">phup(升)</radio><radio value="phdown" @click="cmdText = 'phdown'">phdown(降)</radio><radio value="phstatus" @click=" cmdText = 'phstatus'">phstatus(状态)</radio></radio-group><button @click="sendControllCmd()" style="background-color: #564DD6;color: #fff;border-radius: 26px;margin: 12px;">发送指令</button></view><view style="margin: 22px;font-size: 18px;font-weight: bold;color: royalblue;">蓝牙设置信息:</view><view v-for="devices in devicesInfo"><view style="justify-content: space-between;border-radius: 8px;display: flex;margin: 12px;margin:12px 22px;background-color: #fff;border: 2px dashed #564DD6;padding: 12px;"><view style="display: flex;flex-direction: column;gap: 6px;"><view>设备名称:{{devices.name}}</view><view>ID: {{devices.deviceId}}</view></view><view><button style="margin: 6px;border-radius: 6px;background-color: #9A2CD7;color: #fff;" size="mini" @click="connectBluetooth(devices)">连接</button></view></view></view></view>
</template><script setup lang="ts">import { onReady } from "@dcloudio/uni-app";import { ref } from "vue";onReady(()=>{openBluetooth();});let devicesInfo = ref([]);let reconnectionTimer : any;let breakCount = ref<number>(0);function openBluetooth() {uni.openBluetoothAdapter({success() {uni.offBluetoothDeviceFound();// 监听新设备发现事件uni.onBluetoothDeviceFound((res) => {res.devices.forEach(device => {if (device.name && device.name.startsWith('one-parking')) {const existingDevice = devicesInfo.value.find(d => d.deviceId === device.deviceId);if (!existingDevice) {devicesInfo.value.push(device);}}});});// 开始搜索蓝牙设备uni.startBluetoothDevicesDiscovery({success() {console.log('开始搜索蓝牙设备...');// 设置一个定时器,在一定时间后停止搜索setTimeout(stopDiscovery, 10000);},fail(err) {console.error('开始搜索失败', err);}});},fail() {uni.showToast({title: '请打开蓝牙',icon: 'none'});}});}function stopDiscovery() {uni.stopBluetoothDevicesDiscovery({success() {console.log('停止蓝牙嗅探成功');},fail(err) {console.error('停止蓝牙嗅探失败', err);}});}// 地锁密码获取: 提取MAC地址的最后6位转换为十进制 , 加上520168 , 取结果的最后六位function calculatePassword(mac : string) {let lastSixHex = mac.slice(-6);let hexToInt = parseInt(lastSixHex, 16);let sum = hexToInt + 520168;let finalPassword = ('000000' + sum).slice(-6);return finalPassword;}let bluetoothInfo = ref({deviceId: '',serviceId: '',characteristics: [],devicesInfo: {} // 连接的蓝牙设备});function connectBluetooth( data : any ) {bluetoothInfo.value = data;bluetoothInfo.value.deviceId = data.deviceId;uni.createBLEConnection({deviceId : data.deviceId,success() {// 判断连接的状态和断开重新连接checkSttusOrReconnect(data);// 获取蓝牙的服务ServiceIDuni.getBLEDeviceServices({deviceId : data.deviceId,success(servicesRes : any) {if(servicesRes.services.length > 0) {bluetoothInfo.value.serviceId = servicesRes.services[0].uuid;// 获取蓝牙的特征值characteristicIduni.getBLEDeviceCharacteristics({deviceId : data.deviceId,serviceId : servicesRes.services[0].uuid,success(resCharacter) {if(resCharacter.characteristics.length > 0) {bluetoothInfo.value.characteristics = resCharacter.characteristics;// 接受通知notify();let macAddress = data.name.split('-')[1];let password = calculatePassword(macAddress);let bondCommand = `phbond${password}`;sendMsg(bondCommand);}}})}}});}})}// 检查设备并重连function checkSttusOrReconnect( data : any ) {uni.onBLEConnectionStateChange(function (state) {if (!state.connected) {console.warn('与设备' + data.name + '的连接已断开');if(breakCount.value < 2) {reconnectionTimer = setTimeout(()=>{connectBluetooth(bluetoothInfo.value);},3000);}breakCount.value += 1;} else {console.log('与设备 ' + data.name + ' 的连接已建立');if(reconnectionTimer) { clearTimeout(reconnectionTimer) }breakCount.value = 0;}});}// 接收蓝牙端发送的通知function notify() {uni.notifyBLECharacteristicValueChange({state: true,// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接deviceId : bluetoothInfo.value.deviceId,serviceId: bluetoothInfo.value.serviceId,characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.notify).uuid,success() {monitorMsg();},fail(res) {console.log('不支持这个notify' + JSON.stringify(res))}})}function monitorMsg() {uni.onBLECharacteristicValueChange((res : any) => {console.log("发送过来的数据---------", res);let resHex = ab2ascii(res.value)console.log("转换后的简易数据----------",resHex);});}let cmdText = ref('');function sendControllCmd() {console.log('发送的指令:' + cmdText.value);sendMsg(cmdText.value);}// 向蓝牙写数据function sendMsg( msg : string ) {uni.writeBLECharacteristicValue({deviceId : bluetoothInfo.value.deviceId,serviceId: bluetoothInfo.value.serviceId,characteristicId : bluetoothInfo.value.characteristics.find(item => item.properties.write).uuid,value: asciiToArrayBuffer(msg) as any,writeType: 'write',success(res) {console.log('消息发送成功', res)},fail(res) {console.log('消息发送失败', res)}});}// 二进制转化工具function ab2ascii(buffer : ArrayBuffer) {var str = Array.prototype.map.call(new Uint8Array(buffer),function(bit : any) {return String.fromCharCode(bit);})return str.join('');}function asciiToArrayBuffer(str : string) {if (!str) {return new ArrayBuffer(0);}var buffer = new ArrayBuffer(str.length);var bufView = new Uint8Array(buffer);for (var i = 0, strLen = str.length; i < strLen; i++) {bufView[i] = str.charCodeAt(i);}return buffer;}</script> <style scoped lang="scss">.pageBox {height: 100%;width: 100%;position: fixed;}
</style>