ESP32与9DoF传感器深度睡眠运动唤醒阈值异常问题求助
ESP32搭配LSM303深度睡眠后立即唤醒的问题修复
我正在用ESP32搭配9DoF传感器实现窗户检测功能,目前已经能让设备在无运动时进入深度睡眠,但希望设备能感知轻微运动就唤醒。调整运动阈值(比如从80改成40)后,设备进入深度睡眠会立刻唤醒,串口日志如下:
No movement detected, entering deep sleep... ets Jul 29 2019 12:21:46 rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:2 load:0x3fff0030,len:1184 load:0x40078000,len:13232 load:0x40080400,len:3028 entry 0x400805e4 Woke up
我的原始代码如下:
#include <Arduino.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_LSM303_U.h> #include <Adafruit_9DOF.h> #include <esp_sleep.h> #include <WiFi.h> #include <LSM303.h> /* 传感器初始化 */ Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301); Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(30302); LSM303 lsm; // 9DOF对象 // 运动检测参数 float movementThreshold = 0.05; // 运动检测阈值 sensors_event_t lastAccelEvent; sensors_event_t lastMagEvent; unsigned long noMovementDuration = 0; const unsigned long noMovementThreshold = 2000; // 2秒无运动进入睡眠 // 引脚定义 const int sda = 21; const int scl = 22; const int intPin = 15; // LSM303中断引脚 // 运动检测函数 bool detectMovement(sensors_event_t *currentEvent) { float deltaY = abs(currentEvent->acceleration.y - lastAccelEvent.acceleration.y); float deltaZ = abs(currentEvent->acceleration.z - lastAccelEvent.acceleration.z); return (deltaY > movementThreshold || deltaZ > movementThreshold); } void setup() { Serial.begin(115200); Serial.println("Woke up"); // I2C初始化 Wire.begin(sda, scl); delay(500); // LSM303初始化 if (!lsm.init()) { Serial.println("Failed to initialize LSM303. Check your wiring!"); while (1); } // 传感器初始化 if (!accel.begin()) { Serial.println(F("Ooops, no LSM303 accel detected ... Check your wiring!")); while (1); } if (!mag.begin()) { Serial.println("Ooops, no LSM303 mag detected ... Check your wiring!"); while (1); } // LSM303加速度计配置 lsm.writeAccReg(LSM303::CTRL_REG1_A, 0x27); // 正常模式,50Hz,所有轴激活 lsm.writeAccReg(LSM303::CTRL_REG2_A, 0x00); // 禁用高通滤波器 lsm.writeAccReg(LSM303::CTRL_REG3_A, 0b01000100); // INT1中断启用 lsm.writeAccReg(LSM303::CTRL_REG4_A, 0x00); // ±2g灵敏度 lsm.writeAccReg(LSM303::INT1_CFG_A, 0b00101010); // Z轴上下阈值中断激活 lsm.writeAccReg(LSM303::INT1_THS_A, 80); // 运动阈值,~16mg每LSB lsm.writeAccReg(LSM303::INT1_DURATION_A, 0x00); // 中断持续时间,0ms // ESP32深度睡眠配置 esp_sleep_enable_ext0_wakeup(GPIO_NUM_15, 1); // INT1高电平唤醒 WiFi.mode(WIFI_OFF); // 关闭WiFi btStop(); // 关闭蓝牙 setCpuFrequencyMhz(80); // 降低CPU频率 // 保存初始传感器值 accel.getEvent(&lastAccelEvent); mag.getEvent(&lastMagEvent); } void loop() { // 读取新传感器值 sensors_event_t magEvent; mag.getEvent(&magEvent); sensors_event_t accelEvent; accel.getEvent(&accelEvent); // 运动检测 if (detectMovement(&accelEvent)) { // 检测到运动:输出数据 Serial.println(F("Movement detected! Sensor data:")); Serial.print(F("ACCEL X: ")); Serial.print(accelEvent.acceleration.x); Serial.print(F(" Y: ")); Serial.print(accelEvent.acceleration.y); Serial.print(F(" Z: ")); Serial.println(accelEvent.acceleration.z); Serial.print(F("MAG X: ")); Serial.print(magEvent.magnetic.x); Serial.print(F(" Y: ")); Serial.print(magEvent.magnetic.y); Serial.print(F(" Z: ")); Serial.println(magEvent.magnetic.z); Serial.println(); // 更新最后值 lastAccelEvent = accelEvent; lastMagEvent = magEvent; // 检测到运动,保持唤醒 noMovementDuration = 0; delay(1000); // 输出延迟 } else { // 未检测到运动 noMovementDuration += 1000; delay(1000); // 进入深度睡眠 if (noMovementDuration >= noMovementThreshold) { Serial.println(F("No movement detected, entering deep sleep...")); esp_deep_sleep_start(); } } }
问题根源与修复方案
1. 中断引脚电平不匹配
你设置ESP32用高电平唤醒,但LSM303的INT1引脚默认是低电平有效。如果传感器中断触发后引脚保持低电平,或者进入睡眠前已经触发了中断,就会导致唤醒条件一直满足。
修复:
- 要么修改LSM303的中断引脚极性为高电平有效:
lsm.writeAccReg(LSM303::CTRL_REG3_A, 0b01000000); // 仅开启INT1高电平有效 - 要么把ESP32的唤醒触发改成低电平:
esp_sleep_enable_ext0_wakeup(GPIO_NUM_15, 0);
2. 未清除传感器中断标志
LSM303触发中断后,INT1_SRC_A寄存器的标志位会保持置位状态,导致中断引脚一直处于触发电平。如果进入睡眠前不清除这个标志,ESP32会立刻被唤醒。
修复:在进入深度睡眠前读取该寄存器清除标志:
// 在esp_deep_sleep_start()之前添加 lsm.readAccReg(LSM303::INT1_SRC_A);
3. 无防抖的中断触发
你设置INT1_DURATION_A为0x00,意味着只要加速度超过阈值就立刻触发中断,没有防抖时间。传感器的微小噪声都会触发中断,导致误唤醒。
修复:设置合理的防抖时长,比如2个采样周期(对应50Hz采样率下40ms):
lsm.writeAccReg(LSM303::INT1_DURATION_A, 0x02);
4. 传感器库配置冲突
你同时使用了Adafruit统一传感器库和LSM303原始库,accel.begin()会覆盖你手动写入的寄存器配置,导致中断设置失效。
修复:移除Adafruit统一库的初始化代码,只用LSM303原始库完成配置和数据读取。
修复后的完整代码
#include <Arduino.h> #include <Wire.h> #include <esp_sleep.h> #include <WiFi.h> #include <LSM303.h> /* 传感器初始化 */ LSM303 lsm; // 9DOF对象 // 运动检测参数 float movementThreshold = 0.05; // 运动检测阈值 float lastAccelY = 0; float lastAccelZ = 0; unsigned long noMovementDuration = 0; const unsigned long noMovementThreshold = 2000; // 2秒无运动进入睡眠 // 引脚定义 const int sda = 21; const int scl = 22; const int intPin = 15; // LSM303中断引脚 // 运动检测函数 bool detectMovement(float currentY, float currentZ) { float deltaY = abs(currentY - lastAccelY); float deltaZ = abs(currentZ - lastAccelZ); return (deltaY > movementThreshold || deltaZ > movementThreshold); } void setup() { Serial.begin(115200); Serial.println("已唤醒"); // I2C初始化 Wire.begin(sda, scl); delay(500); // LSM303初始化 if (!lsm.init()) { Serial.println("LSM303初始化失败,请检查接线!"); while (1); } // LSM303加速度计配置 lsm.writeAccReg(LSM303::CTRL_REG1_A, 0x27); // 正常模式,50Hz采样,全轴启用 lsm.writeAccReg(LSM303::CTRL_REG2_A, 0x00); // 禁用高通滤波器 lsm.writeAccReg(LSM303::CTRL_REG3_A, 0b01000000); // INT1引脚高电平有效 lsm.writeAccReg(LSM303::CTRL_REG4_A, 0x00); // ±2g量程 lsm.writeAccReg(LSM303::INT1_CFG_A, 0b00101010); // Z轴上下阈值中断启用 lsm.writeAccReg(LSM303::INT1_THS_A, 40); // 运动阈值(40*16mg=640mg) lsm.writeAccReg(LSM303::INT1_DURATION_A, 0x02); // 2个采样周期防抖 // ESP32深度睡眠配置 esp_sleep_enable_ext0_wakeup(GPIO_NUM_15, 1); // 高电平唤醒 WiFi.mode(WIFI_OFF); btStop(); setCpuFrequencyMhz(80); // 读取初始加速度值 lsm.read(); lastAccelY = lsm.a.y; lastAccelZ = lsm.a.z; } void loop() { // 读取传感器数据 lsm.read(); float currentAccelY = lsm.a.y; float currentAccelZ = lsm.a.z; // 运动检测 if (detectMovement(currentAccelY, currentAccelZ)) { Serial.println("检测到运动!传感器数据:"); Serial.print("加速度 Y: "); Serial.print(currentAccelY); Serial.print(" Z: "); Serial.println(currentAccelZ); Serial.print("磁力计 X: "); Serial.print(lsm.m.x); Serial.print(" Y: "); Serial.print(lsm.m.y); Serial.print(" Z: "); Serial.println(lsm.m.z); Serial.println(); lastAccelY = currentAccelY; lastAccelZ = currentAccelZ; noMovementDuration = 0; delay(1000); } else { noMovementDuration += 1000; delay(1000); if (noMovementDuration >= noMovementThreshold) { Serial.println("未检测到运动,进入深度睡眠..."); lsm.readAccReg(LSM303::INT1_SRC_A); // 清除中断标志 esp_deep_sleep_start(); } } }
内容的提问来源于stack exchange,提问作者user29144738
相关产品推荐
相关产品推荐

