ESP32蓝牙配网完整指南
本文介绍了一个基于ESP32的蓝牙BLE配网系统实现方案。系统通过BLE接收WiFi配置信息并连接网络,包含以下功能点:1) 使用BLE广播和接收SSID/密码;2) 支持WiFi连接和状态反馈;3) 通过NVS存储保存WiFi配置;4) LED指示灯显示连接状态。代码实现了BLE服务端创建、特征值读写回调处理、WiFi配置解析和连接等功能模块,并提供了完整的初始化流程和状态监控机制。系统在首次使
/*
- ESP32 蓝牙配网(Bluetooth Provisioning)完整示例
- 功能:通过蓝牙BLE接收WiFi配置信息并连接网络
- 支持:BLE广播、接收SSID/密码、WiFi连接、状态反馈
- 作者:示例代码
- 日期:2025
*/
#include <BLE2902.h> // BLE描述符库(用于通知功能)
#include <BLEDevice.h> // BLE设备库
#include <BLEServer.h> // BLE服务器库
#include <BLEUtils.h> // BLE工具库
#include <Preferences.h> // NVS存储库,用于保存WiFi配置
#include <WiFi.h> // WiFi库,用于连接无线网络
// ==================== 配置参数 ====================
// 蓝牙设备名称(手机端会看到这个名字)
#define BLE_DEVICE_NAME “ESP32_Config”
// 蓝牙服务和特征UUID(通用唯一识别码)
// 这些UUID需要在手机APP中对应配置
#define SERVICE_UUID “4fafc201-1fb5-459e-8fcc-c5c9c331914b” // 配网服务UUID
#define CHARACTERISTIC_UUID_RX “beb5483e-36e1-4688-b7f5-ea07361b26a8” // 接收特征UUID(手机→ESP32)
#define CHARACTERISTIC_UUID_TX “beb5483e-36e1-4688-b7f5-ea07361b26a9” // 发送特征UUID(ESP32→手机)
// LED指示灯引脚(可选,用于状态指示)
#define LED_PIN 2
// WiFi连接超时时间(秒)
#define WIFI_CONNECT_TIMEOUT 20
// ==================== 全局变量 ====================
// BLE相关对象指针
BLEServer *pServer = NULL; // BLE服务器指针
BLECharacteristic *pTxCharacteristic = NULL; // 发送特征指针(用于向手机发送数据)
bool deviceConnected = false; // 蓝牙连接状态标志
bool oldDeviceConnected = false; // 上一次蓝牙连接状态
bool wifiConfigReceived = false; // WiFi配置接收标志
// WiFi配置信息
String receivedSSID = “”; // 接收到的WiFi名称
String receivedPassword = “”; // 接收到的WiFi密码
// Preferences对象(用于NVS存储)
Preferences preferences; // NVS存储对象,保存配置到Flash
// WiFi连接状态
bool wifiConnected = false; // WiFi连接成功标志
// ==================== BLE服务器回调类 ====================
// 当蓝牙连接状态改变时会调用此类的方法
class MyServerCallbacks : public BLEServerCallbacks
{
// 当手机连接到ESP32时调用
void onConnect(BLEServer *pServer)
{
deviceConnected = true; // 设置连接标志为真
Serial.println(“[BLE] 客户端已连接”);
// 点亮LED表示有设备连接
digitalWrite(LED_PIN, HIGH);
}
// 当手机断开连接时调用
void onDisconnect(BLEServer *pServer)
{
deviceConnected = false; // 设置连接标志为假
Serial.println("[BLE] 客户端已断开连接");
// 熄灭LED表示设备断开
digitalWrite(LED_PIN, LOW);
// 延迟后重新开始广播,允许其他设备连接
delay(500);
pServer->startAdvertising();
Serial.println("[BLE] 重新开始广播...");
}
};
// ==================== BLE特征回调类 ====================
// 当接收到手机发送的数据时会调用此类的方法
class MyCallbacks : public BLECharacteristicCallbacks
{
// 当手机向ESP32写入数据时调用
void onWrite(BLECharacteristic *pCharacteristic)
{
// 获取接收到的数据
std::string rxValue = pCharacteristic->getValue();
// 检查是否接收到数据
if (rxValue.length() > 0) {
Serial.println("\n[BLE] 收到数据:");
Serial.print("[BLE] 长度: ");
Serial.println(rxValue.length());
// 打印接收到的原始数据(十六进制)
Serial.print("[BLE] 数据(HEX): ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.printf("%02X ", (uint8_t)rxValue[i]);
}
Serial.println();
// 打印接收到的字符串数据
Serial.print("[BLE] 数据(String): ");
Serial.println(rxValue.c_str());
// 解析WiFi配置数据
// 数据格式:SSID|PASSWORD
// 例如:MyWiFi|12345678
parseWiFiConfig(String(rxValue.c_str()));
}
}
};
// ==================== 初始化设置 ====================
void setup()
{
// 初始化串口通信,波特率115200
Serial.begin(115200);
delay(1000); // 等待串口稳定
// 打印启动信息
Serial.println();
Serial.println("===================================");
Serial.println(" ESP32 蓝牙配网系统 v1.0");
Serial.println("===================================");
// 初始化LED引脚
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // 初始熄灭
// LED闪烁3次表示启动
for (int i = 0; i < 3; i++) {
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
delay(200);
}
// 打印设备信息
Serial.println("\n--- 设备信息 ---");
Serial.printf("芯片型号: %s\n", ESP.getChipModel());
Serial.printf("MAC地址: %s\n", WiFi.macAddress().c_str());
Serial.printf("可用内存: %d bytes\n", ESP.getFreeHeap());
Serial.println("-------------------\n");
// 尝试从NVS加载已保存的WiFi配置
if (loadWiFiConfig()) {
Serial.println("[配置] 发现已保存的WiFi配置");
Serial.printf("[配置] SSID: %s\n", receivedSSID.c_str());
// 尝试连接WiFi
if (connectToWiFi(receivedSSID, receivedPassword)) {
Serial.println("[WiFi] 使用已保存配置连接成功!");
wifiConnected = true;
// 连接成功后可以选择不启动BLE,节省功耗
// 这里为了演示,仍然启动BLE
Serial.println("[系统] WiFi已连接,但仍启动BLE以便重新配置");
} else {
Serial.println("[WiFi] 使用已保存配置连接失败");
Serial.println("[系统] 将启动BLE进行配网");
}
} else {
Serial.println("[配置] 未找到已保存的WiFi配置");
Serial.println("[系统] 启动BLE进行首次配网");
}
// 初始化BLE
initBLE();
Serial.println("\n[系统] 初始化完成,进入主循环");
Serial.println("[提示] 请使用手机BLE调试APP连接设备");
Serial.printf("[提示] 设备名称: %s\n", BLE_DEVICE_NAME);
}
// ==================== 主循环 ====================
void loop()
{
// ========== 处理蓝牙连接状态变化 ==========
// 检查蓝牙连接状态是否发生变化
if (deviceConnected != oldDeviceConnected) {
oldDeviceConnected = deviceConnected; // 更新状态
if (deviceConnected) {
// 新设备连接
Serial.println("[状态] 蓝牙已连接");
} else {
// 设备断开连接
Serial.println("[状态] 蓝牙已断开");
}
}
// ========== 处理WiFi配置 ==========
// 如果接收到新的WiFi配置
if (wifiConfigReceived) {
wifiConfigReceived = false; // 重置标志
Serial.println("\n[WiFi] 准备连接WiFi...");
Serial.printf("[WiFi] SSID: %s\n", receivedSSID.c_str());
Serial.printf("[WiFi] 密码长度: %d\n", receivedPassword.length());
// 向手机发送"开始连接"的状态
sendBLEMessage("CONNECTING");
// 尝试连接WiFi
if (connectToWiFi(receivedSSID, receivedPassword)) {
// 连接成功
Serial.println("[WiFi] ✓ 连接成功!");
wifiConnected = true;
// 保存WiFi配置到NVS
saveWiFiConfig(receivedSSID, receivedPassword);
// 向手机发送成功消息
String successMsg = "SUCCESS|" + WiFi.localIP().toString();
sendBLEMessage(successMsg);
// LED快速闪烁5次表示成功
for (int i = 0; i < 5; i++) {
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
// 可选:连接成功后关闭BLE以节省功耗
// BLEDevice::deinit();
// Serial.println("[BLE] 已关闭BLE功能");
} else {
// 连接失败
Serial.println("[WiFi] ✗ 连接失败!");
// 向手机发送失败消息
sendBLEMessage("FAILED");
// LED慢速闪烁3次表示失败
for (int i = 0; i < 3; i++) {
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
delay(500);
}
}
}
// ========== WiFi状态监控 ==========
// 如果之前连接过WiFi,检查连接状态
if (wifiConnected) {
// 检查WiFi是否断开
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[WiFi] 连接断开,尝试重连...");
wifiConnected = false;
// 尝试重新连接
if (connectToWiFi(receivedSSID, receivedPassword)) {
Serial.println("[WiFi] 重连成功");
wifiConnected = true;
} else {
Serial.println("[WiFi] 重连失败");
}
}
}
// ========== 定期打印状态 ==========
static unsigned long lastStatusPrint = 0;
if (millis() - lastStatusPrint > 10000) { // 每10秒打印一次
lastStatusPrint = millis();
printStatus();
}
// 短暂延迟,避免CPU占用过高
delay(100);
}
// ==================== 初始化BLE函数 ====================
void initBLE()
{
Serial.println(“\n[BLE] 初始化蓝牙…”);
// 初始化BLE设备,设置设备名称
BLEDevice::init(BLE_DEVICE_NAME);
Serial.printf("[BLE] 设备名称: %s\n", BLE_DEVICE_NAME);
// 创建BLE服务器
pServer = BLEDevice::createServer();
// 设置服务器回调函数(处理连接/断开事件)
pServer->setCallbacks(new MyServerCallbacks());
Serial.println("[BLE] BLE服务器已创建");
// 创建BLE服务(使用预定义的UUID)
BLEService *pService = pServer->createService(SERVICE_UUID);
Serial.println("[BLE] BLE服务已创建");
// 创建BLE发送特征(ESP32 → 手机)
// 属性:READ(可读)+ NOTIFY(可通知)
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY);
// 添加BLE2902描述符(用于启用通知功能)
pTxCharacteristic->addDescriptor(new BLE2902());
Serial.println("[BLE] TX特征已创建(用于发送数据到手机)");
// 创建BLE接收特征(手机 → ESP32)
// 属性:WRITE(可写)
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE);
// 设置接收特征的回调函数(当接收到数据时调用)
pRxCharacteristic->setCallbacks(new MyCallbacks());
Serial.println("[BLE] RX特征已创建(用于接收手机数据)");
// 启动服务
pService->start();
Serial.println("[BLE] 服务已启动");
// 获取广播对象
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
// 添加服务UUID到广播数据
pAdvertising->addServiceUUID(SERVICE_UUID);
// 设置广播参数
pAdvertising->setScanResponse(true); // 启用扫描响应
// 设置最小连接间隔(提高兼容性)
pAdvertising->setMinPreferred(0x06); // 0x06 * 1.25ms = 7.5ms
// 设置最大连接间隔
pAdvertising->setMaxPreferred(0x12); // 0x12 * 1.25ms = 22.5ms
// 开始广播
BLEDevice::startAdvertising();
Serial.println("[BLE] ✓ 广播已开始");
Serial.println("[BLE] 等待客户端连接...\n");
}
// ==================== 解析WiFi配置函数 ====================
void parseWiFiConfig(String data)
{
Serial.println(“\n[解析] 开始解析WiFi配置…”);
// 查找分隔符位置(使用|作为SSID和密码的分隔符)
int separatorIndex = data.indexOf('|');
// 检查数据格式是否正确
if (separatorIndex == -1) {
Serial.println("[解析] ✗ 数据格式错误,未找到分隔符'|'");
Serial.println("[解析] 正确格式: SSID|PASSWORD");
sendBLEMessage("ERROR|格式错误");
return;
}
// 提取SSID(分隔符之前的部分)
receivedSSID = data.substring(0, separatorIndex);
receivedSSID.trim(); // 去除首尾空格
// 提取密码(分隔符之后的部分)
receivedPassword = data.substring(separatorIndex + 1);
receivedPassword.trim(); // 去除首尾空格
// 验证SSID是否为空
if (receivedSSID.length() == 0) {
Serial.println("[解析] ✗ SSID不能为空");
sendBLEMessage("ERROR|SSID为空");
return;
}
// 打印解析结果
Serial.println("[解析] ✓ 解析成功");
Serial.printf("[解析] SSID: %s\n", receivedSSID.c_str());
Serial.printf("[解析] 密码: %s\n", receivedPassword.c_str()); // 生产环境中应该隐藏密码
// 设置配置接收标志
wifiConfigReceived = true;
// 向手机发送确认消息
sendBLEMessage("CONFIG_OK");
}
// ==================== 连接WiFi函数 ====================
bool connectToWiFi(String ssid, String password)
{
Serial.println(“\n[WiFi] 开始连接…”);
// 断开当前连接(如果有)
WiFi.disconnect(true);
delay(1000);
// 设置WiFi为Station模式(客户端模式)
WiFi.mode(WIFI_STA);
// 开始连接WiFi
WiFi.begin(ssid.c_str(), password.c_str());
Serial.printf("[WiFi] 正在连接到: %s\n", ssid.c_str());
// 等待连接,设置超时时间
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < WIFI_CONNECT_TIMEOUT) {
delay(500);
Serial.print(".");
// LED闪烁表示正在连接
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
timeout++;
}
Serial.println();
// 检查连接结果
if (WiFi.status() == WL_CONNECTED) {
// 连接成功
Serial.println("[WiFi] ✓ 连接成功!");
Serial.printf("[WiFi] IP地址: %s\n", WiFi.localIP().toString().c_str());
Serial.printf("[WiFi] 子网掩码: %s\n", WiFi.subnetMask().toString().c_str());
Serial.printf("[WiFi] 网关: %s\n", WiFi.gatewayIP().toString().c_str());
Serial.printf("[WiFi] DNS: %s\n", WiFi.dnsIP().toString().c_str());
Serial.printf("[WiFi] 信号强度: %d dBm\n", WiFi.RSSI());
// LED常亮表示连接成功
digitalWrite(LED_PIN, HIGH);
return true;
} else {
// 连接失败
Serial.println("[WiFi] ✗ 连接失败");
Serial.print("[WiFi] 状态码: ");
Serial.println(WiFi.status());
// 熄灭LED
digitalWrite(LED_PIN, LOW);
return false;
}
}
// ==================== 保存WiFi配置函数 ====================
void saveWiFiConfig(String ssid, String password)
{
Serial.println(“\n[存储] 保存WiFi配置到NVS…”);
// 打开Preferences命名空间(wifi_config)
// 第二个参数false表示以读写模式打开
preferences.begin("wifi_config", false);
// 保存SSID
preferences.putString("ssid", ssid);
// 保存密码
preferences.putString("password", password);
// 关闭Preferences
preferences.end();
Serial.println("[存储] ✓ 配置已保存");
}
// ==================== 加载WiFi配置函数 ====================
bool loadWiFiConfig()
{
Serial.println(“\n[存储] 从NVS加载WiFi配置…”);
// 打开Preferences命名空间(wifi_config)
// 第二个参数true表示以只读模式打开
preferences.begin("wifi_config", true);
// 读取SSID
receivedSSID = preferences.getString("ssid", "");
// 读取密码
receivedPassword = preferences.getString("password", "");
// 关闭Preferences
preferences.end();
// 检查是否读取到有效配置
if (receivedSSID.length() > 0) {
Serial.println("[存储] ✓ 配置加载成功");
return true;
} else {
Serial.println("[存储] 未找到已保存的配置");
return false;
}
}
// ==================== 清除WiFi配置函数 ====================
void clearWiFiConfig()
{
Serial.println(“\n[存储] 清除WiFi配置…”);
// 打开Preferences命名空间
preferences.begin("wifi_config", false);
// 清除所有数据
preferences.clear();
// 关闭Preferences
preferences.end();
Serial.println("[存储] ✓ 配置已清除");
}
// ==================== 通过BLE发送消息函数 ====================
void sendBLEMessage(String message)
{
// 检查是否有设备连接
if (deviceConnected && pTxCharacteristic != NULL) {
Serial.printf(“[BLE] 发送消息: %s\n”, message.c_str());
// 设置特征值
pTxCharacteristic->setValue(message.c_str());
// 发送通知给连接的设备
pTxCharacteristic->notify();
// 短暂延迟,确保数据发送完成
delay(100);
} else {
Serial.println("[BLE] 无法发送消息:没有设备连接");
}
}
// ==================== 打印系统状态函数 ====================
void printStatus()
{
Serial.println(“\n========== 系统状态 ==========”);
// 蓝牙状态
Serial.print("BLE状态: ");
Serial.println(deviceConnected ? "已连接" : "未连接");
// WiFi状态
Serial.print("WiFi状态: ");
if (WiFi.status() == WL_CONNECTED) {
Serial.printf("已连接 (%s)\n", WiFi.localIP().toString().c_str());
Serial.printf("信号强度: %d dBm\n", WiFi.RSSI());
} else {
Serial.println("未连接");
}
// 内存状态
Serial.printf("可用内存: %d bytes\n", ESP.getFreeHeap());
// 运行时间
Serial.printf("运行时间: %lu 秒\n", millis() / 1000);
Serial.println("==============================\n");
}
/*
- ==================== 使用说明 ====================
-
- 硬件准备:
-
- ESP32开发板
-
- LED(可选,连接到GPIO2)
-
- USB数据线
-
- 软件准备:
-
- Arduino IDE安装ESP32支持
-
- 安装手机BLE调试APP:
-
* iOS: LightBlue, nRF Connect -
* Android: nRF Connect, BLE Scanner -
- 上传代码:
-
- 连接ESP32到电脑
-
- 选择正确的开发板和端口
-
- 上传代码
-
- 使用流程:
- a) ESP32上电后会启动BLE广播
- b) 打开手机BLE调试APP
- c) 搜索并连接到"ESP32_Config"
- d) 找到RX特征(UUID: beb5483e-36e1-4688-b7f5-ea07361b26a8)
- e) 写入WiFi配置,格式:SSID|PASSWORD
-
例如:MyWiFi|12345678 - f) ESP32会自动连接WiFi并返回结果
-
- 数据格式:
- 发送到ESP32(手机→ESP32):
-
- “SSID|PASSWORD” - WiFi配置
- ESP32返回(ESP32→手机):
-
- “CONFIG_OK” - 配置接收成功
-
- “CONNECTING” - 正在连接WiFi
-
- “SUCCESS|IP” - 连接成功+IP地址
-
- “FAILED” - 连接失败
-
- “ERROR|msg” - 错误消息
-
- 串口监视器查看日志:
-
- 波特率设置为115200
-
- 可以看到详细的运行日志
-
- 测试命令:
- 使用nRF Connect APP:
-
- 连接设备
-
- 选择RX特征
-
- 写入: “TestWiFi|password123”
-
- 订阅TX特征查看返回消息
-
- 清除已保存的WiFi配置:
- 在setup()中添加一行:
- clearWiFiConfig();
-
- 注意事项:
-
- SSID和密码不能包含’|'字符
-
- 密码可以为空(开放网络)
-
- WiFi配置保存在NVS中,断电不丢失
-
- 连接成功后BLE仍然运行(可选择关闭)
- ==================== 高级功能扩展 ====================
- 可以添加的功能:
-
- WiFi扫描:返回周围的WiFi列表
-
- 配置加密:对传输的密码进行加密
-
- 设备绑定:只允许特定设备配置
-
- 多WiFi配置:保存多个WiFi信息
-
- 远程控制:通过BLE控制GPIO
-
- 状态上报:定时上报温度、湿度等数据
-
- 固件升级:通过BLE进行OTA升级
-
- Web配网:WiFi连接后提供Web配置界面
- ==================== 故障排除 ====================
- 问题1: 手机搜索不到设备
- 解决:
-
- 确保ESP32已上电并运行
-
- 检查手机蓝牙是否开启
-
- 尝试重启ESP32
-
- 查看串口输出确认BLE已启动
- 问题2: 无法连接WiFi
- 解决:
-
- 确认SSID和密码正确
-
- 确保WiFi是2.4GHz(ESP32不支持5GHz)
-
- 检查WiFi信号强度
-
- 查看串口输出的错误信息
- 问题3: 配置丢失
- 解决:
-
- 检查NVS分区是否正确
-
- 确保调用了saveWiFiConfig()
-
- Flash可能已损坏,尝试重新烧录
- 问题4: BLE连接不稳定
- 解决:
-
- 减小手机与ESP32的距离
-
- 避免WiFi和BLE同时高负载使用
-
- 调整BLE连接参数
*/
- 调整BLE连接参数
更多推荐



所有评论(0)