# 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
正在扫描设备...
{{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
正在连接设备...
✓
设备连接成功
设备未连接
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
{{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
✓
✗
⏳
{{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月
**文档状态**: 已完成