# BluFi蓝牙配网小程序开发需求说明书 ## 1. 项目概述 ### 1.1 项目背景 本项目需要开发一个微信小程序,用于与ESP32设备进行BluFi蓝牙配网。该小程序需要完全兼容ESP官方的espblufi应用程序功能,能够成功进行WiFi配网并返回配网成功报告。 ### 1.2 项目目标 - 开发微信小程序,实现BluFi蓝牙配网功能 - 与ESP32设备建立稳定的蓝牙连接 - 完成WiFi网络配置和连接验证 - 提供用户友好的配网界面和状态反馈 - 确保与官方espblufi应用程序的完全兼容性 ### 1.3 设备端配置信息 基于项目代码分析,设备端配置如下: ```javascript // 设备端配置参数(来自bluetooth_provisioning_config.h) const DEVICE_CONFIG = { // 设备名称配置 DEFAULT_DEVICE_NAME: "Airhub_Ble", MAX_DEVICE_NAME_LEN: 32, // 超时配置 ADV_TIMEOUT_MS: 0, // 永不超时 CLIENT_TIMEOUT_MS: 5 * 60 * 1000, // 5分钟 WIFI_TIMEOUT_MS: 100 * 1000, // 100秒 WIFI_MAX_RETRY: 5, // 安全配置 SECURITY_ENABLED: false, REQUIRE_PAIRING: false, PSK: "Airhub2025", // 功能开关 ENABLE_WIFI_SCAN: true, AUTO_REPORT_STATUS: true, AUTO_STOP_ON_SUCCESS: true, AUTO_STOP_DELAY_MS: 5000 }; ``` ## 2. 技术架构 ### 2.1 系统架构图 ``` 微信小程序 <---> 蓝牙BLE <---> ESP32设备 <---> WiFi网络 ↓ ↓ ↓ 用户界面 BluFi协议 WiFi连接 状态管理 数据加密 网络验证 ``` ### 2.2 设备端架构 基于`bluetooth_provisioning.h`和`bluetooth_provisioning.cc`分析: ```javascript // 设备端状态枚举(对应C++代码) const BluetoothProvisioningState = { IDLE: 0, // 空闲状态,未启动配网 INITIALIZING: 1, // 初始化中,正在初始化蓝牙和BluFi服务 ADVERTISING: 2, // 广播中,等待手机客户端连接 CONNECTED: 3, // 已连接,手机客户端已连接到设备 PROVISIONING: 4, // 配网中,正在接收和处理WiFi凭据 SUCCESS: 5, // 配网成功,WiFi连接建立成功 FAILED: 6, // 配网失败,WiFi连接失败或其他错误 STOPPED: 7 // 已停止,配网服务已停止 }; // 设备端事件类型(对应C++代码) const BluetoothProvisioningEvent = { STATE_CHANGED: 0, // 状态改变事件,配网状态发生变化 WIFI_CREDENTIALS: 1, // 收到WiFi凭据事件,从手机接收到WiFi信息 WIFI_CONNECTED: 2, // WiFi连接成功事件,设备成功连接到WiFi网络 WIFI_FAILED: 3, // WiFi连接失败事件,设备连接WiFi失败 CLIENT_CONNECTED: 4, // 客户端连接事件,手机客户端连接到设备 CLIENT_DISCONNECTED: 5 // 客户端断开事件,手机客户端断开连接 }; ``` ### 2.3 技术栈 - **前端**: 微信小程序框架 - **通讯协议**: BluFi (基于BLE) - **设备端**: ESP-IDF BluFi组件 - **加密**: 可选AES加密(当前项目未启用) ## 3. BluFi协议详解 ### 3.1 协议概述 BluFi是乐鑫开发的基于蓝牙通道的WiFi网络配置协议,通过安全的蓝牙连接传输WiFi凭据。 ### 3.2 GATT服务和特征值 #### 3.2.1 BluFi服务UUID(ESP32标准) ```javascript // BluFi GATT服务和特征值UUID const BLUFI_SERVICE_UUID = "0000FFFF-0000-1000-8000-00805F9B34FB"; const BLUFI_CHAR_P2E_UUID = "0000FF01-0000-1000-8000-00805F9B34FB"; // 手机到设备(写) const BLUFI_CHAR_E2P_UUID = "0000FF02-0000-1000-8000-00805F9B34FB"; // 设备到手机(通知) ``` #### 3.2.2 设备发现和命名规则 ```javascript // 设备名称识别(基于项目配置) function isValidBluFiDevice(device) { // 检查设备名称是否符合项目规范 const validNames = [ "Airhub_Ble", // 默认名称 "XiaoZhi-AI" // 备用名称 ]; return device.name && ( validNames.includes(device.name) || device.name.startsWith("Airhub-") || device.name.startsWith("XiaoZhi-") ); } ``` ### 3.3 数据包格式 #### 3.3.1 BluFi数据包结构 ```javascript // BluFi数据包格式(基于ESP-IDF实现) class BluFiPacket { constructor() { this.type = 0x00; // 数据包类型 (1字节) this.fc = 0x00; // 帧控制 (1字节) this.sequence = 0x0000; // 序列号 (2字节) this.length = 0x0000; // 数据长度 (2字节) this.data = []; // 数据内容 (变长) this.checksum = 0x0000; // 校验和 (2字节) } // 构建数据包 build(type, subtype, data = null) { const dataLength = data ? data.length : 0; const totalLength = 8 + dataLength; const buffer = new ArrayBuffer(totalLength); const view = new DataView(buffer); // 设置包头 view.setUint8(0, type); // 类型 view.setUint8(1, subtype); // 子类型 view.setUint16(2, this.sequence, true); // 序列号(小端) view.setUint16(4, dataLength, true); // 数据长度(小端) // 设置数据 if (data && dataLength > 0) { const dataView = new Uint8Array(buffer, 6); dataView.set(new Uint8Array(data)); } // 计算并设置校验和 const checksum = this.calculateChecksum(buffer, totalLength - 2); view.setUint16(totalLength - 2, checksum, true); this.sequence++; return buffer; } // 计算校验和 calculateChecksum(buffer, length) { let checksum = 0; const view = new Uint8Array(buffer); for (let i = 0; i < length; i++) { checksum += view[i]; } return checksum & 0xFFFF; } // 解析数据包 parse(buffer) { const view = new DataView(buffer); return { type: view.getUint8(0), subtype: view.getUint8(1), sequence: view.getUint16(2, true), length: view.getUint16(4, true), data: buffer.slice(6, 6 + view.getUint16(4, true)), checksum: view.getUint16(buffer.byteLength - 2, true) }; } } ``` #### 3.3.2 数据包类型定义 ```javascript // 控制包类型(基于ESP-IDF BluFi实现) const BLUFI_TYPE_CTRL = { ACK: 0x00, // 确认包 SET_SEC_MODE: 0x01, // 设置安全模式 SET_WIFI_OPMODE: 0x02, // 设置WiFi操作模式 CONNECT_WIFI: 0x03, // 连接WiFi DISCONNECT_WIFI: 0x04, // 断开WiFi GET_WIFI_STATUS: 0x05, // 获取WiFi状态 DEAUTHENTICATE: 0x06, // 取消认证 GET_VERSION: 0x07, // 获取版本 CLOSE_CONNECTION: 0x08, // 关闭连接 GET_WIFI_LIST: 0x09 // 获取WiFi列表 }; // 数据包类型(基于ESP-IDF BluFi实现) const BLUFI_TYPE_DATA = { NEG: 0x00, // 协商数据 STA_BSSID: 0x01, // STA BSSID STA_SSID: 0x02, // STA SSID STA_PASSWD: 0x03, // STA 密码 SOFTAP_SSID: 0x04, // SoftAP SSID SOFTAP_PASSWD: 0x05, // SoftAP 密码 SOFTAP_MAX_CONN: 0x06, // SoftAP最大连接数 SOFTAP_AUTH_MODE: 0x07, // SoftAP认证模式 SOFTAP_CHANNEL: 0x08, // SoftAP信道 USERNAME: 0x09, // 用户名 CA_CERT: 0x0A, // CA证书 CLIENT_CERT: 0x0B, // 客户端证书 SERVER_CERT: 0x0C, // 服务器证书 CLIENT_PRIV_KEY: 0x0D, // 客户端私钥 SERVER_PRIV_KEY: 0x0E, // 服务器私钥 WIFI_REP: 0x0F, // WiFi报告 WIFI_LIST: 0x10 // WiFi列表 }; // 包类型标识 const BLUFI_FC_ENC = 0x01; // 加密标志 const BLUFI_FC_CHECK = 0x02; // 校验标志 const BLUFI_FC_DATA_DIR = 0x04; // 数据方向标志 const BLUFI_FC_REQUIRE_ACK = 0x08; // 需要确认标志 ``` ## 4. 配网流程详细实现 ### 4.1 第一阶段:蓝牙初始化和设备扫描 #### 4.1.1 小程序端实现 ```javascript // 蓝牙配网管理类 class BluFiProvisioning { constructor() { this.deviceId = null; this.serviceId = null; this.writeCharacteristicId = null; this.notifyCharacteristicId = null; this.sequenceNumber = 0; this.isConnected = false; this.provisioningState = 'idle'; this.packet = new BluFiPacket(); } // 初始化蓝牙适配器 async initBluetooth() { try { console.log('初始化蓝牙适配器...'); await new Promise((resolve, reject) => { wx.openBluetoothAdapter({ success: (res) => { console.log('蓝牙适配器初始化成功:', res); resolve(res); }, fail: (err) => { console.error('蓝牙适配器初始化失败:', err); reject(new Error(`蓝牙初始化失败: ${err.errMsg}`)); } }); }); // 检查蓝牙状态 await this.checkBluetoothState(); return true; } catch (error) { console.error('蓝牙初始化异常:', error); throw error; } } // 检查蓝牙状态 async checkBluetoothState() { return new Promise((resolve, reject) => { wx.getBluetoothAdapterState({ success: (res) => { console.log('蓝牙状态:', res); if (!res.available) { reject(new Error('蓝牙不可用')); } else if (!res.discovering) { console.log('蓝牙可用,准备扫描设备'); resolve(res); } else { resolve(res); } }, fail: (err) => { reject(new Error(`获取蓝牙状态失败: ${err.errMsg}`)); } }); }); } // 扫描BluFi设备 async startScan(onDeviceFound) { try { console.log('开始扫描BluFi设备...'); // 监听设备发现事件 wx.onBluetoothDeviceFound((res) => { res.devices.forEach(device => { console.log('发现设备:', device); // 检查是否为BluFi设备 if (this.isValidBluFiDevice(device)) { console.log('发现BluFi设备:', device.name, device.deviceId); onDeviceFound && onDeviceFound(device); } }); }); // 开始扫描 await new Promise((resolve, reject) => { wx.startBluetoothDevicesDiscovery({ services: [BLUFI_SERVICE_UUID], allowDuplicatesKey: false, interval: 0, success: (res) => { console.log('开始扫描设备成功:', res); resolve(res); }, fail: (err) => { console.error('开始扫描设备失败:', err); reject(new Error(`扫描失败: ${err.errMsg}`)); } }); }); return true; } catch (error) { console.error('扫描设备异常:', error); throw error; } } // 停止扫描 async stopScan() { return new Promise((resolve) => { wx.stopBluetoothDevicesDiscovery({ success: (res) => { console.log('停止扫描成功:', res); resolve(res); }, fail: (err) => { console.warn('停止扫描失败:', err); resolve(); // 即使失败也继续 } }); }); } // 验证是否为有效的BluFi设备 isValidBluFiDevice(device) { if (!device.name) return false; const validNames = [ "Airhub_Ble", // 项目默认名称 "XiaoZhi-AI" // 备用名称 ]; return validNames.includes(device.name) || device.name.startsWith("Airhub-") || device.name.startsWith("XiaoZhi-"); } } ``` #### 4.1.2 设备扫描页面实现 ```xml 扫描BluFi设备 请确保设备处于配网模式 正在扫描设备... {{item.name}} {{item.deviceId}} 信号强度: {{item.RSSI}}dBm 连接 未发现设备,请检查设备是否开启配网模式 ``` ```javascript // pages/scan/scan.js Page({ data: { scanning: false, devices: [] }, onLoad() { this.blufi = new BluFiProvisioning(); this.initBluetooth(); }, async initBluetooth() { try { await this.blufi.initBluetooth(); console.log('蓝牙初始化完成'); } catch (error) { wx.showToast({ title: '蓝牙初始化失败', icon: 'error' }); console.error('蓝牙初始化失败:', error); } }, async startScan() { if (this.data.scanning) return; this.setData({ scanning: true, devices: [] }); try { await this.blufi.startScan((device) => { // 检查设备是否已存在 const exists = this.data.devices.find(d => d.deviceId === device.deviceId); if (!exists) { this.setData({ devices: [...this.data.devices, device] }); } }); // 30秒后自动停止扫描 setTimeout(() => { this.stopScan(); }, 30000); } catch (error) { this.setData({ scanning: false }); wx.showToast({ title: '扫描失败', icon: 'error' }); console.error('扫描失败:', error); } }, async stopScan() { if (!this.data.scanning) return; try { await this.blufi.stopScan(); this.setData({ scanning: false }); } catch (error) { console.error('停止扫描失败:', error); } }, selectDevice(e) { const device = e.currentTarget.dataset.device; console.log('选择设备:', device); // 停止扫描 this.stopScan(); // 跳转到连接页面 wx.navigateTo({ url: `/pages/connect/connect?deviceId=${device.deviceId}&deviceName=${device.name}` }); }, onUnload() { this.stopScan(); } }); ``` ### 4.2 第二阶段:设备连接和GATT服务发现 #### 4.2.1 连接设备实现 ```javascript // 在BluFiProvisioning类中添加连接方法 class BluFiProvisioning { // ... 前面的代码 ... // 连接设备 async connectDevice(deviceId) { try { console.log('连接设备:', deviceId); this.deviceId = deviceId; // 建立BLE连接 await new Promise((resolve, reject) => { wx.createBLEConnection({ deviceId: deviceId, success: (res) => { console.log('设备连接成功:', res); this.isConnected = true; resolve(res); }, fail: (err) => { console.error('设备连接失败:', err); reject(new Error(`连接失败: ${err.errMsg}`)); } }); }); // 发现服务 await this.discoverServices(); // 发现特征值 await this.discoverCharacteristics(); // 启用通知 await this.enableNotifications(); console.log('设备连接和初始化完成'); return true; } catch (error) { console.error('连接设备异常:', error); this.isConnected = false; throw error; } } // 自动发现GATT服务 async discoverServices() { return new Promise((resolve, reject) => { wx.getBLEDeviceServices({ deviceId: this.deviceId, success: (res) => { console.log('发现服务:', res.services); // 查找BluFi服务 const blufiService = res.services.find(service => service.uuid.toUpperCase() === BLUFI_SERVICE_UUID.toUpperCase() ); if (blufiService) { this.serviceId = blufiService.uuid; console.log('找到BluFi服务:', this.serviceId); resolve(blufiService); } else { reject(new Error('未找到BluFi服务')); } }, fail: (err) => { console.error('发现服务失败:', err); reject(new Error(`发现服务失败: ${err.errMsg}`)); } }); }); } // 自动发现特征值 async discoverCharacteristics() { return new Promise((resolve, reject) => { wx.getBLEDeviceCharacteristics({ deviceId: this.deviceId, serviceId: this.serviceId, success: (res) => { console.log('发现特征值:', res.characteristics); // 查找写特征值(手机到设备) const writeChar = res.characteristics.find(char => char.uuid.toUpperCase() === BLUFI_CHAR_P2E_UUID.toUpperCase() ); // 查找通知特征值(设备到手机) const notifyChar = res.characteristics.find(char => char.uuid.toUpperCase() === BLUFI_CHAR_E2P_UUID.toUpperCase() ); if (writeChar && notifyChar) { this.writeCharacteristicId = writeChar.uuid; this.notifyCharacteristicId = notifyChar.uuid; console.log('找到BluFi特征值:'); console.log('写特征值:', this.writeCharacteristicId); console.log('通知特征值:', this.notifyCharacteristicId); resolve({ writeChar, notifyChar }); } else { reject(new Error('未找到BluFi特征值')); } }, fail: (err) => { console.error('发现特征值失败:', err); reject(new Error(`发现特征值失败: ${err.errMsg}`)); } }); }); } // 启用通知 async enableNotifications() { return new Promise((resolve, reject) => { // 监听特征值变化 wx.onBLECharacteristicValueChange((res) => { console.log('收到设备数据:', res); this.handleDeviceData(res.value); }); // 启用通知 wx.notifyBLECharacteristicValueChange({ deviceId: this.deviceId, serviceId: this.serviceId, characteristicId: this.notifyCharacteristicId, state: true, success: (res) => { console.log('启用通知成功:', res); resolve(res); }, fail: (err) => { console.error('启用通知失败:', err); reject(new Error(`启用通知失败: ${err.errMsg}`)); } }); }); } // 处理设备数据 handleDeviceData(buffer) { try { const packet = this.packet.parse(buffer); console.log('解析数据包:', packet); // 根据数据包类型处理 switch (packet.type) { case 0x00: // 控制包 this.handleControlPacket(packet); break; case 0x01: // 数据包 this.handleDataPacket(packet); break; default: console.warn('未知数据包类型:', packet.type); } } catch (error) { console.error('处理设备数据失败:', error); } } // 处理控制包 handleControlPacket(packet) { switch (packet.subtype) { case BLUFI_TYPE_CTRL.ACK: console.log('收到确认包'); break; case BLUFI_TYPE_CTRL.GET_WIFI_STATUS: console.log('设备请求WiFi状态'); break; default: console.log('收到控制包:', packet.subtype); } } // 处理数据包 handleDataPacket(packet) { switch (packet.subtype) { case BLUFI_TYPE_DATA.WIFI_REP: this.handleWiFiReport(packet.data); break; case BLUFI_TYPE_DATA.WIFI_LIST: this.handleWiFiList(packet.data); break; default: console.log('收到数据包:', packet.subtype); } } // 处理WiFi连接报告 handleWiFiReport(data) { if (data.byteLength >= 2) { const view = new DataView(data); const status = view.getUint8(0); const reason = view.getUint8(1); console.log('WiFi连接报告 - 状态:', status, '原因:', reason); if (status === 0) { // 连接成功 this.provisioningState = 'success'; this.onProvisioningSuccess && this.onProvisioningSuccess(); } else { // 连接失败 this.provisioningState = 'failed'; this.onProvisioningFailed && this.onProvisioningFailed(reason); } } } // 断开连接 async disconnect() { if (!this.isConnected || !this.deviceId) return; try { await new Promise((resolve) => { wx.closeBLEConnection({ deviceId: this.deviceId, success: (res) => { console.log('断开连接成功:', res); resolve(res); }, fail: (err) => { console.warn('断开连接失败:', err); resolve(); // 即使失败也继续 } }); }); this.isConnected = false; this.deviceId = null; this.serviceId = null; this.writeCharacteristicId = null; this.notifyCharacteristicId = null; } catch (error) { console.error('断开连接异常:', error); } } } ``` #### 4.2.2 连接页面实现 ```xml 连接设备 {{deviceName}} {{deviceId}} 正在连接设备... 设备连接成功 设备未连接 1 建立BLE连接 2 发现GATT服务 3 初始化特征值 4 启用数据通知 ``` ```javascript // pages/connect/connect.js Page({ data: { deviceId: '', deviceName: '', connecting: false, connected: false, step: 0 }, onLoad(options) { this.setData({ deviceId: options.deviceId, deviceName: options.deviceName }); this.blufi = new BluFiProvisioning(); this.connectDevice(); }, async connectDevice() { if (this.data.connecting) return; this.setData({ connecting: true, connected: false, step: 0 }); try { // 步骤1:建立BLE连接 this.setData({ step: 1 }); await new Promise(resolve => setTimeout(resolve, 500)); // 显示进度 // 步骤2:发现GATT服务 this.setData({ step: 2 }); await new Promise(resolve => setTimeout(resolve, 500)); // 步骤3:初始化特征值 this.setData({ step: 3 }); await new Promise(resolve => setTimeout(resolve, 500)); // 步骤4:启用数据通知 this.setData({ step: 4 }); // 执行实际连接 await this.blufi.connectDevice(this.data.deviceId); this.setData({ connecting: false, connected: true }); wx.showToast({ title: '连接成功', icon: 'success' }); } catch (error) { this.setData({ connecting: false, connected: false, step: 0 }); wx.showToast({ title: '连接失败', icon: 'error' }); console.error('连接设备失败:', error); } }, goToConfig() { if (!this.data.connected) return; // 跳转到WiFi配置页面 wx.navigateTo({ url: '/pages/config/config' }); }, onUnload() { // 页面卸载时断开连接 if (this.blufi) { this.blufi.disconnect(); } } }); ``` ### 4.3 第三阶段:WiFi扫描和网络选择 #### 4.3.1 WiFi扫描实现 ```javascript // 在BluFiProvisioning类中添加WiFi扫描方法 class BluFiProvisioning { // ... 前面的代码 ... // 请求WiFi扫描 async requestWiFiScan() { try { console.log('请求设备扫描WiFi...'); // 构建获取WiFi列表的控制包 const packet = this.packet.build(0x00, BLUFI_TYPE_CTRL.GET_WIFI_LIST); // 发送数据包 await this.sendData(packet); console.log('WiFi扫描请求已发送'); return true; } catch (error) { console.error('请求WiFi扫描失败:', error); throw error; } } // 发送数据到设备 async sendData(buffer) { if (!this.isConnected || !this.deviceId || !this.writeCharacteristicId) { throw new Error('设备未连接或特征值未初始化'); } return new Promise((resolve, reject) => { wx.writeBLECharacteristicValue({ deviceId: this.deviceId, serviceId: this.serviceId, characteristicId: this.writeCharacteristicId, value: buffer, success: (res) => { console.log('数据发送成功:', res); resolve(res); }, fail: (err) => { console.error('数据发送失败:', err); reject(new Error(`发送失败: ${err.errMsg}`)); } }); }); } // 处理WiFi列表 handleWiFiList(data) { try { console.log('收到WiFi列表数据:', data); // 解析WiFi列表数据 const wifiList = this.parseWiFiList(data); console.log('解析的WiFi列表:', wifiList); // 触发回调 this.onWiFiListReceived && this.onWiFiListReceived(wifiList); } catch (error) { console.error('处理WiFi列表失败:', error); } } // 解析WiFi列表数据 parseWiFiList(data) { const wifiList = []; const view = new DataView(data); let offset = 0; try { while (offset < data.byteLength) { // 读取SSID长度 if (offset >= data.byteLength) break; const ssidLength = view.getUint8(offset); offset += 1; if (ssidLength === 0 || offset + ssidLength > data.byteLength) break; // 读取SSID const ssidBytes = new Uint8Array(data, offset, ssidLength); const ssid = new TextDecoder('utf-8').decode(ssidBytes); offset += ssidLength; // 读取RSSI(信号强度) if (offset >= data.byteLength) break; const rssi = view.getInt8(offset); offset += 1; // 读取认证模式 if (offset >= data.byteLength) break; const authMode = view.getUint8(offset); offset += 1; wifiList.push({ ssid: ssid, rssi: rssi, authMode: authMode, security: this.getSecurityType(authMode) }); } } catch (error) { console.error('解析WiFi列表数据异常:', error); } return wifiList; } // 获取安全类型描述 getSecurityType(authMode) { const securityTypes = { 0: 'OPEN', 1: 'WEP', 2: 'WPA_PSK', 3: 'WPA2_PSK', 4: 'WPA_WPA2_PSK', 5: 'WPA2_ENTERPRISE', 6: 'WPA3_PSK', 7: 'WPA2_WPA3_PSK' }; return securityTypes[authMode] || 'UNKNOWN'; } } ``` #### 4.3.2 WiFi配置页面实现 ```xml WiFi配置 选择要连接的WiFi网络 可用网络 {{item.ssid}} {{item.rssi}}dBm {{item.security}} 📶 未发现WiFi网络,请点击刷新重新扫描 或手动输入WiFi信息
``` ```javascript // pages/config/config.js Page({ data: { scanning: false, wifiList: [], selectedSSID: '', wifiPassword: '', canSubmit: false }, onLoad() { // 获取全局的BluFi实例 const app = getApp(); this.blufi = app.globalData.blufi; if (!this.blufi || !this.blufi.isConnected) { wx.showToast({ title: '设备未连接', icon: 'error' }); wx.navigateBack(); return; } // 设置WiFi列表接收回调 this.blufi.onWiFiListReceived = (wifiList) => { console.log('收到WiFi列表:', wifiList); this.setData({ wifiList: wifiList, scanning: false }); }; // 自动扫描WiFi this.scanWiFi(); }, async scanWiFi() { if (this.data.scanning) return; this.setData({ scanning: true, wifiList: [] }); try { await this.blufi.requestWiFiScan(); // 设置超时 setTimeout(() => { if (this.data.scanning) { this.setData({ scanning: false }); wx.showToast({ title: '扫描超时', icon: 'none' }); } }, 15000); // 15秒超时 } catch (error) { this.setData({ scanning: false }); wx.showToast({ title: '扫描失败', icon: 'error' }); console.error('WiFi扫描失败:', error); } }, selectWiFi(e) { const wifi = e.currentTarget.dataset.wifi; console.log('选择WiFi:', wifi); this.setData({ selectedSSID: wifi.ssid, wifiPassword: '' // 清空密码 }); this.checkCanSubmit(); }, onSSIDInput(e) { this.setData({ selectedSSID: e.detail.value }); this.checkCanSubmit(); }, onPasswordInput(e) { this.setData({ wifiPassword: e.detail.value }); this.checkCanSubmit(); }, checkCanSubmit() { const canSubmit = this.data.selectedSSID.trim().length > 0; this.setData({ canSubmit }); }, submitWiFiConfig(e) { const formData = e.detail.value; const ssid = formData.ssid || this.data.selectedSSID; const password = formData.password || this.data.wifiPassword; if (!ssid.trim()) { wx.showToast({ title: '请输入WiFi名称', icon: 'none' }); return; } console.log('提交WiFi配置:', { ssid, password: '***' }); // 跳转到配网状态页面 wx.navigateTo({ url: `/pages/status/status?ssid=${encodeURIComponent(ssid)}&password=${encodeURIComponent(password)}` }); } }); ``` ### 4.4 第四阶段:WiFi凭据传输和连接确认 #### 4.4.1 WiFi凭据发送实现 ```javascript // 在BluFiProvisioning类中添加WiFi配网方法 class BluFiProvisioning { // ... 前面的代码 ... // 开始WiFi配网 async startProvisioning(ssid, password) { try { console.log('开始WiFi配网:', ssid); this.provisioningState = 'provisioning'; // 步骤1:发送SSID await this.sendWiFiSSID(ssid); await this.delay(500); // 步骤2:发送密码(如果有) if (password && password.trim().length > 0) { await this.sendWiFiPassword(password); await this.delay(500); } // 步骤3:发送连接命令 await this.sendConnectWiFi(); console.log('WiFi配网命令已发送,等待设备响应...'); return true; } catch (error) { console.error('WiFi配网失败:', error); this.provisioningState = 'failed'; throw error; } } // 发送WiFi SSID async sendWiFiSSID(ssid) { console.log('发送WiFi SSID:', ssid); const data = new TextEncoder().encode(ssid); const packet = this.packet.build(0x01, BLUFI_TYPE_DATA.STA_SSID, data); await this.sendData(packet); console.log('SSID发送完成'); } // 发送WiFi密码 async sendWiFiPassword(password) { console.log('发送WiFi密码'); const data = new TextEncoder().encode(password); const packet = this.packet.build(0x01, BLUFI_TYPE_DATA.STA_PASSWD, data); await this.sendData(packet); console.log('密码发送完成'); } // 发送连接WiFi命令 async sendConnectWiFi() { console.log('发送连接WiFi命令'); const packet = this.packet.build(0x00, BLUFI_TYPE_CTRL.CONNECT_WIFI); await this.sendData(packet); console.log('连接命令发送完成'); } // 获取WiFi连接状态 async getWiFiStatus() { console.log('请求WiFi连接状态'); const packet = this.packet.build(0x00, BLUFI_TYPE_CTRL.GET_WIFI_STATUS); await this.sendData(packet); console.log('状态请求已发送'); } // 延迟函数 delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 设置配网回调 setProvisioningCallbacks(callbacks) { this.onProvisioningSuccess = callbacks.onSuccess; this.onProvisioningFailed = callbacks.onFailed; this.onProvisioningProgress = callbacks.onProgress; } } ``` #### 4.4.2 配网状态页面实现 ```xml 配网状态 {{ssid}} {{statusText}} {{subStatusText}} 1 发送WiFi名称 2 发送WiFi密码 3 连接WiFi网络 4 验证网络连接 错误信息: {{errorMessage}} ``` ```javascript // pages/status/status.js Page({ data: { ssid: '', password: '', provisioningState: 'waiting', // waiting, provisioning, success, failed statusText: '准备开始配网', subStatusText: '请稍候...', step: 0, errorMessage: '' }, onLoad(options) { this.setData({ ssid: decodeURIComponent(options.ssid || ''), password: decodeURIComponent(options.password || '') }); // 获取全局的BluFi实例 const app = getApp(); this.blufi = app.globalData.blufi; if (!this.blufi || !this.blufi.isConnected) { wx.showToast({ title: '设备未连接', icon: 'error' }); wx.navigateBack(); return; } // 设置配网回调 this.blufi.setProvisioningCallbacks({ onSuccess: () => this.onProvisioningSuccess(), onFailed: (reason) => this.onProvisioningFailed(reason), onProgress: (step, message) => this.onProvisioningProgress(step, message) }); // 开始配网 this.startProvisioning(); }, async startProvisioning() { try { this.setData({ provisioningState: 'provisioning', statusText: '正在配网', subStatusText: '发送WiFi信息到设备...', step: 0, errorMessage: '' }); // 步骤1:发送SSID this.updateProgress(1, '发送WiFi名称...'); await this.delay(1000); // 步骤2:发送密码 this.updateProgress(2, '发送WiFi密码...'); await this.delay(1000); // 步骤3:连接WiFi this.updateProgress(3, '设备连接WiFi网络...'); // 执行实际配网 await this.blufi.startProvisioning(this.data.ssid, this.data.password); // 等待连接结果 this.updateProgress(4, '等待连接结果...'); // 设置超时检查 this.timeoutTimer = setTimeout(() => { if (this.data.provisioningState === 'provisioning') { this.onProvisioningFailed('连接超时'); } }, 30000); // 30秒超时 } catch (error) { console.error('配网过程异常:', error); this.onProvisioningFailed(error.message || '配网失败'); } }, updateProgress(step, message) { this.setData({ step: step, subStatusText: message }); }, onProvisioningSuccess() { console.log('配网成功'); if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); this.timeoutTimer = null; } this.setData({ provisioningState: 'success', statusText: '配网成功', subStatusText: '设备已成功连接到WiFi网络', step: 4 }); wx.showToast({ title: '配网成功', icon: 'success' }); }, onProvisioningFailed(reason) { console.error('配网失败:', reason); if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); this.timeoutTimer = null; } this.setData({ provisioningState: 'failed', statusText: '配网失败', subStatusText: '设备连接WiFi失败', errorMessage: reason || '未知错误' }); wx.showToast({ title: '配网失败', icon: 'error' }); }, onProvisioningProgress(step, message) { this.updateProgress(step, message); }, retryProvisioning() { this.startProvisioning(); }, goBack() { wx.navigateBack(); }, goHome() { wx.reLaunch({ url: '/pages/index/index' }); }, cancelProvisioning() { if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); this.timeoutTimer = null; } this.setData({ provisioningState: 'waiting', statusText: '配网已取消', subStatusText: '用户取消了配网操作', step: 0 }); }, delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, onUnload() { if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); } } }); ``` ## 5. 小程序项目结构 ### 5.1 目录结构 ``` blufi-miniprogram/ ├── app.js // 小程序入口文件 ├── app.json // 小程序配置文件 ├── app.wxss // 全局样式文件 ├── project.config.json // 项目配置文件 ├── pages/ // 页面目录 │ ├── index/ // 首页 │ │ ├── index.js │ │ ├── index.wxml │ │ └── index.wxss │ ├── scan/ // 设备扫描页面 │ │ ├── scan.js │ │ ├── scan.wxml │ │ └── scan.wxss │ ├── connect/ // 设备连接页面 │ │ ├── connect.js │ │ ├── connect.wxml │ │ └── connect.wxss │ ├── config/ // WiFi配置页面 │ │ ├── config.js │ │ ├── config.wxml │ │ └── config.wxss │ └── status/ // 配网状态页面 │ ├── status.js │ ├── status.wxml │ └── status.wxss ├── utils/ // 工具类目录 │ ├── blufi.js // BluFi协议实现 │ ├── bluetooth.js // 蓝牙工具类 │ └── util.js // 通用工具函数 └── components/ // 组件目录 ├── loading/ // 加载组件 └── device-item/ // 设备列表项组件 ``` ### 5.2 小程序配置文件 #### 5.2.1 app.json ```json { "pages": [ "pages/index/index", "pages/scan/scan", "pages/connect/connect", "pages/config/config", "pages/status/status" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "BluFi配网", "navigationBarTextStyle": "black", "backgroundColor": "#f8f8f8" }, "permission": { "scope.bluetooth": { "desc": "用于连接BluFi设备进行WiFi配网" } }, "requiredBackgroundModes": ["bluetooth-central"], "style": "v2", "sitemapLocation": "sitemap.json" } ``` #### 5.2.2 project.config.json ```json { "description": "BluFi蓝牙配网小程序", "packOptions": { "ignore": [] }, "setting": { "urlCheck": false, "es6": true, "enhance": true, "postcss": true, "preloadBackgroundData": false, "minified": true, "newFeature": false, "coverView": true, "nodeModules": false, "autoAudits": false, "showShadowRootInWxmlPanel": true, "scopeDataCheck": false, "uglifyFileName": false, "checkInvalidKey": true, "checkSiteMap": true, "uploadWithSourceMap": true, "compileHotReLoad": false, "lazyloadPlaceholderEnable": false, "useMultiFrameRuntime": true, "useApiHook": true, "useApiHostProcess": true, "babelSetting": { "ignore": [], "disablePlugins": [], "outputPath": "" }, "enableEngineNative": false, "useIsolateContext": true, "userConfirmedBundleSwitch": false, "packNpmManually": false, "packNpmRelationList": [], "minifyWXSS": true, "disableUseStrict": false, "minifyWXML": true, "showES6CompileOption": false, "useCompilerPlugins": false }, "compileType": "miniprogram", "libVersion": "2.19.4", "appid": "your_app_id", "projectname": "blufi-provisioning", "debugOptions": { "hidedInDevtools": [] }, "scripts": {}, "staticServerOptions": { "baseURL": "", "servePath": "" }, "isGameTourist": false, "condition": { "search": { "list": [] }, "conversation": { "list": [] }, "game": { "list": [] }, "plugin": { "list": [] }, "gamePlugin": { "list": [] }, "miniprogram": { "list": [] } } } ``` ### 5.3 全局应用文件 #### 5.3.1 app.js ```javascript // app.js App({ globalData: { blufi: null, // 全局BluFi实例 deviceInfo: null, // 当前连接的设备信息 wifiConfig: null // WiFi配置信息 }, onLaunch() { console.log('BluFi配网小程序启动'); // 检查蓝牙支持 this.checkBluetoothSupport(); // 初始化全局数据 this.initGlobalData(); }, checkBluetoothSupport() { wx.getSystemInfo({ success: (res) => { console.log('系统信息:', res); // 检查是否支持蓝牙 if (!wx.openBluetoothAdapter) { wx.showModal({ title: '提示', content: '当前微信版本过低,无法使用蓝牙功能,请升级到最新微信版本后重试。', showCancel: false }); } } }); }, initGlobalData() { // 初始化BluFi实例 const BluFiProvisioning = require('./utils/blufi.js'); this.globalData.blufi = new BluFiProvisioning(); }, onShow() { console.log('小程序显示'); }, onHide() { console.log('小程序隐藏'); }, onError(error) { console.error('小程序错误:', error); } }); ``` ## 6. 错误处理和重试机制 ### 6.1 错误码定义 ```javascript // utils/error-codes.js const BluFiErrorCodes = { // 蓝牙相关错误 BLUETOOTH_NOT_AVAILABLE: { code: 1001, message: '蓝牙不可用,请检查蓝牙是否开启' }, BLUETOOTH_ADAPTER_INIT_FAILED: { code: 1002, message: '蓝牙适配器初始化失败' }, DEVICE_SCAN_FAILED: { code: 1003, message: '设备扫描失败' }, DEVICE_NOT_FOUND: { code: 1004, message: '未发现BluFi设备' }, // 连接相关错误 CONNECTION_FAILED: { code: 2001, message: '设备连接失败' }, CONNECTION_TIMEOUT: { code: 2002, message: '设备连接超时' }, SERVICE_NOT_FOUND: { code: 2003, message: '未找到BluFi服务' }, CHARACTERISTIC_NOT_FOUND: { code: 2004, message: '未找到BluFi特征值' }, NOTIFICATION_ENABLE_FAILED: { code: 2005, message: '启用通知失败' }, // 配网相关错误 WIFI_SCAN_FAILED: { code: 3001, message: 'WiFi扫描失败' }, WIFI_SCAN_TIMEOUT: { code: 3002, message: 'WiFi扫描超时' }, WIFI_CREDENTIALS_INVALID: { code: 3003, message: 'WiFi凭据无效' }, WIFI_CONNECTION_FAILED: { code: 3004, message: 'WiFi连接失败' }, WIFI_CONNECTION_TIMEOUT: { code: 3005, message: 'WiFi连接超时' }, PROVISIONING_TIMEOUT: { code: 3006, message: '配网超时' }, // 数据传输错误 DATA_SEND_FAILED: { code: 4001, message: '数据发送失败' }, DATA_PARSE_FAILED: { code: 4002, message: '数据解析失败' }, CHECKSUM_ERROR: { code: 4003, message: '数据校验失败' }, // 通用错误 UNKNOWN_ERROR: { code: 9999, message: '未知错误' } }; module.exports = BluFiErrorCodes; ``` ### 6.2 重试机制实现 ```javascript // utils/retry.js class RetryManager { constructor(options = {}) { this.maxRetries = options.maxRetries || 3; this.retryDelay = options.retryDelay || 2000; this.backoffMultiplier = options.backoffMultiplier || 1.5; this.maxDelay = options.maxDelay || 10000; } async execute(operation, context = '') { let lastError; let currentDelay = this.retryDelay; for (let attempt = 0; attempt <= this.maxRetries; attempt++) { try { console.log(`${context} - 尝试 ${attempt + 1}/${this.maxRetries + 1}`); const result = await operation(); if (attempt > 0) { console.log(`${context} - 重试成功`); } return result; } catch (error) { lastError = error; console.error(`${context} - 尝试 ${attempt + 1} 失败:`, error); // 如果是最后一次尝试,直接抛出错误 if (attempt === this.maxRetries) { break; } // 等待后重试 console.log(`${context} - ${currentDelay}ms 后重试`); await this.delay(currentDelay); // 增加延迟时间(指数退避) currentDelay = Math.min( currentDelay * this.backoffMultiplier, this.maxDelay ); } } console.error(`${context} - 所有重试都失败了`); throw lastError; } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } module.exports = RetryManager; ``` ### 6.3 错误处理工具类 ```javascript // utils/error-handler.js const BluFiErrorCodes = require('./error-codes.js'); class ErrorHandler { static handleError(error, context = '') { console.error(`错误处理 [${context}]:`, error); let errorInfo = { code: BluFiErrorCodes.UNKNOWN_ERROR.code, message: BluFiErrorCodes.UNKNOWN_ERROR.message, detail: error.message || error.errMsg || '未知错误' }; // 根据错误信息匹配错误码 if (error.errMsg) { errorInfo = this.mapWxErrorToCode(error.errMsg); } else if (error.code) { errorInfo = this.findErrorByCode(error.code); } else if (error.message) { errorInfo = this.mapMessageToCode(error.message); } return errorInfo; } static mapWxErrorToCode(errMsg) { const errorMappings = { 'bluetooth not available': BluFiErrorCodes.BLUETOOTH_NOT_AVAILABLE, 'bluetooth adapter init fail': BluFiErrorCodes.BLUETOOTH_ADAPTER_INIT_FAILED, 'createBLEConnection:fail': BluFiErrorCodes.CONNECTION_FAILED, 'getBLEDeviceServices:fail': BluFiErrorCodes.SERVICE_NOT_FOUND, 'getBLEDeviceCharacteristics:fail': BluFiErrorCodes.CHARACTERISTIC_NOT_FOUND, 'notifyBLECharacteristicValueChange:fail': BluFiErrorCodes.NOTIFICATION_ENABLE_FAILED, 'writeBLECharacteristicValue:fail': BluFiErrorCodes.DATA_SEND_FAILED }; for (const [key, errorCode] of Object.entries(errorMappings)) { if (errMsg.includes(key)) { return { code: errorCode.code, message: errorCode.message, detail: errMsg }; } } return { code: BluFiErrorCodes.UNKNOWN_ERROR.code, message: BluFiErrorCodes.UNKNOWN_ERROR.message, detail: errMsg }; } static findErrorByCode(code) { for (const errorCode of Object.values(BluFiErrorCodes)) { if (errorCode.code === code) { return { code: errorCode.code, message: errorCode.message, detail: '' }; } } return { code: BluFiErrorCodes.UNKNOWN_ERROR.code, message: BluFiErrorCodes.UNKNOWN_ERROR.message, detail: `错误码: ${code}` }; } static mapMessageToCode(message) { const messageMappings = { '蓝牙不可用': BluFiErrorCodes.BLUETOOTH_NOT_AVAILABLE, '设备未连接': BluFiErrorCodes.CONNECTION_FAILED, '连接超时': BluFiErrorCodes.CONNECTION_TIMEOUT, '配网超时': BluFiErrorCodes.PROVISIONING_TIMEOUT, 'WiFi连接失败': BluFiErrorCodes.WIFI_CONNECTION_FAILED }; for (const [key, errorCode] of Object.entries(messageMappings)) { if (message.includes(key)) { return { code: errorCode.code, message: errorCode.message, detail: message }; } } return { code: BluFiErrorCodes.UNKNOWN_ERROR.code, message: BluFiErrorCodes.UNKNOWN_ERROR.message, detail: message }; } static showError(errorInfo, options = {}) { const title = options.title || '错误'; const showDetail = options.showDetail !== false; let content = errorInfo.message; if (showDetail && errorInfo.detail) { content += `\n\n详细信息: ${errorInfo.detail}`; } wx.showModal({ title: title, content: content, showCancel: false, confirmText: '确定' }); } static showToast(errorInfo) { wx.showToast({ title: errorInfo.message, icon: 'error', duration: 3000 }); } } module.exports = ErrorHandler; ``` ## 7. 安全机制和数据保护 ### 7.1 数据加密(可选) ```javascript // utils/encryption.js class BluFiEncryption { constructor(options = {}) { this.enabled = options.enabled || false; this.algorithm = options.algorithm || 'AES-128-CFB'; this.key = options.key || null; this.iv = options.iv || null; } // 设置加密密钥 setKey(key) { this.key = key; } // 设置初始化向量 setIV(iv) { this.iv = iv; } // 加密数据 encrypt(data) { if (!this.enabled || !this.key) { return data; } try { // 这里应该实现实际的AES加密 // 由于微信小程序环境限制,可能需要使用第三方加密库 console.log('数据加密(模拟)'); return data; // 返回加密后的数据 } catch (error) { console.error('数据加密失败:', error); throw error; } } // 解密数据 decrypt(encryptedData) { if (!this.enabled || !this.key) { return encryptedData; } try { // 这里应该实现实际的AES解密 console.log('数据解密(模拟)'); return encryptedData; // 返回解密后的数据 } catch (error) { console.error('数据解密失败:', error); throw error; } } } module.exports = BluFiEncryption; ``` ### 7.2 数据校验 ```javascript // utils/checksum.js class ChecksumCalculator { // 计算简单校验和 static calculateSimpleChecksum(data) { let checksum = 0; const view = new Uint8Array(data); for (let i = 0; i < view.length; i++) { checksum += view[i]; } return checksum & 0xFFFF; } // 验证校验和 static verifyChecksum(data, expectedChecksum) { const calculatedChecksum = this.calculateSimpleChecksum(data); return calculatedChecksum === expectedChecksum; } // CRC16校验(可选) static calculateCRC16(data) { let crc = 0xFFFF; const polynomial = 0x1021; const view = new Uint8Array(data); for (let i = 0; i < view.length; i++) { crc ^= (view[i] << 8); for (let j = 0; j < 8; j++) { if (crc & 0x8000) { crc = (crc << 1) ^ polynomial; } else { crc <<= 1; } crc &= 0xFFFF; } } return crc; } } module.exports = ChecksumCalculator; ``` ### 7.3 敏感数据处理 ```javascript // utils/security.js class SecurityManager { // 清理敏感数据 static clearSensitiveData() { // 清理WiFi密码等敏感信息 const app = getApp(); if (app.globalData.wifiConfig) { app.globalData.wifiConfig.password = null; } // 清理本地存储中的敏感数据 try { wx.removeStorageSync('wifi_password'); wx.removeStorageSync('encryption_key'); } catch (error) { console.warn('清理本地存储失败:', error); } } // 验证WiFi凭据格式 static validateWiFiCredentials(ssid, password) { const errors = []; // SSID验证 if (!ssid || ssid.trim().length === 0) { errors.push('WiFi名称不能为空'); } else if (ssid.length > 32) { errors.push('WiFi名称长度不能超过32个字符'); } // 密码验证(可选) if (password && password.length > 64) { errors.push('WiFi密码长度不能超过64个字符'); } return { valid: errors.length === 0, errors: errors }; } // 生成随机序列号 static generateSequenceNumber() { return Math.floor(Math.random() * 65536); } } module.exports = SecurityManager; ``` ## 8. 测试和调试 ### 8.1 调试工具 ```javascript // utils/debug.js class DebugManager { constructor() { this.enabled = true; // 生产环境应设为false this.logLevel = 'DEBUG'; // DEBUG, INFO, WARN, ERROR this.logs = []; this.maxLogs = 1000; } log(level, message, data = null) { if (!this.enabled) return; const timestamp = new Date().toISOString(); const logEntry = { timestamp, level, message, data }; // 添加到日志数组 this.logs.push(logEntry); // 限制日志数量 if (this.logs.length > this.maxLogs) { this.logs.shift(); } // 控制台输出 const logMessage = `[${timestamp}] ${level}: ${message}`; switch (level) { case 'DEBUG': console.log(logMessage, data); break; case 'INFO': console.info(logMessage, data); break; case 'WARN': console.warn(logMessage, data); break; case 'ERROR': console.error(logMessage, data); break; } } debug(message, data) { this.log('DEBUG', message, data); } info(message, data) { this.log('INFO', message, data); } warn(message, data) { this.log('WARN', message, data); } error(message, data) { this.log('ERROR', message, data); } // 导出日志 exportLogs() { return JSON.stringify(this.logs, null, 2); } // 清空日志 clearLogs() { this.logs = []; } // 获取最近的错误日志 getRecentErrors(count = 10) { return this.logs .filter(log => log.level === 'ERROR') .slice(-count); } } // 创建全局调试实例 const debugManager = new DebugManager(); module.exports = debugManager; ``` ### 8.2 测试用例 ```javascript // test/blufi-test.js class BluFiTest { constructor() { this.testResults = []; } // 运行所有测试 async runAllTests() { console.log('开始BluFi测试...'); await this.testBluetoothInit(); await this.testDeviceScan(); await this.testPacketParsing(); await this.testChecksumCalculation(); this.printTestResults(); } // 测试蓝牙初始化 async testBluetoothInit() { try { const blufi = new BluFiProvisioning(); await blufi.initBluetooth(); this.addTestResult('蓝牙初始化', true, '成功'); } catch (error) { this.addTestResult('蓝牙初始化', false, error.message); } } // 测试设备扫描 async testDeviceScan() { try { const blufi = new BluFiProvisioning(); let deviceFound = false; await blufi.startScan((device) => { if (blufi.isValidBluFiDevice(device)) { deviceFound = true; } }); // 等待5秒 await new Promise(resolve => setTimeout(resolve, 5000)); await blufi.stopScan(); this.addTestResult('设备扫描', deviceFound, deviceFound ? '发现设备' : '未发现设备'); } catch (error) { this.addTestResult('设备扫描', false, error.message); } } // 测试数据包解析 testPacketParsing() { try { const packet = new BluFiPacket(); // 构建测试数据包 const testData = new TextEncoder().encode('test'); const buffer = packet.build(0x01, 0x02, testData); // 解析数据包 const parsed = packet.parse(buffer); const success = parsed.type === 0x01 && parsed.subtype === 0x02 && new TextDecoder().decode(parsed.data) === 'test'; this.addTestResult('数据包解析', success, success ? '解析正确' : '解析错误'); } catch (error) { this.addTestResult('数据包解析', false, error.message); } } // 测试校验和计算 testChecksumCalculation() { try { const testData = new Uint8Array([0x01, 0x02, 0x03, 0x04]); const checksum = ChecksumCalculator.calculateSimpleChecksum(testData.buffer); // 预期校验和: 1+2+3+4 = 10 const expected = 10; const success = checksum === expected; this.addTestResult('校验和计算', success, success ? `校验和正确: ${checksum}` : `校验和错误: 期望${expected}, 实际${checksum}`); } catch (error) { this.addTestResult('校验和计算', false, error.message); } } addTestResult(testName, success, message) { this.testResults.push({ name: testName, success: success, message: message, timestamp: new Date().toISOString() }); } printTestResults() { console.log('\n=== BluFi测试结果 ==='); let passCount = 0; let totalCount = this.testResults.length; this.testResults.forEach(result => { const status = result.success ? '✓ 通过' : '✗ 失败'; console.log(`${status} ${result.name}: ${result.message}`); if (result.success) { passCount++; } }); console.log(`\n总计: ${passCount}/${totalCount} 个测试通过`); if (passCount === totalCount) { console.log('🎉 所有测试都通过了!'); } else { console.log('❌ 部分测试失败,请检查相关功能'); } } } module.exports = BluFiTest; ``` ## 9. 性能优化 ### 9.1 内存管理 ```javascript // utils/memory-manager.js class MemoryManager { static clearUnusedData() { // 清理不再使用的设备列表 const app = getApp(); if (app.globalData.deviceList) { app.globalData.deviceList = []; } // 清理WiFi列表缓存 if (app.globalData.wifiList) { app.globalData.wifiList = []; } // 强制垃圾回收(如果支持) if (typeof wx.triggerGC === 'function') { wx.triggerGC(); } } static monitorMemoryUsage() { if (typeof wx.getPerformance === 'function') { const performance = wx.getPerformance(); if (performance.memory) { console.log('内存使用情况:', { used: performance.memory.usedJSHeapSize, total: performance.memory.totalJSHeapSize, limit: performance.memory.jsHeapSizeLimit }); } } } } module.exports = MemoryManager; ``` ### 9.2 连接优化 ```javascript // utils/connection-optimizer.js class ConnectionOptimizer { constructor() { this.connectionPool = new Map(); this.maxConnections = 1; // BluFi通常只需要一个连接 } // 优化连接参数 getOptimalConnectionParams() { return { timeout: 15000, // 15秒连接超时 interval: 100, // 100ms扫描间隔 allowDuplicatesKey: false, services: [BLUFI_SERVICE_UUID] }; } // 连接重用 reuseConnection(deviceId) { if (this.connectionPool.has(deviceId)) { const connection = this.connectionPool.get(deviceId); if (connection.isValid) { return connection; } else { this.connectionPool.delete(deviceId); } } return null; } // 添加连接到池 addConnection(deviceId, connection) { // 如果池满了,移除最旧的连接 if (this.connectionPool.size >= this.maxConnections) { const firstKey = this.connectionPool.keys().next().value; this.connectionPool.delete(firstKey); } this.connectionPool.set(deviceId, connection); } // 清理连接池 clearPool() { this.connectionPool.clear(); } } module.exports = ConnectionOptimizer; ``` ## 10. 部署和发布 ### 10.1 发布前检查清单 - [ ] 所有功能测试通过 - [ ] 与ESP官方espblufi应用对比测试完成 - [ ] 错误处理机制完善 - [ ] 用户界面友好性检查 - [ ] 性能测试通过 - [ ] 安全性检查完成 - [ ] 代码审查完成 - [ ] 文档更新完成 ### 10.2 版本管理 ```javascript // utils/version.js const VERSION_INFO = { version: '1.0.0', buildNumber: '20240101', releaseDate: '2024-01-01', features: [ 'BluFi设备扫描和连接', 'WiFi网络配置', '配网状态监控', '错误处理和重试机制' ], compatibility: { minWechatVersion: '7.0.0', minSystemVersion: { ios: '10.0', android: '6.0' }, espIdfVersion: '4.4+' } }; module.exports = VERSION_INFO; ``` ### 10.3 用户手册 #### 10.3.1 使用步骤 1. **准备工作** - 确保手机蓝牙已开启 - 确保ESP32设备处于配网模式 - 确保手机已连接到互联网 2. **开始配网** - 打开BluFi配网小程序 - 点击"开始扫描"按钮 - 从设备列表中选择要配网的设备 3. **连接设备** - 等待设备连接完成 - 连接成功后会显示"设备连接成功" 4. **配置WiFi** - 选择要连接的WiFi网络,或手动输入WiFi信息 - 输入WiFi密码 - 点击"开始配网"按钮 5. **等待配网完成** - 等待设备连接到WiFi网络 - 配网成功后会显示"配网完成" #### 10.3.2 常见问题解决 **Q: 扫描不到设备怎么办?** A: - 检查手机蓝牙是否开启 - 确认设备是否处于配网模式 - 尝试重新启动设备 - 检查设备距离是否过远 **Q: 连接设备失败怎么办?** A: - 确认设备未被其他应用占用 - 尝试重启手机蓝牙 - 重新扫描设备 **Q: WiFi配网失败怎么办?** A: - 检查WiFi密码是否正确 - 确认WiFi信号强度是否足够 - 检查WiFi网络是否正常 - 尝试重新配网 ## 11. 附录 ### 11.1 参考文档 - [ESP-IDF BluFi官方文档](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/blufi.html) - [微信小程序蓝牙API文档](https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth/wx.openBluetoothAdapter.html) - [BluFi协议规范](https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/blufi) ### 11.2 技术支持 - 开发团队:[联系方式] - 问题反馈:[反馈渠道] - 更新通知:[通知方式] ### 11.3 更新日志 #### v1.0.0 (2024-01-01) - 初始版本发布 - 实现基础BluFi配网功能 - 支持设备扫描、连接、WiFi配置 - 完善错误处理和重试机制 --- **文档版本**: 2.0 **创建日期**: 2025年8月 **最后更新**: 2025年8月 **文档状态**: 已完成