基于 ESP32-WROOM-32 实践 BLE 服务器主动通知 Notify – PlatformIO

最近更新于 2026-07-26 19:39

测试环境

  • ESP32-WROOM-32
  • PlatformIO IDE 3.3.4
  • PlatformIO Core 6.1.19
  • pioarduino platform-espressif32 55.03.311(Arduino Release v3.3.11 based on ESP-IDF v5.5.5)

platformio.ini

[env:esp32dev]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.311/platform-espressif32.zip
board = esp32dev
framework = arduino
monitor_speed = 115200

实践

源码参考来源:https://github.com/espressif/arduino-esp32/blob/3.3.11/libraries/BLE/examples/Notify/Notify.ino

/*
    功能概述:
    创建一个 BLE 服务器,当有手机/客户端建立连接后,定期(每 0.5 秒)主动向手机推送递增数据包 (Notify)。
    服务的广播 UUID 为: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
    特征值 UUID 为: beb5483e-36e1-4688-b7f5-ea07361b26a8

    架构步骤:
    1. 创建 BLE Server(服务器)
    2. 创建 BLE Service(服务)
    3. 在服务下创建 BLE Characteristic(特征值)
    4. 为特征值添加 BLE Descriptor(描述符)
    5. 启动服务
    6. 开启广播

    当与服务器关联的连接回调函数监测到连接时,将配合主循环定期触发数据通知。
*/

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h> // 提供 0x2902 描述符(客户端特征配置描述符 CCCD,用于使能 Notify/Indicate)
#include <BLE2901.h> // 提供 0x2901 描述符(特征值用户描述符,用于显示文本名称)

// 全局指针与变量定义
BLEServer *pServer = NULL;
BLECharacteristic *pCharacteristic = NULL;
BLE2901 *descriptor_2901 = NULL;

bool deviceConnected = false;    // 当前手机是否处于连接状态
bool oldDeviceConnected = false; // 上一次循环时的连接状态(用于边缘触发判定)
uint32_t value = 0;              // 定时推送的 32 位递增数值

// 可以通过访问 https://www.uuidgenerator.net/ 生成你自己的 UUID
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

/**
 * @brief BLE 服务器连接状态监听回调类
 */
class MyServerCallbacks : public BLEServerCallbacks
{
    // 当客户端成功连接到 ESP32 时触发
    void onConnect(BLEServer *pServer) override
    {
        deviceConnected = true;
    };

    // 当客户端与 ESP32 断开连接时触发
    void onDisconnect(BLEServer *pServer) override
    {
        deviceConnected = false;
    }
};

void setup()
{
    Serial.begin(115200);

    // 1. 初始化 BLE 设备并设置广播名称
    BLEDevice::init("ESP32");

    // 2. 创建 BLE 服务器,并绑定连接/断开事件的回调函数
    pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks());

    // 3. 在服务器中创建自定义 GATT 服务
    BLEService *pService = pServer->createService(SERVICE_UUID);

    // 4. 在服务中创建特征值,并开启多种权限:
    // 可读 (READ) | 可写 (WRITE) | 可通知 (NOTIFY) | 可指示 (INDICATE)
    pCharacteristic = pService->createCharacteristic(
        CHARACTERISTIC_UUID,
        BLECharacteristic::PROPERTY_READ |
        BLECharacteristic::PROPERTY_WRITE |
        BLECharacteristic::PROPERTY_NOTIFY |
        BLECharacteristic::PROPERTY_INDICATE);

    // 5. 添加 0x2902 描述符 (Client Characteristic Configuration Descriptor - CCCD)
    // 这是 BLE 标准协议要求的:手机端必须通过写入 0x2902 描述符来开启/关闭 ESP32 的 Notify 推送权限
    // 注:如果未来改用 NimBLE 库,NimBLE 会根据特征值属性自动添加此描述符,无需手动 new
    pCharacteristic->addDescriptor(new BLE2902());

    // 6. 添加 0x2901 描述符 (Characteristic User Description)
    // 用于给这个特征值附带一段人性化的文本说明,手机调试 App 打开后可以直接看到这段文字
    descriptor_2901 = new BLE2901();
    descriptor_2901->setDescription("My own description for this characteristic.");
    descriptor_2901->setAccessPermissions(ESP_GATT_PERM_READ); // 设置该描述符为只读(默认是可读写)
    pCharacteristic->addDescriptor(descriptor_2901);

    // 7. 启动 GATT 服务
    pService->start();

    // 8. 配置并开启广播
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(false);
    pAdvertising->setMinPreferred(0x0); // 设置为 0x00 表示不在广播包中指定推荐连接间隔参数

    BLEDevice::startAdvertising();
    Serial.println("Waiting a client connection to notify...");
}

void loop()
{
    // -------------------------------------------------------------
    // 状态 1:手机当前处于已连接状态 -> 持续推送递增数据
    // -------------------------------------------------------------
    if (deviceConnected)
    {
        // 将 uint32_t 类型的变量 value 转换为无符号 8 位指针格式,推送 4 个字节长度的数据
        pCharacteristic->setValue((uint8_t *)&value, 4);

        // 向已订阅 notification 的客户端主动发送数据变更通知
        pCharacteristic->notify();

        value++;    // 数据递增
        delay(500); // 每 500 毫秒 (0.5秒) 推送一次
    }

    // -------------------------------------------------------------
    // 状态 2:检测到“刚断开连接”的瞬间(下降沿触发)
    // -------------------------------------------------------------
    if (!deviceConnected && oldDeviceConnected)
    {
        delay(500);                  // 给 ESP32 蓝牙协议栈留出处理底层资源释放的缓冲时间
        pServer->startAdvertising(); // 重新开启广播,方便其他或原设备再次连接
        Serial.println("start advertising");

        // 同步状态,防止此 if 分支在下一次 loop 中被重复执行
        oldDeviceConnected = deviceConnected;
    }

    // -------------------------------------------------------------
    // 状态 3:检测到“刚建立连接”的瞬间(上升沿触发)
    // -------------------------------------------------------------
    if (deviceConnected && !oldDeviceConnected)
    {
        // 连接建立的瞬间可以在这里编写一次性的初始化逻辑(如复位计数器、发送欢迎包等)

        // 同步状态,防止此 if 分支在下一次 loop 中被重复执行
        oldDeviceConnected = deviceConnected;
    }
}

虽然服务器开启了主动通知,但是客户端如果不订阅就不会强推,要点进去开启 Notify
file

这样就可以看到值实时更新了

基于 ESP32-WROOM-32 实践 BLE 服务器主动通知 Notify – PlatformIO
Scroll to top
打开目录