1296 lines
31 KiB
JavaScript
1296 lines
31 KiB
JavaScript
// 蓝牙设备结果集
|
||
let devices = []
|
||
|
||
// 当前连接的蓝牙设备
|
||
let device = ''
|
||
|
||
// 蓝牙设备id
|
||
let deviceId = ''
|
||
|
||
|
||
// 写了数据等待通知结果返回
|
||
let waitingForResult = false
|
||
|
||
// 连接状态
|
||
let isConnecting = false
|
||
let connected = false
|
||
|
||
// 服务 uuid 和 特征值 uuid
|
||
//正式
|
||
//测试
|
||
|
||
let service_uuid = "0000AE30-0000-1000-8000-00805F9B34FB"
|
||
let write_characteristic = "0000AE03-0000-1000-8000-00805F9B34FB"
|
||
let notify_characteristic = "0000AE03-0000-1000-8000-00805F9B34FB"
|
||
let notify_characteristic_2 = "0000AE02-0000-1000-8000-00805F9B34FB"//获取设备电量和设备mac地址
|
||
|
||
// 命名规则
|
||
let rule1 = "" // Android
|
||
let rule2 = "" // ios
|
||
|
||
// 手动断开
|
||
var manualDisconnect = true
|
||
|
||
// #################################################
|
||
// #
|
||
// # Callback Definitions
|
||
// #
|
||
// #################################################
|
||
|
||
// 回调事件
|
||
let connectCallback = '' // 连接成功回调
|
||
let scanCallback = '' // 搜索到蓝牙设备回调
|
||
let disconnectCallback = '' // 设备断开连接回调
|
||
let writeCallback = '' // 写成功回调
|
||
// 自定义 业务相关 回调事件
|
||
let realtimeCallback = '' // 实时发送指令的回调
|
||
let batteryCallback = '' // 电量回调
|
||
let macCallback = '' // MAC地址回调
|
||
let lastBatteryLevel = 'N/A';
|
||
|
||
// 自定义 业务相关 标识
|
||
let realtimeType = '' // 实时发送指令的标识
|
||
|
||
|
||
// 重连相关
|
||
let reconnectTimes = 0 // 重连次数
|
||
let reconnectTimer // 重连计时器
|
||
|
||
// 因为有心跳自动计时器发送,不是全是用户手动的操作,用户的手动操作设定为一个操作后才能操作下一个,但有自动心跳发送的话,可能会出现发送心跳时,
|
||
// 用户也正好操作了,那这时可能会出现,同时写两条指令情况,即没收到上一条的回复就发送了下一条指令,现用数组暂存
|
||
let cmdList = [] // 看这里!!! 现在没有心跳,只有用户的操作,确保用户的操作都是有回复的,并且在队列内,等待前一条执行完才执行下一条
|
||
|
||
// 当前蓝牙适配器是否打开的状态
|
||
let isOpen = false
|
||
|
||
// 自动连接
|
||
let auto_connect = false
|
||
|
||
// 准备去自动连接的 deviceId
|
||
let auto_mac = []
|
||
let auto_mac_backup = [] // 备份
|
||
|
||
// 扫描到设备后,再等待个几秒计时器
|
||
let after_timer = ''
|
||
|
||
// 自动连接回调
|
||
let autoConnectCallback = ''
|
||
|
||
// 自动连接检测计时器
|
||
let autoConnectTimer = ''
|
||
|
||
|
||
/**
|
||
* 手机蓝牙是否开启了
|
||
*/
|
||
let isBleOn = false
|
||
|
||
|
||
|
||
let listener = function(res) {
|
||
console.log(res);
|
||
// 该方法回调中可以用于处理连接意外断开等异常情况
|
||
if (!res.connected) {
|
||
device = ''
|
||
cmdList = [] // 这里清空指令
|
||
isConnecting = false
|
||
connected = false
|
||
lastBatteryLevel = 'N/A';
|
||
if (disconnectCallback && typeof disconnectCallback == 'function') {
|
||
disconnectCallback()
|
||
}
|
||
uni.getBluetoothAdapterState({
|
||
success: (res) => {
|
||
if (!res.available) {
|
||
isOpen = false
|
||
auto_connect = true
|
||
}
|
||
reconnectDevice()
|
||
},
|
||
fail: (err) => {}
|
||
})
|
||
}
|
||
}
|
||
|
||
|
||
// 初始化拓展函数
|
||
String.prototype.padLeft = function(len, char) {
|
||
let str = this;
|
||
return new Array(len - str.length + 1).join(char || '') + str
|
||
}
|
||
String.prototype.transFloat = function() {
|
||
let str = this;
|
||
return str.indexOf('.') != -1 ? parseFloat(str).toFixed(1) : str
|
||
}
|
||
|
||
// //uni
|
||
uni.onBluetoothAdapterStateChange(function(res) {
|
||
if (!res.available) {
|
||
isOpen = false
|
||
isBleOn = false
|
||
} else {
|
||
isBleOn = true
|
||
}
|
||
})
|
||
|
||
|
||
|
||
|
||
// 获取 校验
|
||
function getCheckSum(cmd) {
|
||
// console.log(cmd, '我是cmd');
|
||
let checkSum = getCrc16(cmd);
|
||
// console.log(checkSum, '我是checkSum');
|
||
// console.log(splitByLen(checkSum, 2).reverse().map(e => parseInt(e, 16)), 'woshi ');
|
||
return splitByLen(checkSum, 2).reverse().map(e => parseInt(e, 16))
|
||
}
|
||
|
||
function getCrc16(cmd) {
|
||
let crc = 0xffff;
|
||
let len = cmd.length;
|
||
for (let i = 0; i < len; i++) {
|
||
crc = crc ^ (cmd[i] & 0xff)
|
||
for (let j = 0; j < 8; j++) {
|
||
if ((crc & 0x0001) == 1) {
|
||
crc >>= 1
|
||
crc ^= 0xA001
|
||
} else {
|
||
crc >>= 1
|
||
}
|
||
}
|
||
}
|
||
// console.log(crc.toString(16).padLeft(4, '0'), '0121454878');
|
||
return crc.toString(16).padLeft(4, '0')
|
||
}
|
||
|
||
|
||
// #################################################
|
||
// #
|
||
// # Module Setup & Configuration
|
||
// #
|
||
// #################################################
|
||
|
||
// 初始化蓝牙模块
|
||
export function initBlue() {
|
||
uni.onBluetoothDeviceFound((res) => {
|
||
res.devices.forEach(device => {
|
||
// 过滤掉没有名字的设备
|
||
if (!device.name) {
|
||
return;
|
||
}
|
||
|
||
const foundDevices = devices
|
||
const idx = inArray(foundDevices, 'deviceId', device.deviceId)
|
||
if (idx === -1) {
|
||
devices.push(device)
|
||
} else {
|
||
devices[idx] = device
|
||
}
|
||
|
||
// Always notify the UI of any found device
|
||
if (scanCallback && typeof scanCallback == "function") {
|
||
scanCallback() // 查找结果集回调
|
||
}
|
||
|
||
// console.log('auto_connect:', auto_connect);
|
||
// console.log('isConnecting:', isConnecting);
|
||
// console.log('deviceId:', deviceId);
|
||
if (auto_connect && !isConnecting && device.deviceId == deviceId) {
|
||
isConnecting = true
|
||
createBLEConnection(deviceId, true);
|
||
|
||
} else if (auto_connect) {
|
||
// console.log('111111100000000000000000//////////////');
|
||
if (!after_timer) {
|
||
after_timer = setTimeout(() => {
|
||
let valid = []
|
||
auto_mac = JSON.parse(JSON.stringify(auto_mac_backup))
|
||
auto_mac.forEach(item => {
|
||
valid.push(devices.some(e => e.deviceId == item))
|
||
})
|
||
valid.forEach((item, index) => {
|
||
if (!item) {
|
||
auto_mac.splice(index, 1)
|
||
}
|
||
})
|
||
console.log('valid mac ', auto_mac)
|
||
if (auto_mac.length > 0) {
|
||
uni.showLoading({
|
||
title: '自动连接中...',
|
||
mask: true
|
||
})
|
||
let mac = auto_mac[0]
|
||
createBLEConnection(mac)
|
||
auto_mac.splice(0, 1)
|
||
} else {}
|
||
clearTimeout(after_timer)
|
||
after_timer = ''
|
||
}, 1000 * 6)
|
||
}
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
// 设置连接成功回调
|
||
export function setCallback(e) {
|
||
connectCallback = e
|
||
}
|
||
|
||
// 设置搜索到蓝牙设备回调
|
||
export function setScanCallback(e) {
|
||
scanCallback = e
|
||
}
|
||
|
||
// 设置设备断开连接回调
|
||
export function setDisconnectCallback(e) {
|
||
disconnectCallback = e
|
||
}
|
||
|
||
// 设置写成功回调
|
||
export function setWriteCallback(e) {
|
||
writeCallback = e;
|
||
}
|
||
|
||
export function setBatteryCallback(e) {
|
||
batteryCallback = e;
|
||
if (lastBatteryLevel !== 'N/A' && typeof batteryCallback === 'function') {
|
||
batteryCallback(lastBatteryLevel);
|
||
}
|
||
}
|
||
|
||
export function setMacCallback(e) {
|
||
macCallback = e;
|
||
}
|
||
|
||
export function getDeviceId(){
|
||
return deviceId
|
||
}
|
||
|
||
export function getConnectedDevice() {
|
||
return device;
|
||
}
|
||
|
||
// 业务相关回调
|
||
function setRealtimeCallback(type, e) {
|
||
realtimeType = type
|
||
// console.log('业务相关回调', e);
|
||
realtimeCallback = e
|
||
// console.log('set realtimeType ', realtimeType)
|
||
}
|
||
|
||
|
||
// #################################################
|
||
// #
|
||
// # Core BLE Command Functions
|
||
// #
|
||
// #################################################
|
||
|
||
// 转换16进制
|
||
/**
|
||
* @param {number} num 几位
|
||
*/
|
||
function conversion(str, num) {
|
||
const result = [];
|
||
for (let i = 0; i < str.length; i += num) {
|
||
let temp = str.slice(i, i + num);
|
||
// temp.toString(16);
|
||
result.push(parseInt(temp, 16));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// 切换灯的模式
|
||
export function sendLightType(type, e){
|
||
setRealtimeCallback(type, e)
|
||
let str1 = `FA${e}000100FF`;
|
||
let arr = [];
|
||
arr = conversion(str1, 2);
|
||
executeCmd(arr);
|
||
}
|
||
|
||
// 调节灯亮度
|
||
export function setLight(type, e, num) {
|
||
setRealtimeCallback(type, e)
|
||
// let str = `FA05${parseInt(num, 16)}00FF`;
|
||
let str = `FA050001${num.toString(16).padLeft(2,'0')}FF`;
|
||
let arr = conversion(str, 2);
|
||
console.log('light数组', arr);
|
||
executeCmd(arr);
|
||
}
|
||
|
||
/**
|
||
* @description 设置单位、部门、名字...
|
||
* @param { Array } arr
|
||
*/
|
||
export function sendImg(arr,e) {
|
||
let dataArr = conversion(arr, 2);
|
||
console.log(dataArr);
|
||
executeCmd(dataArr);
|
||
}
|
||
|
||
|
||
// 如未空(未收到回复,收到回复会清掉第一个指令)进队列, 空直接执行
|
||
function executeCmd(cmd) {
|
||
// if (cmdList.length == 0) {
|
||
// writeBLECharacteristicValue(write_characteristic, cmd)
|
||
|
||
// }
|
||
// cmdList.push(cmd)
|
||
writeBLECharacteristicValue(write_characteristic, cmd)
|
||
}
|
||
|
||
// 清掉第一个指令,然后检查队列是否为空,不为空继续发送
|
||
function continueExecute() {
|
||
cmdList.splice(0, 1)
|
||
if (cmdList.length != 0) {
|
||
writeBLECharacteristicValue(write_characteristic, cmdList[0])
|
||
}
|
||
}
|
||
|
||
|
||
// #################################################
|
||
// #
|
||
// # Core BLE Connection Logic
|
||
// #
|
||
// #################################################
|
||
|
||
|
||
// step 1
|
||
// 打开蓝牙功能
|
||
export function openBluetoothAdapter(search, callback) {
|
||
const operation = () => {
|
||
openBleAdapter((err) => {
|
||
if (!err) {
|
||
// On success (no error), start scanning for devices.
|
||
startBluetoothDevicesDiscovery();
|
||
}
|
||
// Always call the page's callback to notify it of the result.
|
||
if (callback) {
|
||
callback(err);
|
||
}
|
||
});
|
||
};
|
||
|
||
if (search) {
|
||
// First close any existing adapter session, then perform the operation.
|
||
uni.closeBluetoothAdapter({
|
||
complete: operation,
|
||
});
|
||
} else {
|
||
operation();
|
||
}
|
||
}
|
||
|
||
function openBleAdapter(callback) {
|
||
uni.openBluetoothAdapter({
|
||
success: (res) => {
|
||
isOpen = true;
|
||
isBleOn = true;
|
||
if (callback) callback(); // Success, no error object.
|
||
},
|
||
fail: (err) => {
|
||
// Log the full error to help diagnose release version issues
|
||
console.error('openBluetoothAdapter failed with error:', JSON.stringify(err));
|
||
|
||
if (err.errCode === 10001) {
|
||
uni.showModal({
|
||
content: '请打开手机蓝牙',
|
||
showCancel: false,
|
||
});
|
||
} else {
|
||
// For other errors, show a detailed modal for debugging in the release version.
|
||
uni.showModal({
|
||
title: '蓝牙启动失败',
|
||
content: `错误: ${JSON.stringify(err)}`,
|
||
showCancel: false
|
||
});
|
||
}
|
||
isOpen = false;
|
||
isBleOn = false;
|
||
// Propagate the error to the calling page
|
||
if (callback) callback(err);
|
||
},
|
||
});
|
||
}
|
||
|
||
// step 2
|
||
//开始搜索蓝牙设备
|
||
function startBluetoothDevicesDiscovery() {
|
||
console.log('开始搜索');
|
||
let option = {
|
||
allowDuplicatesKey: false,
|
||
success: (res) => {
|
||
console.log('搜索成功,有设备', res);
|
||
onBluetoothDeviceFound();
|
||
},
|
||
fail: (err) => {
|
||
console.log('当前搜索蓝牙设备失败', err);
|
||
if(err.errCode == 10008) {
|
||
uni.showToast({
|
||
title: '扫描太过频繁,请稍后再试',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
// resetDevices();
|
||
// devices = []
|
||
// openBluetoothAdapter(true);
|
||
}
|
||
}
|
||
if (auto_connect) {
|
||
option.services = [
|
||
service_uuid
|
||
]
|
||
}
|
||
uni.startBluetoothDevicesDiscovery(option)
|
||
}
|
||
|
||
|
||
|
||
let hexString = ''
|
||
// step 3
|
||
// 设备查找结果处理
|
||
function onBluetoothDeviceFound() {
|
||
uni.onBluetoothDeviceFound((res) => {
|
||
res.devices.forEach(device => {
|
||
// 过滤掉没有名字的设备
|
||
if (!device.name) {
|
||
return;
|
||
}
|
||
|
||
const foundDevices = devices
|
||
const idx = inArray(foundDevices, 'deviceId', device.deviceId)
|
||
if (idx === -1) {
|
||
devices.push(device)
|
||
} else {
|
||
devices[idx] = device
|
||
}
|
||
|
||
// Always notify the UI of any found device
|
||
if (scanCallback && typeof scanCallback == "function") {
|
||
scanCallback() // 查找结果集回调
|
||
}
|
||
|
||
// console.log('auto_connect:', auto_connect);
|
||
// console.log('isConnecting:', isConnecting);
|
||
// console.log('deviceId:', deviceId);
|
||
if (auto_connect && !isConnecting && device.deviceId == deviceId) {
|
||
isConnecting = true
|
||
createBLEConnection(deviceId, true);
|
||
|
||
} else if (auto_connect) {
|
||
// console.log('111111100000000000000000//////////////');
|
||
if (!after_timer) {
|
||
after_timer = setTimeout(() => {
|
||
let valid = []
|
||
auto_mac = JSON.parse(JSON.stringify(auto_mac_backup))
|
||
auto_mac.forEach(item => {
|
||
valid.push(devices.some(e => e.deviceId == item))
|
||
})
|
||
valid.forEach((item, index) => {
|
||
if (!item) {
|
||
auto_mac.splice(index, 1)
|
||
}
|
||
})
|
||
console.log('valid mac ', auto_mac)
|
||
if (auto_mac.length > 0) {
|
||
uni.showLoading({
|
||
title: '自动连接中...',
|
||
mask: true
|
||
})
|
||
let mac = auto_mac[0]
|
||
createBLEConnection(mac)
|
||
auto_mac.splice(0, 1)
|
||
} else {}
|
||
clearTimeout(after_timer)
|
||
after_timer = ''
|
||
}, 1000 * 6)
|
||
}
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
|
||
// connect step 1
|
||
// 连接蓝牙设备
|
||
export function createBLEConnection(id, advertisData, sameDevice, closeConnectLoading) { //直接连接设备
|
||
console.log(id, advertisData, sameDevice, '连接蓝牙设备');
|
||
closeBLEConnection(false, () => {
|
||
|
||
// #ifdef MP-WEIXIN
|
||
uni.offBLEConnectionStateChange(listener) // 先移除
|
||
// #endif
|
||
|
||
//监听蓝牙连接状态
|
||
uni.onBLEConnectionStateChange(listener)
|
||
uni.createBLEConnection({
|
||
deviceId: id,
|
||
timeout: 5000,
|
||
success: (res) => {
|
||
console.log(res, '蓝牙连接状态');
|
||
auto_connect = false // 重置
|
||
auto_mac = [] // 重置
|
||
closeAutoConnect() // 关闭全局自动连接
|
||
if (autoConnectCallback && typeof autoConnectCallback == 'function') {
|
||
autoConnectCallback()
|
||
}
|
||
stopBluetoothDevicesDiscovery() // 停止扫描蓝牙设备
|
||
device = devices.find(item => item.deviceId == id)
|
||
isConnecting = false
|
||
connected = true
|
||
manualDisconnect = false
|
||
deviceId = id
|
||
console.log('连接的设备ID', deviceId);
|
||
getBLEDeviceServices(deviceId) // 获取服务,初始化特征值
|
||
uni.setStorageSync('deviceId', deviceId);
|
||
// uni.setStorageSync('deviceName', name);
|
||
},
|
||
fail: (err) => {
|
||
console.log('设备连接失败', err);
|
||
isConnecting = false;
|
||
if (auto_connect) {
|
||
if (auto_mac.length > 0) {
|
||
let mac = auto_mac[0]
|
||
createBLEConnection(mac)
|
||
auto_mac.splice(0, 1)
|
||
} else {
|
||
if (autoConnectCallback && typeof autoConnectCallback == 'function') {
|
||
autoConnectCallback()
|
||
}
|
||
}
|
||
} else {
|
||
console.log('设备连接失败');
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '设备连接失败!' + err.errMsg,
|
||
icon: 'none',
|
||
duration: 2000,
|
||
})
|
||
}
|
||
if (typeof closeConnectLoading === 'function') {
|
||
closeConnectLoading();
|
||
}
|
||
},
|
||
complete: () => {
|
||
uni.hideLoading();
|
||
}
|
||
})
|
||
}, sameDevice) // 关闭当前蓝牙连接
|
||
console.log('结束连接方法');
|
||
}
|
||
|
||
// connect step 2
|
||
// 获取服务
|
||
function getBLEDeviceServices(deviceId) {
|
||
console.log('我获取服务成功接收到的Id是', deviceId);
|
||
//#ifdef APP-PLUS
|
||
setTimeout(() => {
|
||
uni.getBLEDeviceServices({
|
||
deviceId,
|
||
success: (res) => {
|
||
// console.log(res, '获取蓝牙设备所有服务2');
|
||
// console.log('测试1',res.services[i].uuid, service_uuid.toUpperCase())
|
||
for (let i = 0; i < res.services.length; i++) {
|
||
// console.log('测试2',res.services[i].uuid, service_uuid.toUpperCase())
|
||
// 消息服务
|
||
if (res.services[i].uuid == service_uuid ||
|
||
res.services[i].uuid == service_uuid.toUpperCase()) {
|
||
getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
|
||
}
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log(err, '获取服务失败')
|
||
}
|
||
})
|
||
}, 6000)
|
||
//#endif
|
||
|
||
|
||
//#ifdef MP-WEIXIN
|
||
uni.getBLEDeviceServices({
|
||
deviceId,
|
||
success: (res) => {
|
||
console.log(res, '获取蓝牙设备所有服务');
|
||
for (let i = 0; i < res.services.length; i++) {
|
||
console.log(res.services[i].uuid, service_uuid)
|
||
// 消息服务
|
||
if (res.services[i].uuid == service_uuid ||
|
||
res.services[i].uuid == service_uuid.toUpperCase()) {
|
||
getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
|
||
}
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log(err, '获取服务失败')
|
||
}
|
||
})
|
||
//#endif
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
// connect step 3
|
||
// 初始化特征值
|
||
function getBLEDeviceCharacteristics(deviceId, serviceId) {
|
||
uni.getBLEDeviceCharacteristics({
|
||
deviceId,
|
||
serviceId,
|
||
success: (res) => {
|
||
console.log(res,'通知特征值')
|
||
for (let i = 0; i < res.characteristics.length; i++) {
|
||
let item = res.characteristics[i]
|
||
|
||
const itemUUID = item.uuid.toUpperCase();
|
||
const primaryNotifyUUID = notify_characteristic.toUpperCase();
|
||
const secondaryNotifyUUID = notify_characteristic_2.toUpperCase();
|
||
|
||
// Check if the characteristic is one of the notification sources
|
||
if (itemUUID === primaryNotifyUUID || itemUUID === secondaryNotifyUUID) {
|
||
if (item.properties.notify || item.properties.indicate) {
|
||
uni.notifyBLECharacteristicValueChange({
|
||
deviceId,
|
||
serviceId,
|
||
characteristicId: item.uuid,
|
||
state: true,
|
||
success: () => {
|
||
console.log(`已成功监听特征值: ${item.uuid}`);
|
||
},
|
||
fail: (err) => {
|
||
console.error(`监听特征值 ${item.uuid} 失败:`, err);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
if (itemUUID === write_characteristic.toUpperCase()) {
|
||
if (item.properties.write) {}
|
||
}
|
||
}
|
||
},
|
||
fail: (res) => {
|
||
console.error('getBLEDeviceCharacteristics', res)
|
||
}
|
||
})
|
||
// notify的回调函数,操作之前先监听,保证第一时间获取数据
|
||
// 针对不同特征值的回调处理
|
||
uni.onBLECharacteristicValueChange((res) => {
|
||
let result = ab2hex(res.value)
|
||
|
||
// 打印来自 notify_characteristic_2 的数据
|
||
if (res.characteristicId.toUpperCase() === notify_characteristic_2.toUpperCase()) {
|
||
console.log(`接收到来自 [notify_characteristic_2] 的原始Hex数据: ${result}`);
|
||
|
||
if (result.startsWith('fc') && result.endsWith('ff')) {
|
||
const macHex = result.substring(2, result.length - 2);
|
||
const macAddress = macHex.match(/.{1,2}/g).reverse().join(':').toUpperCase();
|
||
console.log(`[notify_characteristic_2] 解析后的MAC地址: ${macAddress}`);
|
||
if (macCallback && typeof macCallback === 'function') {
|
||
macCallback(macAddress);
|
||
}
|
||
return;
|
||
} else if (result.startsWith('fb') && result.endsWith('ff')) {
|
||
const dataHex = result.substring(2, result.length - 2);
|
||
const batteryHex = dataHex.substring(0, 2);
|
||
const batteryLevel = parseInt(batteryHex, 16);
|
||
lastBatteryLevel = batteryLevel;
|
||
console.log(`[notify_characteristic_2] 解析后的电量: ${batteryLevel}%`);
|
||
if (batteryCallback && typeof batteryCallback === 'function') {
|
||
batteryCallback(batteryLevel);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
// This is the original logic for the primary notification characteristic.
|
||
// It is now generalized to handle responses from either characteristic if not handled above.
|
||
console.log('通知特征值',result)
|
||
|
||
let length = result.length
|
||
let len = parseInt(splitByLen(result.substr(2, 4), 2).reverse().join(''), 16)
|
||
// console.log(len)
|
||
let checkArrHex = splitByLen(result.substr(0, length - 4), 2)
|
||
// console.log(checkArrHex)
|
||
let checkArr = checkArrHex.map(e => parseInt(e, 16))
|
||
// console.log(checkArr)
|
||
let sendCheckSum = result.substr(-4)
|
||
let checkSum = getCheckSum(checkArr).map(e => e.toString(16).padLeft(2, '0')).join('')
|
||
// console.log(checkSum)
|
||
// console.log(sendCheckSum)
|
||
// console.log(sendCheckSum === checkSum)
|
||
if (sendCheckSum === checkSum) {
|
||
let type = parseInt(result.substr(8, 2), 16)
|
||
// console.log(type)
|
||
// console.log(realtimeType)
|
||
if (realtimeType == type && realtimeCallback && typeof realtimeCallback == 'function') {
|
||
realtimeType = undefined
|
||
let tempCallback = realtimeCallback
|
||
realtimeCallback = undefined
|
||
tempCallback(result)
|
||
// console.log('realtimeType ', realtimeType)
|
||
}
|
||
continueExecute()
|
||
} else {
|
||
uni.hideLoading() // 校验值不对的情况
|
||
continueExecute()
|
||
}
|
||
})
|
||
connectCallback(deviceId)
|
||
}
|
||
|
||
|
||
// 获取当前是否连接上
|
||
export function getConnected() {
|
||
return connected
|
||
}
|
||
|
||
// 设置自动连接的 mac 地址
|
||
function setAutoMac(arr) {
|
||
auto_mac_backup = arr
|
||
auto_connect = true
|
||
openBluetoothAdapter()
|
||
startAutoConnect()
|
||
}
|
||
|
||
/**
|
||
* 开启全局自动连接
|
||
*/
|
||
function startAutoConnect() {
|
||
auto_connect = true
|
||
autoConnectTimer = setInterval(() => {
|
||
if (auto_connect) {
|
||
if (!connected) {
|
||
console.log('自动连接扫描 ', isOpen)
|
||
if (isOpen) {
|
||
|
||
} else {
|
||
if (isBleOn) {
|
||
openBluetoothAdapter()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}, 1000 * 3)
|
||
}
|
||
|
||
/**
|
||
* 关闭全局自动连接
|
||
*/
|
||
function closeAutoConnect() {
|
||
auto_connect = false
|
||
clearInterval(autoConnectTimer)
|
||
}
|
||
|
||
/**
|
||
* 取消全局自动连接
|
||
*/
|
||
function cancelAutoConnect() {
|
||
auto_connect = false
|
||
auto_mac = []
|
||
autoConnectCallback = ''
|
||
clearInterval(autoConnectTimer)
|
||
closeBluetoothAdapter()
|
||
}
|
||
|
||
|
||
// 获取暂存的扫描到的设备数组
|
||
function getDevices() {
|
||
if (device) {
|
||
devices.push(device)
|
||
}
|
||
return devices
|
||
}
|
||
|
||
// 重置扫描到的设备数组
|
||
export function resetDevices() {
|
||
devices = [];
|
||
}
|
||
|
||
// write msg 发送指令
|
||
// msg 为 hexstring 形式
|
||
function writeBLECharacteristicValue(characteristicId, msg) {
|
||
if (connected == false) {
|
||
reconnectDevice();
|
||
return;
|
||
}
|
||
let buffer = hexArr2ab(msg);
|
||
waitingForResult = true; //等待通知返回结果。
|
||
let len = buffer.byteLength
|
||
let arr = []
|
||
if (len > 20) {
|
||
for (let i = 0, j = 0; i < len; i += 20) {
|
||
let start = j * 20;
|
||
let end = start + 20 > len ? len - start : 20
|
||
let data = Uint8Array.from(new Uint8Array(buffer, start, end))
|
||
// console.log(data)
|
||
// let tempBuffer = data.buffer.slice(start, end)
|
||
// console.log(tempBuffer)
|
||
arr.push(data.buffer)
|
||
j++
|
||
}
|
||
stopFlag = false
|
||
writeData(characteristicId, arr)
|
||
} else {
|
||
stopFlag = false
|
||
writeData(characteristicId, [buffer])
|
||
}
|
||
|
||
}
|
||
|
||
let stopFlag = false
|
||
let timeoutTimer = ''
|
||
let timeoutCallback = ''
|
||
|
||
function writeData(characteristicId, buffer) {
|
||
console.log('准备发送');
|
||
clearTimeout(timeoutTimer)
|
||
// console.log(buffer)
|
||
if (stopFlag) {
|
||
return
|
||
}
|
||
uni.writeBLECharacteristicValue({
|
||
deviceId,
|
||
serviceId: service_uuid,
|
||
characteristicId,
|
||
value: buffer[0],
|
||
success: (res) => {
|
||
if (realtimeCallback) {
|
||
let str = `写入 ${ab2hex(buffer[0])}`
|
||
console.log(str)
|
||
}
|
||
// 写入一次进度条就前进
|
||
if (writeCallback && typeof writeCallback == 'function') {
|
||
let str = `写入 ${ab2hex(buffer[0])}`
|
||
console.log('分包发送了一次');
|
||
writeCallback(str);
|
||
}
|
||
|
||
if (!stopFlag && buffer.length > 1) {
|
||
writeData(characteristicId, buffer.slice(1))
|
||
} else {
|
||
let time = new Date().getTime()
|
||
// console.log('time ', time)
|
||
timeoutTimer = setTimeout(() => {
|
||
if (timeoutCallback && typeof timeoutCallback == 'function') {
|
||
timeoutCallback()
|
||
}
|
||
}, 2000)
|
||
}
|
||
},
|
||
fail: (res) => {
|
||
cmdList = [] // 这里清空指令, 放弃队列中所有指令,为后续新指令的到来做准备
|
||
waitingForResult = false;
|
||
console.error('write failed ', res);
|
||
uni.showToast({
|
||
title: '请重新连接'
|
||
});
|
||
uni.navigateTo({
|
||
url: '/pages/search/index'
|
||
})
|
||
},
|
||
})
|
||
}
|
||
// 550800019301112f
|
||
// 550800019105118c
|
||
|
||
//重连设备
|
||
function reconnectDevice() {
|
||
// #ifdef MP-WEIXIN
|
||
uni.offBLEConnectionStateChange(listener) // 先移除
|
||
// #endif
|
||
reconnectTimes = 0;
|
||
clearInterval(reconnectTimer)
|
||
if (manualDisconnect) // 主动断开的不做处理
|
||
return
|
||
uni.hideLoading()
|
||
uni.showLoading({
|
||
mask: true,
|
||
title: '重连中...'
|
||
})
|
||
reconnect()
|
||
reconnectTimer = setInterval(
|
||
() => {
|
||
console.log('重连?>>>>>>>');
|
||
reconnect()
|
||
}, 3000); // 7000
|
||
}
|
||
|
||
function reconnect() {
|
||
if (connected) //已连接
|
||
{
|
||
reconnectTimes = 0;
|
||
clearInterval(reconnectTimer)
|
||
return
|
||
}
|
||
if (reconnectTimes >= 2) //超时 // 9
|
||
{
|
||
reconnectTimes = 0;
|
||
clearInterval(reconnectTimer)
|
||
uni.hideLoading()
|
||
showModal()
|
||
return
|
||
} else {
|
||
reconnectTimes += 1
|
||
if (isOpen) {
|
||
if (!auto_connect) {
|
||
connectNow()
|
||
}
|
||
} else {
|
||
if (isBleOn) {
|
||
openBluetoothAdapter(true)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function connectNow() {
|
||
if (!isConnecting) {
|
||
isConnecting = true
|
||
// #ifdef MP-WEIXIN
|
||
uni.offBLEConnectionStateChange(listener) // 先移除
|
||
// #endif
|
||
//监听蓝牙连接状态
|
||
uni.onBLEConnectionStateChange(listener)
|
||
uni.createBLEConnection({
|
||
deviceId: deviceId,
|
||
timeout: 3000,
|
||
success: (res) => {
|
||
uni.hideLoading()
|
||
device = devices.find(item => item.deviceId == deviceId)
|
||
isConnecting = false
|
||
connected = true
|
||
reconnectTimes = 0;
|
||
clearInterval(reconnectTimer) //结束循环
|
||
getBLEDeviceServices(deviceId)
|
||
|
||
|
||
},
|
||
fail: (res) => {
|
||
isConnecting = false
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
|
||
// 获取当前状态
|
||
// 用于下拉刷新
|
||
function getBluetoothAdapterState() {
|
||
uni.getBluetoothAdapterState({
|
||
success: (res) => {
|
||
if (!res.available) {
|
||
openBluetoothAdapter()
|
||
} else {
|
||
uni.stopBluetoothDevicesDiscovery({
|
||
complete: () => {
|
||
startBluetoothDevicesDiscovery();
|
||
}
|
||
})
|
||
}
|
||
},
|
||
fail: (err) => {}
|
||
})
|
||
}
|
||
|
||
// 断开蓝牙设备连接
|
||
function closeBLEConnection(close, callback, sameDevice) {
|
||
try {
|
||
// console.log('123', close)
|
||
if (close) {
|
||
manualDisconnect = true;
|
||
}
|
||
console.log('123', deviceId)
|
||
if (deviceId) {
|
||
uni.closeBLEConnection({
|
||
deviceId: deviceId,
|
||
success: (res) => {
|
||
handleCloseBle(close, callback, sameDevice)
|
||
},
|
||
fail: (err) => {
|
||
if (err.errCode == 10006) {
|
||
handleCloseBle(close, callback, sameDevice)
|
||
}
|
||
}
|
||
})
|
||
} else {
|
||
// console.log('123', typeof callback == 'function')
|
||
if (callback && typeof callback == 'function') {
|
||
callback()
|
||
}
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.log(error)
|
||
}
|
||
}
|
||
|
||
function handleCloseBle(close, callback, sameDevice) {
|
||
if (!sameDevice) {
|
||
deviceId = ''
|
||
}
|
||
connected = false
|
||
lastBatteryLevel = 'N/A';
|
||
// 手动断开连接处理
|
||
if (close) {
|
||
closeBluetoothAdapter(true)
|
||
}
|
||
if (callback && typeof callback == 'function') {
|
||
callback()
|
||
}
|
||
}
|
||
|
||
// 关闭蓝牙扫描
|
||
function stopBluetoothDevicesDiscovery() {
|
||
isConnecting = false;
|
||
uni.stopBluetoothDevicesDiscovery({
|
||
success: (res) => {
|
||
}
|
||
})
|
||
}
|
||
|
||
export const stopBlue = stopBluetoothDevicesDiscovery;
|
||
|
||
// 关闭蓝牙适配器
|
||
function closeBluetoothAdapter(doCallback) {
|
||
uni.stopBluetoothDevicesDiscovery({
|
||
success: (result) => {
|
||
uni.closeBluetoothAdapter({
|
||
success: (res) => {
|
||
// 手动断开连接处理
|
||
}
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 蓝牙断开 弹窗提示 (重连不可连上)
|
||
function showModal() {
|
||
deviceId = ''
|
||
devices = []
|
||
// auto_connect = true // 开启全局自动连接
|
||
openBluetoothAdapter(true)
|
||
// startAutoConnect()
|
||
uni.showModal({
|
||
content: '设备重连失败,已断开连接!',
|
||
showCancel: false,
|
||
success() {
|
||
uni.reLaunch({
|
||
url: '/pages/search/index'
|
||
});
|
||
}
|
||
})
|
||
}
|
||
|
||
// #################################################
|
||
// #
|
||
// # Utility Functions
|
||
// #
|
||
// #################################################
|
||
|
||
/////////////////// 工具函数 /////////////////////
|
||
function splitByLen(str, len) {
|
||
let length = str.length
|
||
let newArr = []
|
||
for (let i = 0; i < length; i += len) {
|
||
newArr.push(str.substr(i, len))
|
||
}
|
||
return newArr
|
||
}
|
||
|
||
function arrayBufferToString(arr) {
|
||
if (typeof arr === 'string') {
|
||
return arr;
|
||
}
|
||
var dataview = new DataView(arr);
|
||
var ints = new Uint8Array(arr.byteLength);
|
||
for (var i = 0; i < ints.length; i++) {
|
||
ints[i] = dataview.getUint8(i);
|
||
}
|
||
arr = ints;
|
||
var str = '',
|
||
_arr = arr;
|
||
for (var i = 0; i < _arr.length; i++) {
|
||
var one = _arr[i].toString(2),
|
||
v = one.match(/^1+?(?=0)/);
|
||
if (v && one.length == 8) {
|
||
var bytesLength = v[0].length;
|
||
var store = _arr[i].toString(2).slice(7 - bytesLength);
|
||
for (var st = 1; st < bytesLength; st++) {
|
||
store += _arr[st + i].toString(2).slice(2);
|
||
}
|
||
str += String.fromCharCode(parseInt(store, 2));
|
||
i += bytesLength - 1;
|
||
} else {
|
||
str += String.fromCharCode(_arr[i]);
|
||
}
|
||
}
|
||
return str;
|
||
}
|
||
|
||
|
||
function inArray(arr, key, val) {
|
||
for (let i = 0; i < arr.length; i++) {
|
||
if (arr[i][key] === val) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// 字符串转为ArrayBuffer对象,参数为字符串,注意这里的字符串为非hex字符串
|
||
function stringToArrayBuffer(str) {
|
||
var bytes = new Array();
|
||
var len, c;
|
||
len = str.length;
|
||
for (var i = 0; i < len; i++) {
|
||
c = str.charCodeAt(i);
|
||
if (c >= 0x010000 && c <= 0x10FFFF) {
|
||
bytes.push(((c >> 18) & 0x07) | 0xF0);
|
||
bytes.push(((c >> 12) & 0x3F) | 0x80);
|
||
bytes.push(((c >> 6) & 0x3F) | 0x80);
|
||
bytes.push((c & 0x3F) | 0x80);
|
||
} else if (c >= 0x000800 && c <= 0x00FFFF) {
|
||
bytes.push(((c >> 12) & 0x0F) | 0xE0);
|
||
bytes.push(((c >> 6) & 0x3F) | 0x80);
|
||
bytes.push((c & 0x3F) | 0x80);
|
||
} else if (c >= 0x000080 && c <= 0x0007FF) {
|
||
bytes.push(((c >> 6) & 0x1F) | 0xC0);
|
||
bytes.push((c & 0x3F) | 0x80);
|
||
} else {
|
||
bytes.push(c & 0xFF);
|
||
}
|
||
}
|
||
var array = new Int8Array(bytes.length);
|
||
for (var i in bytes) {
|
||
array[i] = bytes[i];
|
||
}
|
||
return array.buffer;
|
||
}
|
||
|
||
function hexArr2ab(hexArr) {
|
||
let buffer = new ArrayBuffer(hexArr.length)
|
||
let dataView = new DataView(buffer)
|
||
for (var i = 0; i < hexArr.length; i++) {
|
||
dataView.setUint8(i, hexArr[i])
|
||
}
|
||
return buffer;
|
||
}
|
||
|
||
function hexStr2ab(hexStr) {
|
||
let buffer = new ArrayBuffer(hexStr.length / 2)
|
||
let dataView = new DataView(buffer)
|
||
for (var i = 0, j = 0; i < hexStr.length; i += 2, j++) {
|
||
let _str = "0x" + hexStr.substr(i, 2)
|
||
dataView.setUint8(j, parseInt(_str, 16))
|
||
}
|
||
return buffer;
|
||
}
|
||
// ArrayBuffer转16进度字符串示例
|
||
function ab2hex(buffer) {
|
||
var hexArr = Array.prototype.map.call(
|
||
new Uint8Array(buffer),
|
||
function(bit) {
|
||
return ('00' + bit.toString(16)).slice(-2)
|
||
}
|
||
)
|
||
return hexArr.join('');
|
||
}
|
||
|
||
function hexStr2HexArr(hexStr) {
|
||
var a = [];
|
||
for (var i = 0; i < hexStr.length; i += 2) {
|
||
let _str = "0x" + hexStr.substr(i, 2)
|
||
a.push(parseInt(_str, 16));
|
||
}
|
||
return a;
|
||
}
|
||
|
||
function hexArr2HexStr(hexArr) {
|
||
let arr = []
|
||
hexArr.forEach(item => {
|
||
arr.push(item.toString(16).padLeft(2, '0'))
|
||
})
|
||
return arr.join('')
|
||
}
|
||
|
||
// 10进制转16进制补0
|
||
function dec2hex(dec, len) { //10进制转16进制补0
|
||
var hex = "";
|
||
while (dec) {
|
||
var last = dec & 15;
|
||
hex = String.fromCharCode(((last > 9) ? 55 : 48) + last) + hex;
|
||
dec >>= 4;
|
||
}
|
||
if (len) {
|
||
while (hex.length < len) hex = '0' + hex;
|
||
}
|
||
return hex;
|
||
}
|
||
// 10进制转16进制补0
|
||
function string_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);
|
||
}
|
||
|
||
//校验和
|
||
function checkSumResult(hexStr, len) //返回校验和的hexstring
|
||
{
|
||
let abData = hexStr2HexArr(hexStr)
|
||
var ucI = 0,
|
||
ucJ = 0;
|
||
var uiCrcValue = 0xffff;
|
||
for (ucI = 0; ucI < len; ucI++) {
|
||
uiCrcValue = (uiCrcValue ^ (abData[ucI] & 0xff));
|
||
for (ucJ = 0; ucJ < 8; ucJ++) {
|
||
if ((uiCrcValue & 0x0001) == 1) {
|
||
uiCrcValue = ((uiCrcValue >> 1) ^ 0x8408);
|
||
} else {
|
||
uiCrcValue = (uiCrcValue >> 1);
|
||
}
|
||
}
|
||
}
|
||
//校验和为uiCrcValue 的高低位互换
|
||
var hexStr = dec2hex(uiCrcValue, 4);
|
||
var resultStr = hexStr.substr(2, 2) + hexStr.substr(0, 2)
|
||
return resultStr;
|
||
}
|
||
|
||
function Uint8ToStr(arr) {
|
||
for (var i = 0, str = ''; i < arr.length; i++)
|
||
str += String.fromCharCode(arr[i]);
|
||
return str;
|
||
}
|
||
|
||
function strToUint8(str) {
|
||
for (var i = 0, arr = []; i < str.length; i++) {
|
||
arr.push(str.charCodeAt(i));
|
||
}
|
||
return new Uint8Array(arr);
|
||
}
|
||
|
||
export function getDiscoveredDevices() {
|
||
return [...devices];
|
||
}
|
||
|
||
// 手动断开蓝牙
|
||
export function manualDisconnectDevice() {
|
||
closeBLEConnection(true);
|
||
}
|