ESP32 实战教程:做一个定时唤醒的低功耗 BLE 传感器

本文由 AI 协助整理。低功耗数值必须以整机实测为准,开发板上的 USB 转串口、稳压器、LED 和传感器都会增加耗电。深度睡眠唤醒后通常从启动流程重新开始,不能假设程序会从睡眠前下一行继续。

ESP32 低功耗传感器周期

低功耗的核心不是“少 delay 一会儿”

一个电池传感器如果一直运行 WiFi、持续刷新屏幕和每秒打印串口,换再大的电池也会很快失去耐心。低功耗设计通常是另一种工作节奏:大部分时间睡眠,定时醒来读取传感器,短暂广播数据,然后再次进入深度睡眠。

本教程做一个 BLE 广播节点:每隔 60 秒唤醒,读取温湿度,广播一小段环境数据,保存启动计数,再睡眠。手机或网关不需要一直连接,只要在广播窗口扫描即可。

如果业务必须让手机随时连接,就不能把 BLE 连接和深度睡眠同时当成长期状态。低功耗蓝牙的“低功耗”不代表设备可以在睡眠时维持一个完整连接,设计时要先确定是广播型节点还是连接型设备。

先测整机,而不是只看芯片参数

测试对象应包括:ESP32 模组、开发板稳压器、USB 芯片、传感器、指示灯和电源模块。开发板插着 USB 测出来的电流,不能直接等同于最终电池产品。

记录至少三种状态:

  • 唤醒采样时电流。
  • BLE 广播时电流。
  • 深度睡眠时电流。

如果每 60 秒醒来 2 秒,平均电流可以用分段能量估算;但电池寿命还会受到电池内阻、温度、稳压器静态电流和低电压保护影响。不要拿万用表普通电流档串上去就宣布得到了“精确待机功耗”,测量设备自身也可能改变系统。

硬件和广播数据

需要一块支持 BLE 的 ESP32、温湿度传感器和电池供电。广播数据可以放在 Manufacturer Data 或 Service Data 中。为了让手机工具容易查看,先采用文本或短字节结构;长期运行再优化为定长二进制。

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEAdvertising.h>
#include "esp_sleep.h"

RTC_DATA_ATTR uint32_t bootCount = 0;
constexpr uint64_t SLEEP_SECONDS = 60;

RTC_DATA_ATTR 让变量在深度睡眠唤醒后保留一部分状态,但它不是永久存储。长时间运行的累计计数若不能丢失,应考虑 NVS,并控制写入频率。

读取唤醒原因

void printWakeReason() {
  esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
  switch (cause) {
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Wake by timer");
      break;
    case ESP_SLEEP_WAKEUP_EXT0:
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial.println("Wake by external GPIO");
      break;
    default:
      Serial.println("Power-on or reset");
      break;
  }
}

每次唤醒都重新执行 setup()。把唤醒原因打印出来,有助于区分“正常定时醒来”和“电源抖动导致重启”。如果设备每隔一分钟都显示 power-on,先查供电和复位,而不是继续优化睡眠代码。

传感器初始化与采样

struct Sample {
  float temperature;
  float humidity;
  bool valid;
};

Sample readSensor() {
  Sample sample{};

  // 替换为真实传感器读取。
  sample.temperature = 24.6f;
  sample.humidity = 52.8f;
  sample.valid = isfinite(sample.temperature) &&
                 isfinite(sample.humidity) &&
                 sample.humidity >= 0.0f &&
                 sample.humidity <= 100.0f;
  return sample;
}

传感器在深度睡眠期间是否需要断电,取决于模块。可以用 MOSFET 或负载开关控制传感器电源,但要考虑启动时间、稳定时间和 GPIO 上电状态。若只是为了省几百微安,却加入一个自身消耗同样电流的电源开关,账本会显得很幽默。

创建一次性 BLE 广播

void broadcastSample(const Sample& sample) {
  BLEDevice::init("ESP32-LowPower");

  BLEAdvertising* advertising = BLEDevice::getAdvertising();
  BLEAdvertisementData data;

  char text[80];
  snprintf(text, sizeof(text),
           "v=1;boot=%lu;t=%.1f;h=%.1f;ok=%d",
           static_cast<unsigned long>(bootCount),
           sample.temperature, sample.humidity,
           sample.valid ? 1 : 0);

  data.setManufacturerData(std::string(text));
  advertising->setAdvertisementData(data);
  advertising->start();
  delay(1500); // 给扫描器留出发现窗口
  advertising->stop();
  BLEDevice::deinit(true);
}

广播窗口不宜无限延长。手机扫描通常不是持续监听,过短可能错过,过长又浪费电。实际项目可以根据扫描器周期、网关部署和环境干扰调整,并给数据增加设备短 ID、协议版本和序号。

不同 BLE 库版本对 setManufacturerData、广告对象生命周期和 deinit 的行为可能略有差异。编译时以当前 Arduino-ESP32 BLE 示例为准;如果设备长时间运行后内存异常,优先检查 BLE 对象是否每次唤醒都正确释放。

进入深度睡眠

void sleepNow() {
  Serial.flush();
  esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * 1000000ULL);
  Serial.printf("Sleep for %llu seconds\n",
                static_cast<unsigned long long>(SLEEP_SECONDS));
  esp_deep_sleep_start();
}

void setup() {
  Serial.begin(115200);
  delay(100);
  ++bootCount;
  printWakeReason();

  Sample sample = readSensor();
  if (sample.valid) {
    broadcastSample(sample);
  }

  sleepNow();
}

void loop() {}

进入深度睡眠后,loop() 不会像普通程序一样继续运行。唤醒后芯片重新启动,RTC_DATA_ATTR 变量是少量可保留状态,不是整个应用上下文。

增加外部 GPIO 唤醒

经典 ESP32 只有 RTC GPIO 才能用于某些深度睡眠唤醒模式,具体可用脚位随芯片不同。以 GPIO33 按键为例:

#include "driver/rtc_io.h"

constexpr gpio_num_t WAKE_PIN = GPIO_NUM_33;

void enableButtonWake() {
  esp_sleep_enable_ext0_wakeup(WAKE_PIN, 1);
  rtc_gpio_pulldown_en(WAKE_PIN);
  rtc_gpio_pullup_dis(WAKE_PIN);
}

实际按键电路要避免悬空和抖动。按键唤醒后不要立刻再次进入睡眠,可以在启动阶段判断唤醒原因并进入配网、强制广播或校准模式。

广播数据中的时间和电池电压

低功耗节点很适合把电池电压放进广播,让网关提前发现电量下降:

id=env01;ver=1;seq=42;t=24.6;h=52.8;batt_mv=3970;ok=1

电池电压采样要考虑分压电阻、ADC 参考误差和采样时机。不要把 ADC 的一个原始值直接当成精确电量百分比。锂电池电压曲线也不是线性的,电量估算应结合电池类型、负载和校准数据。

低功耗调试方法

先让设备每 10 秒睡眠,确认唤醒原因、广播内容和计数都正确;再改成一分钟或更长。串口日志会唤醒或影响功耗测量,测待机时要减少串口输出并确认 USB 芯片仍是否供电。

每一轮记录:

  • 唤醒时间。
  • 传感器读取是否成功。
  • 广播持续时长。
  • 进入睡眠前电压。
  • 睡眠电流和唤醒峰值。

如果平均功耗没有下降,优先检查是不是开发板上的电源 LED、USB-UART、传感器模块和稳压器仍在吃电。软件已经睡了,硬件邻居可能还在开派对。

常见问题

现象 优先检查
唤醒后计数归零 是否使用 RTC_DATA_ATTR,是否发生彻底掉电
广播搜不到 广播窗口、手机扫描间隔、BLE 对象释放
深睡眠电流很高 开发板外围、传感器、电源模块和 GPIO 状态
外部按键无法唤醒 GPIO 是否为 RTC IO、上下拉和电平条件
传感器读数异常 上电稳定时间、供电控制和采样时机
时间间隔不准 睡眠计时误差、唤醒耗时和电源状态

总结

低功耗 BLE 传感器的工作方式是“醒来做事,完成就睡”。传感器采样、短暂广播、状态保存和唤醒配置都要围绕这个周期设计。

先测整机,确认广播窗口和数据格式,再优化传感器断电、NVS 写入与电量估算。深度睡眠不是把 delay() 换成一个函数,而是重新设计设备的生命周期。

参考资料

打赏

如果这篇学习记录对你有帮助,欢迎扫码支持后续整理。

打赏码

打赏支持:王庆岩

posted on 2026-09-28 23:43  潇洒石头  阅读(17)  评论(0)    收藏  举报

导航