【uniapp】举例解决uniapp无法注销回调函数,导致多次调用 + 举例EPS32-arduino 与uniapp间蓝牙JSON通信 BLE链接与通信过程
1.【uniapp】举例解决uniapp无法注销回调函数,导致多次调用 + 举例EPS32-arduino 与uniapp间蓝牙JSON通信 BLE链接与通信过程
2.【uni-app】uni-icons 图标使用和通过文心一言解决icon不显示BUG3.【uniapp】BLE及通信过程+举例EPS32-arduino 与uniapp间蓝牙JSON通信4.【ESP32 Arduino】使用ArduinoJson 7.x库处理JSon数据5.【uniapp】使用举例 radio控件与data()内数据 响应式双向数据绑定6.【ESP32 Arduino】RS485通信及MODBUS RTU通信实例主要经验
1、ESP32 作为BLE服务器,将BLE客户端发来数据返回,作为调试手机APP的工具
2、编写一个公共文件ble_api.js,实现蓝牙功能。编写一个类,类中存储BLE通信数据和方法,定义此类唯一一个实例,供网页.vue调用。
3、为了解决uniapp蓝牙无法注销回调函数,在ble_api.js就BLE uni.notify编写唯一一个回调函数,保证唯一回调函数注册一次。否者会出现多次调用问题。
4、在回调函数中,使用uni.$emit公共函数,将BLE数据发送至具体网页.vue中。
5、经实验具体做法,在每个网页script内, onshow()中调用BLE类实例注册一次BLE uni.notify的回调函数。否者,要么BLE不接收数据,要么回调函数重复多次执行。
BLE uni.notify的回调函数中使用uni.$emit()转发BLE数据,在具体网页.vue的 onshow()使用uni.$on()监听接收与处理数据,在此具体网页.vue的onhide()中使用uni.$off()注销此监听。
6、推测:其他uniapp设备中,回调函数是否可以按照类似方法处理, 要么app生命周期内注册一次,要么在网页显示周期内注册一次?
ESP32程序
ESP32作为BLE服务器,将BLE客户端发来数据返回,作为调试手机APP的工具。

/* 将接收的数据,返送回去。 Create a BLE server that, once we receive a connection, will send periodic notifications. The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY" The design of creating the BLE server is: 1. Create a BLE Server 2. Create a BLE Service 3. Create a BLE Characteristic on the Service 4. Create a BLE Descriptor on the characteristic 5. Start the service. 6. Start advertising. 将接收的数据,返送回去。 */ #include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> #include "String.h" #include <ArduinoJson.h> // 需安装 ArduinoJson库 BLEServer *pServer = NULL; BLECharacteristic *pTxCharacteristic; bool deviceConnected = false; bool oldDeviceConnected = false; uint8_t txValue = 0; String Receive_Char_Read = ""; String Receive_char; // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID #define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" #define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" StaticJsonDocument<200> jsonBuffer; //声明一个JsonDocument对象,长度200 StaticJsonDocument<200> doc; void str2json(String jsonString) { // 反序列化JSON DeserializationError error = deserializeJson(jsonBuffer, jsonString); if (error) { Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); return; } // 解析JSON const char *str = jsonBuffer["name"]; // 读取字符串 int data1 = jsonBuffer["age"]; // 读取整形数据 Serial.println(str); Serial.println(data1); return; }; // 定义发送数据函数 void sendData(String jsonString) { // json数据及其字符串化 // doc["sensor"] = "sensor"; // doc["value"] = 1234; // String jsonString; // serializeJson(doc, jsonString); // 发送Json字符串 pTxCharacteristic->setValue(jsonString); // 设定特性值 //Serial.println(jsonString); //pTxCharacteristic->setValue(txString); pTxCharacteristic->notify(); //发送特性(描述符、值等) delay(100); // bluetooth stack will go into congestion, if too many packets are sent } // 定义处理连接等回调函数 class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer *pServer) { deviceConnected = true; }; void onDisconnect(BLEServer *pServer) { deviceConnected = false; } }; // 定义回调函数,处理接收数据。 class MyCallbacks : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { String rxValue = pCharacteristic->getValue(); if (rxValue.length() > 0) { Serial.println("*********"); Serial.print("Received: "); Serial.println(rxValue); // for (int i = 0; i < rxValue.length(); i++) { // Serial.print(rxValue[i]); // } // str2json(rxValue); // Serial.println(); // Serial.println("*********"); sendData(rxValue);//蓝牙发送 } } }; void setup() { Serial.begin(115200); // Create the BLE Device 初始化蓝牙外设 BLEDevice::init("UART Service"); // Create the BLE Server 创建一个服务器 pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); // Create the BLE Service 创建一个BLE服务 BLEService *pService = pServer->createService(SERVICE_UUID); // Create a BLE Characteristic 创建一个BLE特性 通知属性 pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY); pTxCharacteristic->addDescriptor(new BLE2902()); // 绑定描述符 // Create a BLE Characteristic 创建一个BLE特性 读属性 BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE); pRxCharacteristic->setCallbacks(new MyCallbacks()); // 绑定回调函数 // Start the service 启动服务器 pService->start(); // Start advertising 开始广播 pServer->getAdvertising()->start(); Serial.println("Waiting a client connection to notify..."); } void loop() { if (deviceConnected) { } // disconnecting if (!deviceConnected && oldDeviceConnected) { delay(500); // give the bluetooth stack the chance to get things ready pServer->startAdvertising(); // restart advertising Serial.println("start advertising"); oldDeviceConnected = deviceConnected; } // connecting if (deviceConnected && !oldDeviceConnected) { // do stuff here on connecting oldDeviceConnected = deviceConnected; } }
uniAPP程序
手机作为BLE客户端,包括功能有 BLE设备搜索、连接、接收和发送数据。
公共文件 BLEl类

1 // ble_api.js 2 3 // 文档地址 https://uniapp.dcloud.net.cn/api/system/ble.html 4 // 同ESP32内定义的UUID 5 const SERVICE_UUID_ESP32 = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"; // UART service UUID 6 const CHARACTERISTIC_UUID_ESP32_RX = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"; 7 const CHARACTERISTIC_UUID_ESP32_TX = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"; 8 // globalClass.js 9 class BLE_DeviceClass { 10 constructor() { 11 // ... 初始化代码 12 this.name = "BLE_APP"; 13 // 是否已连接 14 this.connected = false; 15 this.inited = false; 16 17 // 当前连接的设备id 18 this.connectId = ""; 19 this.previousConnectId = ""; //有链接时,为空 20 21 this.devices = []; //蓝牙周围设备列表 22 // 接收到的JSON字符串 23 this.rxJSONString = ""; 24 // 用于存储 接收到的JSON object数据 25 this.rxJSONobject = {}; // 用于存储BLE发来的JSON object数据 在defaultCallback(jsonString)方法中完成 26 27 // 避免以下重复注册回调函数,内部使用 28 this.BLEAdapterStateChangeCallback = false; 29 this.BLEConnectionStateChangeCallback = false; 30 //周期性函数 31 this.intervalId = null; // 用于存储setInterval返回的ID 32 33 // 创建一个数组来存储BLE接收事件监听器的引用(但实际上这里只是模拟,防止重复注册相同监听函数) 34 this.eventListeners = []; 35 } 36 /*-----------------------------------------------------------------------*/ 37 // 内部函数,防止重复添加 蓝牙接收数据回调函数 38 addeventListeners(callback) { // 参数为 funcction 39 40 // this.eventListeners.forEach(listener => {// 遍历 41 // // 判别是否已经注册 42 const index = this.eventListeners.indexOf(callback); 43 if (index !== -1) { 44 return false; // 已有,不重复添加 45 }; 46 // }); 47 this.eventListeners.push(callback); //添加 48 return true; 49 } 50 /*-----------------------------------------------------------------------*/ 51 // 内部函数,防止重复添加 蓝牙接收数据回调函数 52 isIneventListeners(callback) { 53 const index = this.eventListeners.indexOf(callback); 54 if (index !== -1) { 55 return true; 56 } else { 57 return false; 58 }; 59 } 60 /*-----------------------------------------------------------------------*/ 61 // 测试 62 myMethod() { 63 64 65 // ... 方法实现 66 console.log(this.name); 67 68 /* 69 const person = { 70 firstName: "Bill", 71 lastName : "Gates", 72 language : "EN" 73 }; 74 75 76 console.log( Object.keys(person)); 77 console.log( Object.values(person)); 78 let keys = Object.keys(person);// 获取JSON objet的键 79 let text = ""; 80 for (let key in person) { 81 text += person[key]; 82 person[key] = key+"_value"; 83 } 84 console.log( text); 85 console.log( person); 86 if(keys.includes("language")) 87 { 88 console.log(person["language"]); 89 }*/ 90 } 91 92 /*-----------------------------------------------------------------------*/ 93 // API函数,开启蓝牙外设 94 initBlue() { 95 console.log('初始化蓝牙开始...') 96 const _this = this; 97 return new Promise((resolve) => { 98 uni.openBluetoothAdapter({ 99 success(res) { 100 console.log('初始化蓝牙成功') 101 _this.inited = true; 102 resolve(true); 103 }, 104 fail(err) { 105 console.log('初始化蓝牙失败', err) 106 uni.showModal({ 107 title: '失败提示', 108 content: "初始化蓝牙失败", 109 showCancel: false 110 }); 111 } 112 }) 113 }); 114 } 115 /*-----------------------------------------------------------------------*/ 116 //API函数,扫描外围设备,包括ESP32 117 async scan_start() { 118 119 const _this = this; 120 if (!_this.inited) { 121 await _this.initBlue(); //注意this的作用域 122 }; 123 // 设定扫描回调函数 124 this.scan_callback(); 125 // 开始扫描外围设备 126 await uni.startBluetoothDevicesDiscovery({ 127 success(res) { 128 console.log('启动搜索'); 129 }, 130 fail(err) { 131 console.log('启动搜索失败', err) 132 uni.showModal({ 133 title: '失败提示', 134 content: "启动搜索失败", 135 showCancel: false 136 }); 137 } 138 }); 139 //console.log('读取搜索结果'); 140 // 扫描到外围蓝牙设备后的回调 141 } 142 // 内部函数,在扫描scan_start()前设定回调函数。 143 async scan_callback() { 144 const _this = this; 145 //console.log('搜索结果'); 146 await uni.onBluetoothDeviceFound( 147 ({ 148 devices 149 }) => { 150 _this.devices.push(...devices); 151 // 去重 152 _this.devices = [..._this.devices].filter((cur, index, self) => { 153 return self.findIndex(item => item.deviceId == cur.deviceId) === 154 index; 155 }); 156 //console.log(_this.devices); 157 }); 158 } 159 /*-----------------------------------------------------------------------*/ 160 //API函数,扫描外围设备,包括ESP32 161 async scan_stop() { 162 // 停止扫描设备, 否则会浪费性能 163 await uni.stopBluetoothDevicesDiscovery({ 164 success(res) { 165 console.log("停止蓝牙设备搜索") 166 //console.log(res) 167 }, 168 fail(err) { 169 console.error(err); 170 } 171 }); 172 } 173 174 /*-----------------------------------------------------------------------*/ 175 // 内部函数,字符串转为ArrayBuffer对象,参数为字符串 176 str2ab(str) { 177 var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节 178 var bufView = new Uint16Array(buf); 179 for (var i = 0, strLen = str.length; i < strLen; i++) { 180 bufView[i] = str.charCodeAt(i); 181 } 182 return buf; 183 } 184 185 /*-----------------------------------------------------------------------*/ 186 // 内部函数,监听蓝牙模块状态 187 setCheckAdapterStateChange() { 188 // 避免以下重复注册回调函数 189 let _this = this; 190 if (_this.BLEAdapterStateChangeCallback) { 191 return; 192 } 193 //this.BLEConnectionStateChangeCallback = false; 194 uni.onBluetoothAdapterStateChange(function(res) { 195 console.log('BLE adapterState changed, now is', res) 196 _this.BLEAdapterStateChangeCallback = true; 197 }); 198 } 199 /*-----------------------------------------------------------------------*/ 200 //监听低功耗蓝牙连接状态的改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等等 201 setCheckConnectionStateChange() { 202 // 避免以下重复注册回调函数 203 let _this = this; 204 if (_this.BLEConnectionStateChangeCallback) { 205 return; 206 }; 207 208 uni.onBLEConnectionStateChange(function(res) { 209 //console.log('BLEConnectionState changed, now is', res); 210 // 该方法回调中可以用于处理连接意外断开等异常情况 211 //console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`); 212 213 if (!res.connected) { 214 // 是否已连接 215 _this.connected = false; 216 // 当前连接的设备id 217 console.log(`BLE device ${res.deviceId} state has disconnected`); 218 this.connectId = ""; 219 }; 220 221 _this.BLEConnectionStateChangeCallback = true; 222 223 }); 224 } 225 /*-----------------------------------------------------------------------*/ 226 // 内部函数 227 setMTU() { 228 const _this = this; 229 //3)设置MTU,否则传输报文不全,默认是23bytes,但是蓝牙本身是需要3bytes,故而只能传输20bytes 230 setTimeout(() => { 231 console.log('deviceId>>>', _this.connectId); 232 uni.setBLEMTU({ 233 deviceId: _this.connectId, 234 mtu: 512, 235 success: (res) => { 236 console.log('设置MTU最大值512成功', res); 237 //启动蓝牙接收数据 238 }, 239 fail: (res) => { 240 console.log('设置MTU最大值512失败', res); 241 } 242 }); 243 }, 1000); 244 245 } 246 //API函数, 247 connect() { 248 const _this = this; 249 console.log('设备id', _this.connectId); 250 251 const promise = new Promise((resolve) => { 252 uni.showLoading({ 253 title: '连接蓝牙设备中...' 254 }) 255 uni.createBLEConnection({ 256 deviceId: _this.connectId, 257 success(res) { 258 console.log('蓝牙设备连接成功'); 259 _this.previousConnectId = null; // 有连接时置为空 260 console.log(res); 261 // 监听蓝牙模块状态 262 _this.setCheckAdapterStateChange(); 263 // 设置MTU,传输报文255字节 264 _this.setMTU(); 265 // 在需要关闭加载提示的时候,调用 uni.hideLoading 方法 266 uni.hideLoading(); 267 }, 268 fail(err) { 269 console.log('蓝牙设备连接失败'); 270 console.error(err); 271 // 在需要关闭加载提示的时候,调用 uni.hideLoading 方法 272 uni.hideLoading(); 273 } 274 }); 275 }); 276 277 } 278 /*-----------------------------------------------------------------------*/ 279 //API函数,断开链接 280 disConnect() { 281 const _this = this; 282 _this.previousConnectId = _this.connectId; 283 if (!_this.connectId) return; 284 uni.showLoading({ 285 title: '断开蓝牙连接中...' 286 }); 287 return new Promise((resolve) => { 288 uni.closeBLEConnection({ 289 deviceId: _this.connectId, 290 success(res) { 291 console.log("断开连接成功:", res) 292 293 // 断开后必须释放蓝牙资源,不然再点击连接就连不上了 294 // https://uniapp.dcloud.net.cn/api/system/bluetooth.html 295 uni.closeBluetoothAdapter({ 296 success() { 297 console.log('释放蓝牙资源成功') 298 _this.connected = false; 299 _this.connectId = null 300 _this.SERVICE_UUID = null 301 _this.CHARACTERISTIC_UUID = null; 302 _this.inited = false; 303 _this.received = false; 304 305 // 延时下保险点 306 setTimeout(() => { 307 uni.hideLoading(); 308 resolve(); 309 }, 600) 310 }, 311 fail() { 312 uni.hideLoading(); 313 } 314 }) 315 316 }, 317 fail(err) { 318 uni.hideLoading(); 319 console.log('断开连接失败:', err) 320 uni.showModal({ 321 title: '失败提示', 322 content: "断开连接失败 " + err.errMsg, 323 showCancel: false 324 }); 325 } 326 }) 327 }) 328 } 329 /*-----------------------------------------------------------------------*/ 330 // API函数,获取蓝牙服务UUID等信息 331 getServices() { 332 uni.getBLEDeviceServices({ 333 deviceId: this.connectId, // 设备ID 334 success(res) { 335 console.log(res); 336 }, 337 fail(err) { 338 console.error(err); 339 } 340 }); 341 } 342 /*-----------------------------------------------------------------------*/ 343 // API函数,获取指定服务下的特性 344 getCharacteristics() { 345 uni.getBLEDeviceCharacteristics({ 346 deviceId: this.connectId, // 设备ID 347 serviceId: SERVICE_UUID_ESP32, // 服务UUID 348 success(res) { 349 console.log(res); 350 }, 351 fail(err) { 352 console.error(err); 353 } 354 }); 355 } 356 /*-----------------------------------------------------------------------*/ 357 /*接收外设设备发来的具有通知属性服务及其特性*/ 358 // 内部函数,ArrayBuffer转16进度字符串示例 359 ab2hex(buffer) { 360 const hexArr = Array.prototype.map.call( 361 new Uint8Array(buffer), 362 function(bit) { 363 return ('00' + bit.toString(16)).slice(-2); 364 } 365 ); 366 return hexArr.join(''); 367 } 368 369 // 内部函数,将16进制的内容转成我们看得懂的字符串内容 370 hexCharCodeToStr(hexCharCodeStr) { 371 var trimedStr = hexCharCodeStr.trim(); 372 var rawStr = 373 trimedStr.substr(0, 2).toLowerCase() === '0x' ? 374 trimedStr.substr(2) : 375 trimedStr; 376 var len = rawStr.length; 377 if (len % 2 !== 0) { 378 alert('存在非法字符!'); 379 return ''; 380 } 381 var curCharCode; 382 var resultStr = []; 383 for (var i = 0; i < len; i = i + 2) { 384 curCharCode = parseInt(rawStr.substr(i, 2), 16); 385 resultStr.push(String.fromCharCode(curCharCode)); 386 } 387 return resultStr.join(''); 388 } 389 // 接收回调函数,将数据进行处理,保存为JSON object 390 // 在网页中,编写回调函数处理业务,并且要调用此函数。 391 defaultCallback(jsonString) { 392 const _this = this; 393 console.log('Callback function called, raised by onBLECharacteristicValueChange'); 394 if (!jsonString) { 395 return; 396 } 397 //将监听到的值转发其他网页 398 uni.$emit('BLE_RX_Value', { 399 msg: jsonString 400 }) //触发全局的自定义事件。附加参数都会传给监听器回调。 401 402 // 403 console.log(jsonString); 404 _this.rxJSONString = jsonString; // 收到json字符串 405 // 使用JSON.parse()将字符串转换回对象 406 let rxjsonObj = JSON.parse(_this.rxJSONString); 407 408 // 遍历rxjsonObj的所有键,组成数组 409 let keys = []; 410 for (let key in rxjsonObj) { 411 // 键在obj中不存在或值不相同 412 if (rxjsonObj.hasOwnProperty(key)) { 413 keys.push(key); // 获取JSON objet的键 414 } 415 }; 416 //console.log(keys); 417 418 // 保存键值对 放在 this.rxJSONobject 419 for (let key in rxjsonObj) { 420 let element = key; 421 let value = rxjsonObj[element]; 422 _this.rxJSONobject[element] = value; 423 } 424 console.log(_this.rxJSONobject); 425 } 426 // API函数,接收,将数据进行处理在回调函数中callback处理 427 // 仅在App.vue 中调用一次。 428 async notify(callback = this.defaultCallback()) { 429 const _this = this; 430 try { 431 // 已从BLE接收的数据,设置回调调函数处理。 432 let resultFromFunB = await uni.onBLECharacteristicValueChange(res => { 433 // 结果 434 //console.log(res); 435 // 结果里有个value值,该值为 ArrayBuffer 类型,所以在控制台无法用肉眼观察到,必须将该值转换为16进制 436 let resHex = this.ab2hex(res.value); 437 //console.log(resHex); 438 // 最后将16进制转换为ascii码,就能看到对应的结果 439 let jsonString = this.hexCharCodeToStr(resHex); 440 441 callback(jsonString); // 回调函数实现功能 442 }); 443 console.log('funB的结果:', resultFromFunB); // 打印funB的结果 444 // 启动BLE接收功能 445 const resultFromFunA = await uni.notifyBLECharacteristicValueChange({ 446 deviceId: this.connectId, // 设备ID 447 serviceId: SERVICE_UUID_ESP32, // 服务UUID 448 characteristicId: CHARACTERISTIC_UUID_ESP32_TX, // 特性UUID 449 success(res) { 450 console.log(res); 451 }, 452 fail(err) { 453 console.error(err); 454 } 455 }); 456 457 } catch (error) { 458 // 处理可能出现的错误 459 console.error('执行函数时出错:', error); 460 } finally { 461 console.log('notify() finish'); // 打印结果 462 }; 463 } 464 /*-----------------------------------------------------------------------*/ 465 //API函数, 向外围蓝牙设备发送数据,指定服务及其特性的UUID 466 send(jsonObject) { 467 // 向蓝牙设备发送一个0x00的16进制数据 468 // let jsonObject = { 469 // name: "John", 470 // age: -30, 471 // city: "New York" 472 // }; 473 return new Promise((resolve, reject) => { 474 let jsonString = JSON.stringify(jsonObject); 475 //字符串转为arraybuffer 476 const buffer = new ArrayBuffer(jsonString.length); 477 const dataView = new DataView(buffer); 478 // dataView.setUint8(0, 0) 479 for (var i = 0; i < jsonString.length; i++) { 480 dataView.setUint8(i, jsonString.charAt(i).charCodeAt()); 481 } 482 483 uni.writeBLECharacteristicValue({ 484 deviceId: this.connectId, // 设备ID 485 serviceId: SERVICE_UUID_ESP32, // 服务UUID 486 characteristicId: CHARACTERISTIC_UUID_ESP32_RX, // 特性UUID 487 value: buffer, 488 success(res) { 489 490 //console.log(res); 491 resolve(res); 492 }, 493 fail(err) { 494 //console.error(err); 495 reject(err); 496 } 497 }); 498 }); 499 } 500 /*-----------------------------------------------------------------------*/ 501 // 接口函数 点击某个设备的连接 502 async connectClick(item) { 503 let _this = this; 504 console.log('即将连接蓝牙:', item); 505 // 如果当前有连接的设备,需要先断开 506 if (this.connected) { 507 await this.disConnect(); 508 } 509 if (!this.inited) { 510 await this.initBlue(); 511 } 512 // 停止扫描设备, 否则会浪费性能 513 await uni.stopBluetoothDevicesDiscovery({ 514 success(res) { 515 console.log("停止蓝牙设备搜索") 516 //console.log(res) 517 }, 518 fail(err) { 519 console.error(err); 520 521 } 522 }); 523 // 连接指定蓝牙设备 524 //console.log('开始连接蓝牙设备'); 525 this.connectId = item.deviceId; // MAC地址 526 this.connect(); 527 } 528 /*-----------------------------------------------------------------------*/ 529 530 /*-----------------------------------------------------------------------*/ 531 //API函数, 周期性调用的函数 532 periodicFunction() { 533 console.log('这个函数被周期性调用了!'); 534 // 在这里执行你的周期性任务 535 } 536 537 /*-----------------------------------------------------------------------*/ 538 //API函数,开始周期性调用 539 startPeriodicCall(callback, intervalms = 1000) { 540 if (!this.intervalId) { // 确保没有重复的定时器 541 this.intervalId = setInterval(callback, intervalms); // 每秒调用一次periodicFunction 542 } 543 } 544 /*-----------------------------------------------------------------------*/ 545 //API函数, 停止周期性调用 546 stopPeriodicCall() { 547 if (this.intervalId) { // 确保有定时器在运行 548 clearInterval(this.intervalId); // 停止周期性调用 549 this.intervalId = null; // 清空ID,以便下次可以重新开始 550 console.log('这个周期性函数被停止了!'); 551 } 552 } 553 /*-----------------------------------------------------------------------*/ 554 //API函数, 重新开始周期性调用 555 restartPeriodicCall(callback, intervalms = 1000) { 556 if (this.intervalId) { // 确保有定时器在运行 557 clearInterval(this.intervalId); // 停止周期性调用 558 this.intervalId = null; // 清空ID,以便下次可以重新开始 559 console.log('这个周期性函数被停止了!'); 560 }; 561 if (!this.intervalId) { // 确保没有重复的定时器 562 this.intervalId = setInterval(callback, intervalms); // 每秒调用一次periodicFunction 563 }; 564 } 565 /*-----------------------------------------------------------------------*/ 566 567 } 568 569 // 创建一个单例实例 570 const BLE_DeviceInstance = new BLE_DeviceClass(); 571 572 /*-----------------------------------------------------------------------*/ 573 // module.exports = { 574 // BLE_DeviceInstance, // 导出实例 ,作为全局实例 575 // } 576 export { 577 BLE_DeviceClass, 578 BLE_DeviceInstance 579 } 580 // import * as api from '@/util/api.js';// 导入公用变量与函数方法 581 // 在函数中引用 api.BLE_DeviceInstance.initBlue();
BLE搜索与连接 网页.vue

1 <template> 2 <view> 3 <!--功能按钮--> 4 <label class="flex-container"> 5 <button class="button_Green" 6 style="background-color: white;color: black;border-style: solid;border: 2px solid #4CAF50;">注意:开启手机蓝牙与定位功能 7 </button> 8 </label> 9 10 <!--设备列表--> 11 <view> 12 <div v-for="item in deviceArray" :key="item.deviceId"> 13 <view class="flex-container" 14 style="flex-direction: column; border: 2px solid #4CAF50; margin_left:2%; margin_top:2%;padding: 0rpm;"> 15 <label class="flex-container"> 16 <text class="flex-item" style="text-align: center;"> 蓝牙名称:{{item.name}}</text> 17 </label> 18 <label class="flex-container" style="align-items: left;"> 19 <view style="display:flex; text-align: left;flex:5">MAC:{{item.deviceId}}</view> 20 <view style="display:flex; text-align: left;flex:5">信号:{{item.RSSI}}</view> 21 22 <view class="button_Green" style="flex:5;"> 23 <a @click="selectlick(item)" v-if="connectId !== item.deviceId">连接</a> 24 <a @click="unselectlick(item)" v-else style="color: red;">断开连接</a> 25 </view> 26 27 </label> 28 </view> 29 </div> 30 </view> 31 32 <label class="flex-container"> 33 <button @click="startScanBLE" :disabled="!btn_select" class="button_Green" 34 style="background-color: #007aff;">搜索蓝牙外设 </button> 35 </label> 36 <label class="flex-container"> 37 <button @click="stopScanBLE" :disabled="btn_select" class="button_Green" 38 style="background-color: #4CAF50;">停止搜索蓝牙</button> 39 </label> 40 41 </view> 42 </template> 43 44 <script> 45 import * as ble_api from '@/utilities/ble_api.js'; // 导入公用变量与函数方法 46 47 export default { 48 data() { 49 return { 50 onscanBLE: true, 51 deviceArray: [], //蓝牙周围设备列表 52 connectId: "", // 连接的MAC 53 btn_select: true, // 控制按钮有效性 54 } 55 }, 56 onLoad() { 57 //console.log("test.vue"); 58 ble_api.BLE_DeviceInstance.setCheckAdapterStateChange(); 59 ble_api.BLE_DeviceInstance.setCheckConnectionStateChange(); 60 }, 61 onShow() { 62 // 监听页面显示 63 // if(ble_api.BLE_DeviceInstance.previousConnectId){ 64 // this.connectId = ""; 65 // } 66 console.log('页面显示'); 67 console.log(this.connectId); 68 }, 69 onHide() { 70 // 监听页面隐藏 71 console.log('页面隐藏'); 72 }, 73 methods: { 74 //启动手机蓝牙功能 75 openBLE() { 76 ble_api.BLE_DeviceInstance.initBlue(); 77 }, // end of openBLE() 78 //搜索蓝牙外设 79 startScanBLE() { 80 this.btn_select = false; 81 let _this = this; 82 _this.onscanBLE = true; 83 ble_api.BLE_DeviceInstance.scan_start(); 84 _this.getScanBLE(); 85 }, // end of startScanBLE() 86 //搜索蓝牙外设 87 stopScanBLE() { 88 this.btn_select = true; 89 let _this = this; 90 _this.onscanBLE = false; 91 ble_api.BLE_DeviceInstance.scan_stop(); 92 }, // end of stopScanBLE() 93 //定时获取 搜索蓝牙外设 的结果 94 async getScanBLE() { 95 let _this = this; 96 // 使用Promise包装setTimeout 97 while (_this.onscanBLE) { 98 //console.log('发一个数据包,开始延时...'); 99 await new Promise(resolve => { 100 setTimeout(() => { 101 // 检测已发现的蓝牙外设列表,并根据两次搜索数量判断是否停止 102 let len_previous = _this.deviceArray.length; 103 _this.deviceArray.length = 0; 104 let len = ble_api.BLE_DeviceInstance.devices.length; 105 for (var i = 0; i < len; i++) { 106 if (ble_api.BLE_DeviceInstance.devices[i].name) { 107 _this.deviceArray.push(ble_api.BLE_DeviceInstance.devices[i]); 108 } 109 } 110 console.log("设备列表:"); 111 console.log(_this.deviceArray); 112 // 比较相邻两次搜索结果,判断是否停止搜索蓝牙。 113 len = _this.deviceArray.length; 114 if (len_previous == len) //没有收到新外设 115 { 116 117 _this.stopScanBLE(); // 含有 _this.onscanBLE = false; 118 } 119 //console.log('延时100ms后执行(使用await)!'); 120 resolve(); // 调用resolve来继续执行async函数 121 }, 2000); 122 }); 123 // 这段代码将在延时后执行 124 }; 125 console.log('关闭定时器(搜索蓝牙外设)'); 126 }, // end of async getScanBLE() 127 //选择与连接蓝牙设备 128 selectlick(item) { 129 this.connectId = ""; 130 console.log(item.deviceId); 131 this.connectId = item.deviceId; 132 133 //断开蓝牙连接,连接新蓝牙 134 ble_api.BLE_DeviceInstance.connectClick(item); 135 }, // end of selectlick(item) 136 //断开蓝牙设备 137 unselectlick(item) { 138 this.connectId = ""; 139 140 //断开蓝牙连接 141 ble_api.BLE_DeviceInstance.disConnect(); 142 }, // end of unselectlick(item) 143 // 144 } 145 } 146 </script> 147 148 <style> 149 .flex-container { 150 display: flex; 151 flex-direction: row; 152 /* 主轴方向,默认为 row */ 153 flex-wrap: nowrap; 154 /* 是否换行,默认为 nowrap */ 155 156 justify-content: center; 157 /* 如果需要的话,可以水平居中 */ 158 align-items: center; 159 /* 如果需要的话,可以垂直居中 */ 160 box-sizing: border-box; 161 width: 100%; 162 /* 或其他你需要的宽度 width: 96%;*/ 163 164 /* 或其他你需要的高度 height: 100%;*/ 165 /* 其他样式... */ 166 } 167 168 .flex-item { 169 flex: 1; 170 /* 使按钮占据全部可用空间 */ 171 /* 如果需要的话,可以添加边距或填充 */ 172 /* margin: ...; */ 173 /* padding: ...; */ 174 /* 其他样式... */ 175 text-align: center; 176 text-decoration: none; 177 display: inline-block; 178 font-size: 50rpx; 179 } 180 181 .button_Green { 182 flex: 1; 183 /* Green */ 184 background-color: #4CAF50; 185 /* Green */ 186 border: none; 187 color: white; 188 /*padding: 15px 32px; */ 189 text-align: center; 190 text-decoration: none; 191 /*display: inline-block;*/ 192 font-size: 40rpx; 193 194 border-radius:20rpx; 195 /* 或者使用 50% 来确保它总是圆形的 */ 196 } 197 </style>
BLE数据发送与接收举例 网页.vue

1 <template> 2 <view> 3 <!--设备列表--> 4 <div class="flex-container" style="flex-direction: column;" v-for="item in deviceArray" :key="item.deviceId"> 5 <view class="flex-container" 6 style="flex-direction: column; border: 2px solid #4CAF50; margin_left:2%; margin_top:2%;padding: 0rpm;"> 7 <label class="flex-container"> 8 <text class="flex-item" style="text-align: center;">设备编号:{{item.deviceId+1}}</text> 9 </label> 10 <label class="flex-container"> 11 <text class="flex-item" style="text-align: left;">安装位置:{{item.locate}}</text> 12 <text class="flex-item" style="text-align: left;">运动状态:{{item.state}}</text> 13 14 </label> 15 <label class="flex-container"> 16 <button @click="btnForwardClick(item)" class="button_Green" style="background-color: #4CAF50;" 17 :disabled="!item.btn_forward">前进</button> 18 <button @click="btnBackwardClick(item)" class="button_Green" style="background-color: #008CBA;" 19 :disabled="!item.btn_backward">返回</button> 20 <button @click="btnStopClick(item)" class="button_Green" style="background-color: #f44336;" 21 :disabled="!item.btn_stop">停止</button> 22 </label> 23 </view> 24 </div> 25 <!--设备列表--> 26 </view> 27 </template> 28 29 <script> 30 import * as ble_api from '@/utilities/ble_api.js'; // 导入公用变量与函数方法 31 export default { 32 data() { 33 return { 34 35 deviceRegs: { // 编号:0bit,device0;bit1,device1;...bit31,device31; 36 existence: 0, 37 forward_state: 0, 38 backward_state: 0, 39 origin_pos: 0, //驻泊位状态 有,或无 40 end_pos: 0, //暂停位状态 有,或无 41 deviceId_cmd: 0, //接收命令的RS485设备 42 forward_cmd: 0, 43 backward_cmd: 0, 44 }, 45 deviceArray: [], 46 // item: { 47 // deviceId: 0, 48 // locate: "设备NO.", 49 // state: "", 50 // btn_forward: true, 51 // btn_backward: true, 52 // btn_stop: false, 53 // }, 54 } // end of return 55 }, 56 onLoad() { 57 // BLE 读取 RS485设备状态 58 this.button_disable = true; 59 this.deviceRegs.existence = 15 + 15 * 16; 60 this.deviceRegs.backward_state = 5; 61 this.deviceRegs.forward_state = 10; 62 this.deviceRegs.origion_pos = 5 * 16; 63 this.deviceRegs.end_pos = 10 * 16; 64 console.log(this.deviceRegs); 65 66 this.setDeviceArray(); 67 }, // end of onLoad() 68 onShow() { 69 // 监听页面显示 70 // 必须在此添加一次。否者不能接收数据。回调函数只有一个。 71 ble_api.BLE_DeviceInstance.notify(ble_api.BLE_DeviceInstance.defaultCallback); 72 //**别的页面 监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数。 73 // uni.$on('BLE_RX_Value', function(data) { 74 // console.log('监听到事件来自 update ,携带参数 msg 为:' + data.msg); 75 // }); 76 uni.$on('BLE_RX_Value', this.BLE_RX_ValueFun); 77 78 //此处应延时读取设备状态 79 }, 80 onHide() { 81 // 监听页面隐藏 82 uni.$off('BLE_RX_Value', this.BLE_RX_ValueFun); //移除全局自定义事件监听器。 83 84 console.log('页面隐藏'); 85 }, 86 methods: { 87 //回调函数 。uni.$on()监听全局的自定义事件。事件可以由 uni.$emit 触发,回调函数会接收所有传入事件触发函数的额外参数。 88 BLE_RX_ValueFun(data) { 89 console.log('监听到事件来自 BLE_RX_Value ,携带参数 msg 为:' + data.msg); 90 let jsonString = data.msg; 91 this.BLE_RX_DataRead(jsonString); //定义函数,完成对ESP32发来的数据处理。 92 }, 93 //定义函数,完成对ESP32发来的数据处理。 94 BLE_RX_DataRead(jsonString) { 95 let _this = this; 96 let Keys = []; 97 // 解析 JSON string 98 let JsonObj = JSON.parse(jsonString); 99 //取得键值 100 let obj1 = _this.deviceRegs; 101 for (let key in obj1) { // 遍历obj1={}的所有键 102 Keys.push(key); 103 }; 104 //根据蓝牙发来的JsonObj,更新this.deviceRegs 105 for (let key in JsonObj) { // 遍历JsonObj ={}的所有键 106 if (Keys.includes(key)) { // 更新 JsonObj{}内的值 107 obj1[key] = JsonObj[key]; 108 //console.log(key); 109 } 110 }; 111 }, 112 113 // 机APP通过蓝牙通信发送数据给外围蓝牙设备ESP32 114 // 报文功能是向设备写数据。 115 BLE_write_device(jsonObject) { 116 let _this = this; 117 //let ble_all_object = ble_api.BLE_DeviceInstance.rxJSONobject; // 用于存储JSON object数据 118 jsonObject["command"] = "write_device"; 119 //API函数, 向外围蓝牙设备发送数据,指定服务及其特性的UUID 120 ble_api.BLE_DeviceInstance.send(jsonObject) 121 console.log("BLE_write_device(jsonObject)"); 122 }, // end of BLE_write_device(jsonObject) 123 // 报文功能是读取设备状态。 124 BLE_read_device(jsonObject) { 125 let _this = this; 126 //let ble_all_object = ble_api.BLE_DeviceInstance.rxJSONobject; // 用于存储JSON object数据 127 jsonObject["command"] = "read_device"; 128 //API函数, 向外围蓝牙设备发送数据,指定服务及其特性的UUID 129 ble_api.BLE_DeviceInstance.send(jsonObject) 130 console.log("BLE_write_device(jsonObject)"); 131 }, // end of BLE_read_device(jsonObject) 132 /*-----------------------------------------------------------------------*/ 133 /*<!-------------------------------------------------------------------------------------------->*/ 134 /*根据this.deviceRegs 设置 this.deviceArray*/ 135 setDeviceArray() { 136 let _this = this; 137 let existflag = _this.deviceRegs.existence; 138 139 this.deviceArray.length = 0; //清空数组 140 //arr.splice(0, arr.length); 141 142 for (var i = 0; i < 16; i++) { //最多16个RS85设备 143 let bit = 0x0001; 144 let bit_flag = bit << i; 145 if ((bit_flag & existflag) != bit_flag) { 146 continue; // 无RS85设备 147 }; 148 //有编号i的RS485设备 149 let item = { 150 deviceId: i, 151 locate: "RS485设备安装位置", 152 state: "", 153 btn_forward: false, 154 btn_backward: false, 155 btn_stop: false, 156 }; 157 // RS485运转时,只能停 158 let _forward = ((_this.deviceRegs.forward_state & bit_flag) == bit_flag); 159 let _backward = ((_this.deviceRegs.backward_state & bit_flag) == bit_flag); 160 161 // 使能 停止按钮 162 if (_forward || _backward) { 163 item.btn_forward = false; 164 item.btn_backward = false; 165 item.btn_stop = true; 166 if (_forward) { 167 item.state = "前进中"; 168 } else { 169 item.state = "返回中"; 170 } 171 _this.deviceArray.push(item); 172 continue; 173 } 174 // RS485在端部时,只能向另外一端运动 175 // 使能 前进按钮 176 let at_origion = ((_this.deviceRegs.origion_pos & bit_flag) == bit_flag); 177 if (at_origion) { 178 item.btn_forward = true; 179 item.btn_backward = false; 180 item.btn_stop = false; 181 182 item.state = "驻停端"; 183 _this.deviceArray.push(item); 184 continue; 185 } 186 // 使能 返回按钮 187 let at_end = ((_this.deviceRegs.end_pos & bit_flag) == bit_flag); 188 if (at_end) { 189 item.btn_forward = false; 190 item.btn_backward = true; 191 item.btn_stop = false; 192 193 item.state = "暂停端"; 194 _this.deviceArray.push(item); 195 continue; 196 } 197 // 处于中部,且停止,使能 前进 和 返回 198 item.btn_forward = true; 199 item.btn_backward = true; 200 item.btn_stop = false; 201 item.state = "停在中间段"; 202 _this.deviceArray.push(item); 203 } 204 console.log(_this.deviceArray); 205 }, // end of setDeviceArray() 206 // 点击某个设备的连接 207 // 208 btnForwardClick(item) { 209 console.log(item.deviceId); 210 let pos = item.deviceId; 211 let bit = 0x0001 << pos; 212 let flag = this.deviceRegs.existence; 213 if ((flag & bit) != bit) { //无设备 214 return; 215 }; 216 this.deviceRegs.deviceId_cmd = bit; 217 this.deviceRegs.backward_cmd = 0; 218 this.deviceRegs.forward_cmd = bit; 219 220 // BLE 发送命令给ESP32 221 let jsonObject = { 222 deviceId_cmd: this.deviceRegs.deviceId_cmd, //接收命令的RS485设备 223 forward_cmd: this.deviceRegs.forward_cmd, 224 backward_cmd: this.deviceRegs.backward_cmd, 225 }; 226 jsonObject["command"] = "write_device"; 227 228 this.BLE_write_device(jsonObject); 229 }, // end of btnForwardClick(item) 230 231 btnBackwardClick(item) { 232 console.log(item.deviceId); 233 let pos = item.deviceId; 234 let bit = 0x0001 << pos; 235 let flag = this.deviceRegs.existence; 236 if ((flag & bit) != bit) { //无设备 237 return; 238 }; 239 this.deviceRegs.deviceId_cmd = bit; 240 this.deviceRegs.backward_cmd = bit; 241 this.deviceRegs.forward_cmd = 0; 242 243 // BLE 发送命令给ESP32 244 let jsonObject = { 245 deviceId_cmd: this.deviceRegs.deviceId_cmd, //接收命令的RS485设备 246 forward_cmd: this.deviceRegs.forward_cmd, 247 backward_cmd: this.deviceRegs.backward_cmd, 248 }; 249 jsonObject["command"] = "write_device"; 250 251 this.BLE_write_device(jsonObject); 252 }, // end of btnBackwardClick(item) 253 254 btnStopClick(item) { 255 console.log(item.deviceId); 256 let pos = item.deviceId; 257 let bit = 0x0001 << pos; 258 let flag = this.deviceRegs.existence; 259 if ((flag & bit) != bit) { //无设备 260 return; 261 }; 262 this.deviceRegs.deviceId_cmd = bit; 263 this.deviceRegs.backward_cmd = 0; 264 this.deviceRegs.forward_cmd = 0; 265 266 // BLE 发送命令给ESP32 267 let jsonObject = { 268 deviceId_cmd: this.deviceRegs.deviceId_cmd, //接收命令的RS485设备 269 forward_cmd: this.deviceRegs.forward_cmd, 270 backward_cmd: this.deviceRegs.backward_cmd, 271 }; 272 jsonObject["command"] = "write_device"; 273 274 this.BLE_write_device(jsonObject); 275 }, // end of btnStopClick(item) 276 } 277 } 278 </script> 279 280 281 <style> 282 .flex-container { 283 display: flex; 284 flex-direction: row; 285 /* 主轴方向,默认为 row */ 286 flex-wrap: nowrap; 287 /* 是否换行,默认为 nowrap */ 288 289 justify-content: center; 290 /* 如果需要的话,可以水平居中 */ 291 align-items: center; 292 /* 如果需要的话,可以垂直居中 */ 293 width: 100%; 294 box-sizing: border-box; 295 296 /* 或其他你需要的宽度 width: 96%;*/ 297 298 /* 或其他你需要的高度 height: 100%;*/ 299 /* 其他样式... */ 300 } 301 302 .flex-item { 303 flex: 1; 304 /* 使按钮占据全部可用空间 */ 305 /* 如果需要的话,可以添加边距或填充 */ 306 /* margin: ...; */ 307 /* padding: ...; */ 308 /* 其他样式... */ 309 text-align: center; 310 text-decoration: none; 311 display: inline-block; 312 font-size: 16px; 313 } 314 315 .button_Green { 316 flex: 1; 317 /* Green */ 318 background-color: #4CAF50; 319 /* Green */ 320 border: none; 321 color: white; 322 /*padding: 15px 32px; */ 323 text-align: center; 324 text-decoration: none; 325 /*display: inline-block;*/ 326 font-size: 16px; 327 } 328 </style>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2023-07-02 Tkinter Menu菜单 (disable 菜单命令)