import {
ab2hex,
hexCharCodeToStr,
hexToArrayBuffer
} from './utils.js'
const BlModule = (function() {
let events = {}
let BLDeviceList = []
// 设备id
let deviceId = null
// 读取id
let read = {
serviceId: null,
uuid: null
}
// 写入id
let write = {
serviceId: null,
uuid: null
}
function _blModule() {
const _this = this
init()
search()
this.on = function(eventName, listener) {
if (!events[eventName]) {
events[eventName] = [];
}
events[eventName].push(listener);
}
// 连接设备
this.connect = connect
//发送消息
this.sendWrite = sendWrite
this.sendRead = sendRead
this.sendHeart = heart
// 获取 设备id
this.getDeviceId = function() {
return deviceId
}
// 获取所有特征编码
this.getCharacteristicIds = function() {
return {
read: read,
write: write,
notify: notify,
indicate: indicate
}
}
}
/**
* 开启蓝牙
*/
function init() {
uni.openBluetoothAdapter({
success() {
uni.getBluetoothAdapterState({
fail(error) {
uni.showToast({
title: '查看手机蓝牙是否打开',
icon: 'none'
});
}
})
},
fail(err) {
uni.showToast({
title: '查看手机蓝牙是否打开',
icon: 'none'
});
}
})
}
/**
* 搜索设备
*/
function search() {
uni.startBluetoothDevicesDiscovery({
success() {
console.log('开始搜索');
// 开启监听回调
uni.onBluetoothDeviceFound(found)
}
})
}
/**
* 发现新设备添加至DlDeviceList中
* @param {Object} res
*/
function found(res) {
const oldArr = JSON.parse(JSON.stringify(BLDeviceList))
BLDeviceList = oldArr.concat(res.devices).filter(item => item.name != "")
// 对搜索到的蓝牙设备去重
for (let i = 0; i < BLDeviceList.length; i++) {
for (let j = i + 1; j < BLDeviceList.length; j++) {
if (BLDeviceList[i].name == BLDeviceList[j].name) {
BLDeviceList.splice(j, 1)
}
}
}
emit('updateBlDevice', BLDeviceList)
}
/**
* 连接设备
* @param {object} data 设备信息
* @param {string} data.deviceId 设备信息
*/
function connect(data) {
deviceId = data.deviceId // 将获取到的设备ID存起来
uni.createBLEConnection({
deviceId: deviceId,
success(res) {
// 停止搜索
stopDiscovery()
setMaxMTU()
uni.showLoading({
title: '连接中...'
})
setTimeout(async () => {
await getServices()
uni.hideLoading()
emit('ConnectSuccess')
}, 1000)
},
fail(err) {
console.error(err)
uni.showToast({
title: '连接失败,请稍后重试',
icon: 'error'
})
}
})
}
/**
* 设置最大值
*/
function setMaxMTU() {
uni.setBLEMTU({
deviceId: deviceId,
mtu: 512,
success(res) {
console.log("设置最大值成功")
}
})
}
/**
* 关闭搜索
*/
function stopDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success(res) {
console.log('停止成功')
},
fail(err) {
console.log('停止失败', err)
}
})
}
/**
* 获取服务
*/
function getServices() {
return new Promise((resolve, reject) => {
let characteristics = []
uni.getBLEDeviceServices({
deviceId: deviceId,
success: async (res) => {
const asyncRes = await Promise.all(res.services.map(async (item) => {
const c = await getCharacteristics(item.uuid)
return c
}));
asyncRes.map(item => {
characteristics = characteristics.concat(item)
})
characteristics.map((c, j) => {
if (c.properties.read && !c.properties.write) {
read = {
serviceId: c.serviceId,
uuid: c.uuid
}
}
if (c.properties.write && !c.properties.read) {
write = {
serviceId: c.serviceId,
uuid: c.uuid
}
}
if (c.properties.notify) {
const st = setTimeout(()=>{
notifyEvn({
serviceId: c.serviceId,
uuid: c.uuid
})
clearTimeout(st)
}, 1000,)
resolve()
}
})
},
fail(err) {
console.log('连接失败');
reject(err)
}
})
})
// 如果是自动链接的话,uni.getBLEDeviceServices方法建议使用setTimeout延迟1秒后再执行
}
/**
* 获取特征值
*/
function getCharacteristics(uuid) {
// 如果是自动链接的话,uni.getBLEDeviceCharacteristics方法建议使用setTimeout延迟1秒后再执行
return new Promise((resolve, reject) => {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: uuid,
success(res) {
res.characteristics.map(item => {
item.serviceId = uuid
})
const properties = res.characteristics
resolve(properties) // 可以在此判断特征值是否支持读写等操作,特征值其实也需要提前向硬件佬索取的
},
fail(err) {
reject(err)
}
})
})
}
/**
* 开启消息监听
*/
function notifyEvn(notify) {
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
deviceId: deviceId, // 设备id
serviceId: notify.serviceId, // 监听指定的服务
characteristicId: notify.uuid, // 监听对应的特征值
success(res) {
console.log('开启设备监听');
listenValueChange()
},
fail(err) {
console.error('消息监听失败', err)
}
})
}
/**
* 监听消息变化
*/
function listenValueChange() {
uni.onBLECharacteristicValueChange(res => {
let resHex = ab2hex(res.value)
// emit('newMsg', resHex)
})
}
/**
* 发送写入命令
* 接受一个16进制字符串
*/
function sendWrite(val) {
// 向蓝牙设备发送一个0x00的16进制数据
const buffer = hexToArrayBuffer(val);
uni.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: write.serviceId,
characteristicId: write.uuid,
value: buffer,
writeType: 'write',
success(res) {
emit('SendSuccess')
},
fail(err) {
console.error(err)
uni.showToast({
title: '指令发送失败',
icon: 'none'
})
}
})
}
/**
* 发送读取指令
* @param {string} val
*/
function sendRead(val) {
// 向蓝牙设备发送一个0x00的16进制数据
const buffer = hexToArrayBuffer(val);
uni.readBLECharacteristicValue({
deviceId: deviceId,
serviceId: read.serviceId,
characteristicId: read.uuid,
success(res) {
emit('SendSuccess')
},
fail(err) {
console.error(err)
uni.showToast({
title: '指令发送失败',
icon: 'none'
})
}
})
}
/**
* 发送心跳数据
* 接受一个16进制字符串
*/
function heart(val) {
// 向蓝牙设备发送一个0x00的16进制数据
const buffer = hexToArrayBuffer(val);
uni.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: write.serviceId,
characteristicId: write.uuid,
value: buffer,
writeType: 'write',
success(res) {},
fail(err) {
console.error(err)
uni.showToast({
title: '指令发送失败',
icon: 'none'
})
}
})
}
/**
* 事件监听器
*/
function emit(eventName, data) {
if (!events[eventName]) {
return;
}
events[eventName].forEach(listener => listener(data));
}
return _blModule
})()
export default BlModule
<template>
<scroll-view scroll-y class="box">
<view class="item" v-for="(item, index) in BLDeviceList" :key="index" @click="connectBlue(item)">
<view>
<text>id: {{ item.deviceId }}</text>
</view>
<view>
<text>name: {{ item.name }}</text>
</view>
</view>
</scroll-view>
<button @click="SearchBT">搜索蓝牙设备</button>
<input placeholder="输入发送指令" v-model="order"/>
<button @click="sendWriteOrder">发 送</button>
<input placeholder="输入读取指令" v-model="read"/>
<button @click="sendReadOrder">发 送</button>
</template>
<script setup>
import BlModule from '@/components/BlModule/index.js'
import { ref } from 'vue';
let order = ref('CD3613051500015AB76ECD3E')
let read = ref('CD3613051500015AC1CD3E')
let BLDeviceList = ref([])
let blTools = null
// 搜索蓝牙设备
function SearchBT() {
blTools = new BlModule()
// 监听获取设备列表
blTools.on('updateBlDevice',(e) => {
BLDeviceList.value = e
})
}
// 连接蓝牙设备
function connectBlue(data) {
blTools.connect(data)
// 监听连接成功状态
blTools.on('ConnectSuccess', () => {
uni.showToast({
title: '连接成功',
icon: 'none'
});
})
}
// 发送写入指令
function sendWriteOrder() {
blTools.sendWrite('0X'+order.value)
// 监听发送成功状态
blTools.on('SendSuccess', () => {
uni.showToast({
title: '发送成功',
icon: 'none'
});
})
}
// 发送读取命令
function sendReadOrder() {
blTools.sendWrite('0X'+read.value)
// 监听发送成功状态
blTools.on('SendSuccess', () => {
uni.showToast({
title: '发送成功',
icon: 'none'
});
})
}
</script>
<style>
.box {
width: 98%;
height: 400rpx;
box-sizing: border-box;
margin: 0 auto 20rpx;
border: 2px solid dodgerblue;
}
.item {
box-sizing: border-box;
padding: 10rpx;
border-bottom: 1px solid #ccc;
}
button {
margin-bottom: 20rpx;
}
.msg_x {
border: 2px solid seagreen;
width: 98%;
margin: 10rpx auto;
box-sizing: border-box;
padding: 20rpx;
}
.msg_x .msg_txt {
margin-bottom: 20rpx;
}
</style>