class BleHelper { constructor() { this.StorageKey = "linkedDevices"; this.init(); } init() { let key = this.StorageKey; var store = uni.getStorageInfoSync(); var f = store.keys.find(function(v) { return v == key; }); var linkedDevices = []; if (f) { var str = uni.getStorageSync(key); ////console.log(str); if (str) { linkedDevices = str; } else { linkedDevices = []; } } linkedDevices.filter((v) => { v.Linked = false; v.notifyState = false; }); setTimeout(() => { this.linkAllDevices(); }, 10); this.data = { isOpenBlue: false, //蓝牙模块是否开启 available: false, //蓝牙状态是否可用 discovering: false, //蓝牙是否正在搜索 searchList: [], //已搜索到的设备列表, isSubscribe: false, //是否开启了订阅 LinkedList: linkedDevices, //已连接的设备列表 } this.cfg = { onDeviceFound: [], //发现新设备的事件 receivDataCallback: [] //接收到数据的事件 } this.dic = { errRemarks: [{ key: '10000', remark: '未初始化蓝牙适配器' }, { key: '10001', remark: '当前蓝牙适配器不可用' }, { key: '10002', remark: '没有找到指定设备' }, { key: '10003', remark: '连接失败' }, { key: '10004', remark: '没有找到指定服务' }, { key: '10005', remark: '没有找到指定特征值' }, { key: '10006', remark: '当前连接已断开' }, { key: '10007', remark: '当前特征值不支持此操作' }, { key: '10008', remark: '其余所有系统上报的异常' }, { key: '10009', remark: '系统不支持 BLE低功耗蓝牙' }, { key: '10010', remark: '已连接' }, { key: '10011', remark: '配对设备需要配对码' }, { key: '10012', remark: '连接超时' }, { key: '10013', remark: '连接 deviceId 为空或者是格式不正确' } ] } } linkAllDevices() { console.log("模块启动时,自动连接已连接过的设备", this.data.LinkedList); if (this.data.LinkedList && this.data.LinkedList.length > 0) { for (var i = 0; i < this.data.LinkedList.length; i++) { let device = this.data.LinkedList[i]; console.log("自动连接:" + device.deviceId); this.LinkBlue(device.deviceId, device.writeServiceId, device.wirteCharactId, device .notifyCharactId); } } else { console.log("无设备连接"); } } getCurrentPagePath() { const pages = getCurrentPages(); if (pages.length === 0) { return ''; } const currentPage = pages[pages.length - 1]; return currentPage.route; } //设置发现新设备的回调 addDeviceFound(callback) { let key = this.getCurrentPagePath(); if (key) { let f = this.cfg.onDeviceFound.findIndex((v) => { return v.key == key; }); if (f > -1) { this.cfg.onDeviceFound[f].callback = callback; return; } this.cfg.onDeviceFound.push({ key: key, callback: callback }); } } //设置接收到数据的回调 addReceiveCallback(callback) { let key = this.getCurrentPagePath(); if (key) { let f = this.cfg.receivDataCallback.findIndex((v) => { return v.key == key; }); if (f > -1) { this.cfg.receivDataCallback.callback = callback; return; } this.cfg.receivDataCallback.push({ key: key, callback: callback }); } } //设置发现新设备的回调 removeDeviceFound() { let key = this.getCurrentPagePath(); if (key) { let f = this.cfg.onDeviceFound.findIndex((v) => { return v.key == key; }); if (f > -1) { this.cfg.onDeviceFound.splice(f, 1); } } } //设置接收到数据的回调 removeReceiveCallback() { let key = this.getCurrentPagePath(); if (key) { let f = this.cfg.receivDataCallback.findIndex((v) => { return v.key == key; }); if (f > -1) { this.cfg.receivDataCallback.splice(f, 1); } } } getError(ex) { let code = ex.code; ex.msg = "未知的异常" let f = this.dic.errRemarks.find((v) => { return v.key == code; }); if (f) { ex.msg = f.remark; } return ex; } //引导到设置中打开蓝牙 showBlueSetting(showTip) { let platform = process.env.UNI_PLATFORM; var openBlueSetting = function() { // 判断平台类型 if (platform === 'mp-weixin') { uni.openSetting(); } else if (platform === 'app-plus' || platform === 'app') { //---------------------------------------------------------------- const osName = plus.os.name; if (osName === 'iOS') { // iOS 平台打开蓝牙设置 plus.runtime.openURL('App-Prefs:root=Bluetooth', function() { ////console.log('成功打开蓝牙设置'); }, function(err) { console.error('打开蓝牙设置失败:' + err.message); uni.showModal({ title: '提示', content: '无法自动打开蓝牙设置,请手动前往设置 > 蓝牙 进行操作。', showCancel: false }); }); } else if (osName === 'Android') { // Android 平台打开蓝牙设置 try { const Intent = plus.android.importClass('android.content.Intent'); const Settings = plus.android.importClass('android.provider.Settings'); const main = plus.android.runtimeMainActivity(); const intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); main.startActivity(intent); } catch (e) { console.error('打开蓝牙设置失败:' + e.message); // 尝试使用通用设置页面作为备选方案 plus.runtime.openURL('settings://', function() { ////console.log('打开系统设置成功,请手动找到蓝牙选项'); }, function() { uni.showModal({ title: '提示', content: '无法自动打开蓝牙设置,请手动前往设置页面开启蓝牙。', showCancel: false }); }); } } else { uni.showModal({ title: '提示', content: '当前系统不支持自动打开蓝牙设置,请手动操作。', showCancel: false }); } //-------------------------------------------------------------------- } else if (platform === 'mp-alipay') { uni.openSetting(); } } if (showTip !== undefined) { openBlueSetting(); return; } if (platform === 'mp-weixin' || platform === 'app-plus' || platform === 'mp-alipay' || platform === 'app') { uni.showModal({ title: '蓝牙未开启', content: '请在系统设置中打开蓝牙以使用此功能', success: (res) => { if (res.confirm) { openBlueSetting(); } } }); } else { ////console.log("当前平台不支持打开系统设置" + platform); } } //获取蓝牙适配器状态 CheckBlue() { return new Promise((resolve, reject) => { if (this.data.available) { resolve({ available: this.data.available, discovering: this.data.discovering }); return; } if (!this.data.isOpenBlue) { resolve({ available: false, discovering: false }); return; } uni.getBluetoothAdapterState({ success: (info) => { resolve(info); }, fail: (ex1) => { ////console.log("ex1", ex1); let res1 = { available: false, discovering: false } resolve(res1); } }); }); } //打开蓝牙适配器 OpenBlue() { var init = () => { return new Promise((resolve, reject) => { if (this.data.isOpenBlue) { console.log("蓝牙状态已可用,提前返回"); resolve(false); return; } uni.openBluetoothAdapter({ success: (args) => { console.log("蓝牙初始化成功:" + JSON.stringify(args)); this.data.isOpenBlue = true; resolve(true); if (this.data.isSubscribe) { //整个App生命周期,只订阅一次 return; } console.log("开始订阅各类变化消息"); this.data.isSubscribe = true; var bytesToHexString = (bytes) => { return bytes.map(byte => byte.toString(16).padStart(2, '0')) .join(' '); } uni.onBluetoothAdapterStateChange((state) => { console.log('蓝牙状态发生变化:' + JSON.stringify(state)); this.data.available = state.available; this.data.discovering = state.discovering; if (this.data.available && this.data .isOpenBlue) { //蓝牙状态再次可用,重连所有设备 } if (!state.available) { //蓝牙状态不可用了,将所有设备标记为断开连接 this.data.LinkedList.filter((v) => { v.Linked = false; v.notifyState=false; return true; }); uni.setStorageSync(this.StorageKey, this.data .LinkedList); } }); uni.onBLEConnectionStateChange((res) => { if (!res.connected) { console.log("蓝牙连接已断开", res); this.data.LinkedList.find((v) => { if (v.deviceId == res.deviceId) { v.Linked = false; v.notifyState = false; return true; } return false; }); uni.setStorageSync(this.StorageKey, this.data .LinkedList); } else{ console.log("蓝牙连接已恢复,",res); } }); uni.onBluetoothDeviceFound((devices) => { // ////console.log("发现新设备:" + JSON.stringify(devices)); this.data.searchList = this.data.searchList.concat( devices); if (this.cfg.onDeviceFound) { if (this.cfg.onDeviceFound.length > 0) { this.cfg.onDeviceFound.forEach((found) => { found.callback(devices); }); } } }); uni.onBLECharacteristicValueChange((receive) => { let dataView = new DataView(receive.value); // 转换为字节数组 let bytes = []; for (let i = 0; i < dataView.byteLength; i++) { bytes.push(dataView.getUint8(i)); } let hexs = bytesToHexString(bytes); let str = ''; try { let uint8Array = new Uint8Array(receive.value); for (let i = 0; i < uint8Array.length; i++) { // 将每个字节转换为对应的字符 str += String.fromCharCode(uint8Array[i]); } //////console.log("收到的字符串:", str) } catch (ex) { ////console.log("将数据转文本失败", ex); } let recData = { bytes: bytes, str: str, hexs: hexs }; console.log("监听到特征值:" + JSON.stringify(recData)); if (this.cfg.receivDataCallback) { if (this.cfg.receivDataCallback.length > 0) { this.cfg.receivDataCallback.forEach((rec) => { rec.callback(recData); }) } } else { console.log("无任何人订阅receivDataCallback,不处理数据"); } }); }, fail: (ex2) => { console.log("蓝牙模块启动失败", ex2); reject(this.getError(ex2)); } }); }); } return new Promise((resolve, reject) => { if (this.data.isOpenBlue) { resolve(); return; } this.CheckBlue().then((res) => { console.log("res=", res) return init(); }).then(resolve).catch((ex) => { console.log("22222"); reject(ex); }); }); } //关闭蓝牙适配器 CloseBlue() { return new Promise((resolve, reject) => { this.data.available = false; this.data.discovering = false; this.data.isOpenBlue = false; this.data.searchList = []; this.StopSearch(); this.disconnectDevice(); uni.closeBluetoothAdapter({ success: () => { console.log("蓝牙模块已关闭"); this.data.isOpenBlue = false; }, fail: (ex) => { ex = this.getError(ex); ////console.log(msg); }, complete: () => { resolve(); } }); }); } //开始搜索新设备 StartSearch() { this.data.searchList = []; var these = this; //开始搜索 var Search = () => { return new Promise((resolve, reject) => { uni.startBluetoothDevicesDiscovery({ services: [], allowDuplicatesKey: false, success: (res) => { //console.log('开始搜索蓝牙设备成功'); resolve(res); }, fail: (err) => { //console.log(`搜索蓝牙设备失败:`, err); reject(this.getError(err)); } }); }); } return this.OpenBlue().then((res) => { //console.log("蓝牙适配器状态", res) this.StopSearch(); return Search(); }); } //停止搜索 StopSearch() { return new Promise((resolve, reject) => { uni.stopBluetoothDevicesDiscovery({ success: (res) => { //console.log("停止搜索蓝牙设备成功"); resolve(); }, fail: (ex) => { //console.log("无法停止蓝牙搜索"); reject(this.getError(ex)); } }); }) } //获取已连接的设备 getLinkBlue(callback) { return new Promise((resolve, reject) => { uni.getConnectedBluetoothDevices({ success: (res) => { resolve(res); }, fail: (ex) => { ////console.log("获取已连接设备异常", ex); reject({ devices: [] }); } }); }); } //订阅消息 subScribe(deviceId, state) { return new Promise((resolve, reject) => { setTimeout(() => { let c = this.data.LinkedList.find((v) => { return v.deviceId == deviceId; }); if (state) { if (c.notifyState) { resolve(); return; } } //console.log("开始订阅消息"); uni.notifyBLECharacteristicValueChange({ deviceId: deviceId, serviceId: c.notifyServiceid, characteristicId: c.notifyCharactId, state: state, success: (res) => { if (state) { console.log("订阅消息成功", res); } else { console.log("取消订阅成功", res); } this.data.LinkedList.find((v) => { if (v.deviceId == deviceId) { v.notifyState = state; } }); resolve(); }, fail: (ex) => { reject(this.getError(ex)); } }); }, 20); }); } //获取服务 getService(id, targetServiceId, writeCharId, notifyCharId) { if (!targetServiceId) { targetServiceId = "FFE0"; } return new Promise((resolve, reject) => { var repeatCnt = 0; var startgetService = () => { uni.getBLEDeviceServices({ deviceId: id, success: (res) => { if (res.services && res.services.length > 0) { ////console.log("获取到服务:" + JSON.stringify(res)); this.data.LinkedList.find((v) => { if (v.deviceId == id) { v.services = res.services; } }); uni.setStorageSync(this.StorageKey, this.data.LinkedList); var promises = []; for (var i = 0; i < res.services.length; i++) { let service = res.services[i]; if (service.uuid.indexOf(targetServiceId) > -1) { promises.push(this.getFeatrus(id, service.uuid, writeCharId, notifyCharId)); } } if (promises.length == 0) { reject({ msg: "未找到主服务", code: -1 }); } else { Promise.all(promises) .then(results => { ////console.log('所有操作成功完成', results); resolve(); }) .catch(error => { reject(error); }); } } else { repeatCnt++; if (repeatCnt > 5) { reject({ msg: "获取设备服务失败", code: -1 }); return; } setTimeout(function() { startgetService(id); }, 100); } }, fail: (ex) => { reject(this.getError(ex)); } }) } setTimeout(function() { startgetService(id); }, 100); }); } //获取特征 getFeatrus(id, serviceId, writeCharId, notifyCharId) { var promise = new Promise((resolve, reject) => { uni.getBLEDeviceCharacteristics({ deviceId: id, serviceId: serviceId, success: (res) => { ////console.log("获取到特征:" + JSON.stringify(res)); //写特征 let writeChar = res.characteristics.find(char => char.uuid.indexOf(writeCharId) > -1 ); //通知特征 let notiChar = res.characteristics.find(char => char.uuid.indexOf(notifyCharId) > -1 ); this.data.LinkedList.find(function(v) { if (v.deviceId == id) { if (!v.Characteristics) { v.Characteristics = []; } v.Characteristics = v.Characteristics.concat(res .characteristics); if (writeChar) { v.writeServiceId = serviceId; v.wirteCharactId = writeChar.uuid; } if (notiChar) { v.notifyServiceid = serviceId; v.notifyCharactId = notiChar.uuid; } } }); this.subScribe(id, true); uni.setStorageSync(this.StorageKey, this.data.LinkedList); resolve(res); }, fail: (ex) => { ////console.log("获取特征出现异常:" + JSON.stringify(ex)); reject(ex); } }); }); return promise; } //连接某个设备 LinkBlue(deviceId, targetServiceId, writeCharId, notifyCharId) { this.StopSearch(); if (!writeCharId) { writeCharId = "FFE1"; } if (!notifyCharId) { notifyCharId = "FFE2"; } let fIndex = -1; let f = this.data.LinkedList.find((v, index) => { if (v.deviceId == deviceId) { fIndex = index; return true; } return false; }); ////console.log("findex=" + fIndex); var these = this; //连接设备 var linkDevice = () => { return new Promise((resolve, reject) => { if (fIndex > -1 && f?.Linked) { ////console.log("当前已连接,跳过其他步骤") resolve(false); return; } console.log("正在连接" + deviceId); uni.createBLEConnection({ deviceId: deviceId, timeout: 5000, success: (info) => { console.log("新连接成功"); this.getLinkBlue().then((arr) => { let cr = arr.devices.find(c => { if (c.deviceId == deviceId) { c.Linked = true; return true; } return false; }); if (fIndex > -1) { this.data.LinkedList[fIndex].Linked = true; } else { this.data.LinkedList.push(cr); } uni.setStorageSync(this.StorageKey, this.data .LinkedList); let os = plus.os.name; if (os == 'android') { uni.setBLEMTU({ deviceId: deviceId, mtu: 512, success: (mtu) => { ////console.log("mtu设置成功"); }, fail: () => { ////console.log("mtu设置失败") } }); } resolve(true); }).catch((ex) => { reject(this.getError(ex)); }); }, fail: (ex) => { ex = this.getError(ex) console.log("蓝牙连接失败" + JSON.stringify(ex)); reject(ex); } }); }); } return new Promise((resolve, reject) => { this.OpenBlue().then((res) => { ////console.log("2222222"); return linkDevice(deviceId); }).then((res) => { ////console.log("11111111"); if (res) { //新连接 ////console.log("开始获取服务") return this.getService(deviceId, targetServiceId, writeCharId, notifyCharId); //获取服务 } else { //已连接过,直接订阅消息 if (fIndex > -1 && f && !f.notifyState) { this.subScribe(deviceId, true); } return Promise.resolve(true); //已连接过的设备无需获取服务 } }).then(() => { return resolve(); }).catch((ex) => { ////console.log("出现异常", ex); reject(ex); }); }); } //断开连接 disconnectDevice(deviceId) { var disconnect = (id) => { return new Promise((resolve, reject) => { uni.closeBLEConnection({ deviceId: id, success: (res) => { ////console.log("蓝牙连接已断开:" + id); this.subScribe(id, false); resolve(); }, fail: (ex) => { reject(this.getError(ex)); } }); }); } if (deviceId) { disconnect(deviceId); this.data.LinkedList.find(v => { if (v.deviceId === deviceId) { return true; } return false; }); return; } if (this.data.LinkedList.length > 0) { let arr = []; for (var i = 0; i < this.data.LinkedList.length; i++) { let item = this.data.LinkedList[i]; if (item.Linked) { arr.push(disconnect(item.deviceId)); } } if (arr.length == 0) { return; } Promise.allSettled(arr).then((results) => { results.forEach((result, index) => { if (result.status === 'fulfilled') { ////console.log(`第${index + 1}个Promise成功:`, result.value); } else { ////console.log(`第${index + 1}个Promise失败:`, result.reason.message); } }); }); } else { ////console.log("无已连接设备"); } } sendData(deviceid, buffer, writeServiceId, wirteCharactId, ms) { console.log("deviceid=" + deviceid + ",writeServiceId=" + writeServiceId + ",wirteCharactId=" + wirteCharactId + ",timeout=" + ms) if (ms === undefined) { ms = 50; } let c = this.data.LinkedList.find((v) => { return v.deviceId == deviceid; }); let device = { deviceId: deviceid, writeServiceId: null, wirteCharactId: null }; if (writeServiceId) { device.writeServiceId = writeServiceId; } if (wirteCharactId) { device.wirteCharactId = wirteCharactId; } if (!device.writeServiceId || !device.wirteCharactId) { //console.log("从已连接中找设备") if (c) { device = c; } } let sendBuffer = () => { return new Promise(async (resolve, reject) => { var promise = new Promise((succ, err) => { if (!c) { err(this.getError({ code: 10002 })); //没有找到指定设备 return; } uni.writeBLECharacteristicValue({ deviceId: device.deviceId, serviceId: device.writeServiceId, characteristicId: device.wirteCharactId, value: buffer, success: () => { console.log("发送数据成功"); succ(); }, fail: (ex) => { ex = this.getError(ex); console.log("发送数据失败", ex); err(ex); } }); }); if (plus.os.name == 'iOS') { function timeOut(ms) { return new Promise((_, err) => { setTimeout(() => { err({ code: -1, errMsg: '超时了' }) }, ms); }); } Promise.race([timeOut(ms), promise]).then(resolve).catch((ex) => { console.log("ex=", ex); if (ex.code == -1) { resolve(); } else { reject(ex); } }).finally(() => { //console.log("完成了") }); } else { promise.then(resolve).catch(reject); } }); } if (c.Linked) { console.log("蓝牙已连接,直接发送"); return sendBuffer(); } else { console.log("先连接蓝牙再发送"); return new Promise((resolve, reject) => { this.LinkBlue(deviceid).then((res) => { console.log("连接成功"); return sendBuffer(); }).then(() => { //console.log("发送成功") resolve(); }).catch((ex) => { //console.log("出现异常", ex); reject(this.getError(ex)); }); }); } } } let instance = null; export default { getBleTool: function(found, receive) { if (!instance) { instance = new BleHelper(); ////console.log("第一次初始化"); } else { ////console.log("调用现有实例"); } return instance; } }