uniapp 实现蓝牙打卡功能
·
在 Uniapp 中实现蓝牙打卡功能,主要利用 Uniapp 提供的蓝牙 API 与周边蓝牙设备进行通信,通过识别特定蓝牙设备(如打卡器)完成打卡操作。以下是详细实现步骤:
一、功能流程设计
- 初始化蓝牙适配器
- 检查并开启手机蓝牙
- 搜索附近蓝牙设备(过滤目标打卡设备)
- 连接目标蓝牙设备
- 与设备交互(发送 / 接收打卡指令)
- 完成打卡后断开连接
- 关闭蓝牙适配器
二、实现步骤(代码示例)
1. 页面结构(template)
<template>
<view class="container">
<view class="status">
<text>蓝牙状态:{{ bluetoothState }}</text>
</view>
<button
@click="initBluetooth"
:disabled="isProcessing"
>
初始化蓝牙
</button>
<button
@click="startDiscovery"
:disabled="!isBluetoothReady || isProcessing"
>
搜索设备
</button>
<button
@click="stopDiscovery"
:disabled="!isDiscovering || isProcessing"
>
停止搜索
</button>
<view class="device-list">
<view class="device-item" v-for="device in devices" :key="device.deviceId">
<text>设备名:{{ device.name || '未知设备' }}</text>
<text>设备ID:{{ device.deviceId }}</text>
<button @click="connectDevice(device.deviceId)">连接打卡</button>
</view>
</view>
<view class="result">
<text>{{打卡结果:{{ checkinResult }}}</text>
</view>
</view>
</template>
2. 逻辑实现(script)
export default {
data() {
return {
bluetoothState: '未初始化',
isBluetoothReady: false, // 蓝牙是否初始化成功
isDiscovering: false, // 是否正在搜索设备
isProcessing: false, // 是否正在执行操作
devices: [], // 搜索到的设备列表
connectedDeviceId: '', // 已连接的设备ID
checkinResult: '', // 打卡结果
// 目标打卡设备的特征值(需替换为实际设备的UUID)
targetServiceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
targetCharacteristicId: '0000FFF3-0000-1000-8000-00805F9B34FB'
};
},
onUnload() {
// 页面卸载时关闭蓝牙
this.closeBluetooth();
},
methods: {
// 1. 初始化蓝牙适配器
initBluetooth() {
this.isProcessing = true;
this.bluetoothState = '初始化中...';
uni.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙初始化成功', res);
this.bluetoothState = '已开启';
this.isBluetoothReady = true;
// 监听蓝牙状态变化
uni.onBluetoothAdapterStateChange((state) => {
this.bluetoothState = state.available ? '已开启' : '已关闭';
this.isBluetoothReady = state.available;
});
},
fail: (err) => {
console.error('蓝牙初始化失败', err);
this.bluetoothState = '初始化失败,请开启蓝牙';
// 提示用户开启蓝牙
uni.showModal({
title: '提示',
content: '请开启蓝牙后重试',
showCancel: false
});
},
complete: () => {
this.isProcessing = false;
}
});
},
// 2. 开始搜索设备
startDiscovery() {
this.isProcessing = true;
this.devices = [];
this.isDiscovering = true;
this.bluetoothState = '搜索中...';
// 清空已发现的设备
uni.startBluetoothDevicesDiscovery({
// 过滤目标设备的服务UUID(提高搜索效率)
services: [this.targetServiceId],
allowDuplicatesKey: false, // 不重复上报同一设备
success: (res) => {
console.log('开始搜索设备', res);
// 监听新设备发现
uni.onBluetoothDeviceFound((devices) => {
this.handleFoundDevices(devices.devices);
});
},
fail: (err) => {
console.error('搜索设备失败', err);
this.bluetoothState = '搜索失败';
},
complete: () => {
this.isProcessing = false;
}
});
},
// 处理搜索到的设备
handleFoundDevices(devices) {
devices.forEach(device => {
// 过滤重复设备和无名称设备(可根据实际设备名过滤)
if (device.name && !this.devices.some(d => d.deviceId === device.deviceId)) {
this.devices.push(device);
}
});
},
// 3. 停止搜索设备
stopDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success: () => {
console.log('停止搜索设备');
this.isDiscovering = false;
this.bluetoothState = '已停止搜索';
}
});
},
// 4. 连接目标设备
connectDevice(deviceId) {
if (!deviceId) return;
this.isProcessing = true;
this.checkinResult = '连接中...';
// 连接低功耗蓝牙设备
uni.createBLEConnection({
deviceId,
success: (res) => {
console.log('设备连接成功', res);
this.connectedDeviceId = deviceId;
this.checkinResult = '连接成功,准备打卡...';
// 连接成功后获取服务
this.getBLEServices(deviceId);
},
fail: (err) => {
console.error('设备连接失败', err);
this.checkinResult = '连接失败,请重试';
this.isProcessing = false;
}
});
},
// 获取设备服务
getBLEServices(deviceId) {
uni.getBLEDeviceServices({
deviceId,
success: (res) => {
console.log('设备服务列表', res.services);
// 查找目标服务
const targetService = res.services.find(
service => service.uuid === this.targetServiceId
);
if (targetService) {
this.getBLECharacteristics(deviceId, targetService.uuid);
} else {
this.checkinResult = '未找到目标服务';
this.isProcessing = false;
}
},
fail: (err) => {
console.error('获取服务失败', err);
this.checkinResult = '获取服务失败';
this.isProcessing = false;
}
});
},
// 获取特征值(用于读写数据)
getBLECharacteristics(deviceId, serviceId) {
uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: (res) => {
console.log('特征值列表', res.characteristics);
// 查找目标特征值
const targetChar = res.characteristics.find(
char => char.uuid === this.targetCharacteristicId
);
if (targetChar) {
// 启用通知(监听设备返回的数据)
this.notifyBLECharacteristicValueChange(deviceId, serviceId, targetChar.uuid);
// 发送打卡指令
this.sendCheckinCommand(deviceId, serviceId, targetChar.uuid);
} else {
this.checkinResult = '未找到目标特征值';
this.isProcessing = false;
}
},
fail: (err) => {
console.error('获取特征值失败', err);
this.checkinResult = '获取特征值失败';
this.isProcessing = false;
}
});
},
// 启用特征值变化通知
notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) {
uni.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId,
state: true, // 启用通知
success: () => {
console.log('启用通知成功');
// 监听特征值变化(接收设备返回的数据)
uni.onBLECharacteristicValueChange((res) => {
this.handleCheckinResponse(res.value);
});
}
});
},
// 发送打卡指令(根据设备协议定义指令)
sendCheckinCommand(deviceId, serviceId, characteristicId) {
// 示例:发送"CHECKIN"指令(需转为ArrayBuffer)
const command = 'CHECKIN';
const buffer = new ArrayBuffer(command.length);
const dataView = new DataView(buffer);
for (let i = 0; i < command.length; i++) {
dataView.setUint8(i, command.charCodeAt(i));
}
uni.writeBLECharacteristicValue({
deviceId,
serviceId,
characteristicId,
value: buffer,
success: () => {
console.log('打卡指令发送成功');
this.checkinResult = '打卡指令已发送...';
},
fail: (err) => {
console.error('发送指令失败', err);
this.checkinResult = '打卡失败';
this.isProcessing = false;
}
});
},
// 处理设备返回的打卡结果
handleCheckinResponse(value) {
// 将ArrayBuffer转为字符串
const result = String.fromCharCode.apply(null, new Uint8Array(value));
console.log('打卡结果', result);
if (result.includes('SUCCESS')) {
this.checkinResult = '打卡成功!';
// 这里可以添加上传服务器逻辑
} else {
this.checkinResult = '打卡失败:' + result;
}
// 打卡完成后断开连接
setTimeout(() => {
this.disconnectDevice();
}, 2000);
},
// 断开设备连接
disconnectDevice() {
if (!this.connectedDeviceId) return;
uni.closeBLEConnection({
deviceId: this.connectedDeviceId,
success: () => {
console.log('断开设备连接');
this.connectedDeviceId = '';
this.isProcessing = false;
}
});
},
// 关闭蓝牙适配器
closeBluetooth() {
uni.closeBluetoothAdapter({
success: () => {
console.log('关闭蓝牙适配器');
this.isBluetoothReady = false;
this.bluetoothState = '已关闭';
}
});
}
}
};
3. 样式(style)
.container {
padding: 20rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.status, .result {
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
}
button {
background-color: #007aff;
color: white;
padding: 15rpx 0;
border-radius: 8rpx;
}
.device-list {
margin-top: 20rpx;
display: flex;
flex-direction: column;
gap: 15rpx;
}
.device-item {
padding: 20rpx;
border: 1px solid #eee;
border-radius: 8rpx;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.device-item button {
background-color: #4cd964;
width: 200rpx;
align-self: flex-end;
}
1. 权限配置
需在 manifest.json 中添加蓝牙权限:
- Android(
app-plus -> android -> permissions):
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_ADMIN",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT"
iOS(app-plus -> ios -> plist):
<key>NSBluetoothAlwaysUsageDescription</key>
<string>需要蓝牙权限进行打卡</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>需要蓝牙权限进行打卡</string>
2. 设备 UUID 替换
代码中的 targetServiceId 和 targetCharacteristicId 需替换为实际打卡设备的 UUID,可通过以下方式获取:
- 设备说明书
- 蓝牙调试工具(如 nRF Connect)扫描设备获取服务和特征值 UUID
3. 数据交互协议
- 发送的打卡指令(
sendCheckinCommand中的command)需与设备端协议一致。 - 设备返回的结果解析(
handleCheckinResponse)需根据设备协议调整。
4. 兼容性处理
- 部分安卓机型需要开启定位权限才能搜索蓝牙设备。
- iOS 对蓝牙后台运行有严格限制,建议在前台完成打卡操作。
- 不同设备的连接超时时间可能不同,需合理设置重试机制。
四、功能扩展
- 自动打卡:搜索到目标设备后自动连接并完成打卡。
- 距离判断:通过蓝牙信号强度(
RSSI)判断是否在有效打卡范围内。 - 打卡记录:本地存储打卡记录,同步至服务器。
通过以上步骤,即可在 Uniapp 中实现蓝牙打卡功能,核心是利用蓝牙 API 完成设备搜索、连接和数据交互。
更多推荐
所有评论(0)