ESP8266 的浅睡眠和 GPIO 唤醒

问题描述 投票:0回答:1

我正在尝试通过 GPIO 唤醒在 ESP8266 上实现轻度睡眠。一切工作正常,除了如果唤醒后 GPIO 保持在与 WAKEUP 触发电平相反的状态,则 ESP 会因 HW WDT 复位而崩溃。同样需要一些帮助。

我想实现什么目标: 我有一个连接到流量计的 ESP。流量计转动时发送高/低脉冲。我需要的是能够使 ESP 浅睡眠并在流程再次开始时将其唤醒。为此,我将流量计输出连接到 ESP 上的 WAKE PIN,以便在流量开始时唤醒 ESP。由于我不知道睡眠开始时流量计输出是低电平还是高电平,所以我读取了 GPIO,然后决定是否将 WAKEUP 触发器配置为

GPIO_PIN_INTR_LOLEVEL
GPIO_PIN_INTR_HILEVEL

有什么问题: 如果 ESP 在 GPIO 为低电平时休眠,那么它必须在高信号时唤醒,它会这样做,但如果流量计在高信号处停止,则 ESP 在大约 7 秒后崩溃。无论唤醒后已经过了多长时间,这种情况都会发生。如果由于任何原因 GPIO(连接到流量计)将停止并保持在高电平,ESP 将在 7 秒内崩溃。如果它在 GPIO 为高电平时休眠,然后在低电平时唤醒,则同样适用,如果 GPIO 随时变为低电平,则硬件看门狗定时器复位将导致崩溃。 我在blog上读到,浅睡眠唤醒只能在脉冲触发器上工作,所以如果是这样的话,这是有道理的,但我仍然无法接受这个GPIO不能在任何时候处于特定状态的事实唤醒后的程序中。

我尝试过什么: 在我意识到这是崩溃的原因之前,我尝试了很多事情,所以我不会详细讨论这些,但将其仅限于这个原因。

  • 我尝试在醒来后放置
    wifi_fpm_close();
    ,但没有解决问题。
  • ESP 唤醒后,我尝试再次休眠 1 毫秒,并配置另一个未使用的 GPIO。这也不能解决问题。
  • 我已经尝试了代码中的所有其他相关操作,例如 WiFi off 、turnign OFF espnow 等,但所有这些都与此问题无关。没有WiFi的简单草图也有这个问题。
  • 在我的测试设置中,我用按钮开关(连接到 GPIO)替换了流量计。如果在任何时候我将开关保持在与唤醒级别相反的位置,它就会崩溃。
  • 另一种解决方案是构建一些硬件来感应脉冲并仅提前发送一个脉冲。我可以解决这个问题,但前提是我知道没有软件解决方案。

崩溃时输出

19:15:39.318 >  ets Jan  8 2013,rst cause:4, boot mode:(3,7)
19:15:39.321 >
19:15:39.321 > wdt reset
19:15:39.321 > load 0x4010f000, len 3424, room 16
19:15:39.328 > tail 0
19:15:39.329 > chksum 0x2e
19:15:39.329 > load 0x3fff20b8, len 40, room 8
19:15:39.331 > tail 0
19:15:39.331 > chksum 0x2b
19:15:39.334 > csum 0x2b
19:15:39.334 > v00044c80
19:15:39.336 > ~ld

删除所有不相关内容的代码

#define DEBOUNCE_INTERVAL 10 //debouncing time in ms for interrupts
#define IDLE_TIME 15 //300 //idle time in sec beyond which the ESP goes to sleep , to be woken up only by a pulse from the meter
#define PULSE_PIN                4  // defines the pin to which the encoder is connected
#define WAKEUP_PIN               14  // defines the pin to which the wakeup signal is connected, in this case it is the same as the encoder pin 
// ************ HASH DEFINES *******************

#include <Arduino.h>
#include "secrets.h"
#include "Debugutils.h"
#include <ESP8266WiFi.h>
#include <user_interface.h> // for RTC memory functions

// ************ GLOBAL OBJECTS/VARIABLES *******************
unsigned int pulses = 0; //stores the no of pulses detected by the sensor, it is reset after SENSOR_UPDATE_INTERVAL
unsigned long last_sleep_time = millis();//stores the no of ms since a message was last published , used to sleep the ESP beyond a certain value
unsigned long idle_time = millis();
volatile unsigned long lastMicros;
// ************ GLOBAL OBJECTS/VARIABLES *******************

void IRAM_ATTR pulseHandler() {
  if((long)(micros() - lastMicros) >= DEBOUNCE_INTERVAL * 1000) {  //note DEBOUNCE_INTERVAL is in ms , so multiply by 1000 for microsec
    pulses += 1;
    lastMicros = micros();
  }
}

void light_sleep(){
    WiFi.mode(WIFI_OFF);  // you must turn the modem off; using disconnect won't work
    // extern os_timer_t* timer_list;
    // timer_list = nullptr;  // stop (but don't disable) the 4 OS timers    
    wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
    bool wakeup_pin_state = digitalRead(WAKEUP_PIN);
    gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKEUP_PIN), wakeup_pin_state? GPIO_PIN_INTR_LOLEVEL:GPIO_PIN_INTR_HILEVEL);// only LOLEVEL or HILEVEL interrupts work, no edge, that's a CPU limitation
    Serial.printf("CPU going to sleep, pull WAKE_UP_PIN %s to wakeup",wakeup_pin_state?"LOW":"HIGH");Serial.flush();
    wifi_fpm_open();
    wifi_fpm_do_sleep(0xFFFFFFF);  // only 0xFFFFFFF, any other value and it won't disconnect the RTC timer
    delay(10);  // it goes to sleep during this delay() and waits for an interrupt
    Serial.println(F("Woke up!"));  // the interrupt callback hits before this is executed*/
    wifi_fpm_close();  // Disable force sleep mode
    //sometimes the above statement gets printed before actually sleeping off, doesnt happen all the time though
    //But the code works as expected, the ESP sleeps after printing Woke up
 }

void setup() {
  Serial.begin(115200);
  pinMode(WAKEUP_PIN, INPUT_PULLUP);//INPUT_PULLUP
  pinMode(PULSE_PIN, INPUT_PULLUP);
  attachInterrupt(PULSE_PIN, pulseHandler, RISING);

  Serial.println("Setup complete");
}

void loop() {
    idle_time = millis() - last_sleep_time;
    if(idle_time >= IDLE_TIME * 1000)
    {
      Serial.print("going to sleep after being idle for :"); Serial.print(idle_time/1000);Serial.println(" sec");Serial.flush();
      light_sleep();
      //now that we're awake , add the sleep time to the previous value
      last_sleep_time = millis();
    }
    yield();

  }
interrupt esp8266 watchdog sleep-mode wakeup
1个回答
0
投票

如果硬件解决方案可以接受,请使用外部单稳态多谐振荡器(例如单稳态模式下的 555 定时器)将信号转换为固定持续时间的脉冲。这确保唤醒信号始终是瞬态的,避免崩溃。

您可以在此处查看更多参考。 https://www.theengineeringprojects.com/2016/02/555-timer-projects.html

您可以在 GPIO 引脚和地之间使用 0.1 µF 等小电容器。这将创建一个低通滤波器,平滑过渡并确保短暂的脉冲而不是持续的电平。这可能会阻止 GPIO 停留在触发看门狗的状态。

© www.soinside.com 2019 - 2024. All rights reserved.