You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:57:02