最近首次接触esp8266,也是第一次接触硬件,在一个墨水屏日历项目上遇到了低功耗问题,特此记录。
此墨水屏日历不需要一直处于启动状态,我希望它每几小时或者每天启动一次即可。
解决方案 1:仅使用内部 RTC
通过将GPIO16
引脚与RST
引脚连接,在内部使用ESP.deepSleep(微秒)
进行倒计时唤醒,此方案有个很大的问题,就是8266内部的RTC非常不准确,受环境温度影响较大,每小时可能会偏差几分钟,同时,由于硬件限制,其最大只支持4294967295us,约 ~71 分钟左右,无法直接设置到预期时间再唤醒。
解决方案2:定期唤醒 MCU 并检查外部 RTC
通过外接ds3231高精度时钟来对时间进行精确控制,此方案需要频繁启动设备并读取外部RTC的时间(如每10分钟),时间未到则继续休眠,不是最优解
解决方案 3:在外部 RTC 上设置闹钟以唤醒 MCU
通过外接ds3231,并使用其闹钟功能唤醒MCU,但是在直接链接其SQW
引脚到MUC的RST
引脚上后发现,无法唤醒(中断也试过,在进入deepsleep模式后无法响应中断)
在阅读了这个页面Deep sleep wake ESP8266 with DS3231 RTC alarm后,我找到了答案
需要制作这样一个单稳态电路,小白看了也不要怕,只要认真看一看电路你也能做出来的
某宝下单:
焊接,就得到了它:
根据电路图将引脚连接好后,测试,成功。
测试代码:
#include <DS3231.h>
#include <Wire.h>
DS3231 rtc;
#define SDA_PIN D6 // 针脚定义
#define SCL_PIN D7 // 针脚定义
#define DS3231VCC D5 // ds3231 vcc针脚定义,用于控制其外部电源
void setup()
{
// Begin Serial communication
Serial.begin(115200);
pinMode(DS3231VCC, OUTPUT);
delay(100);
digitalWrite(DS3231VCC, HIGH);
delay(1000);
Wire.begin(SDA_PIN, SCL_PIN);
delay(100);
Serial.println("........");
Serial.println("........");
Serial.println("........");
DateTime now = RTClib::now();
Serial.print("现在:");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println("");
const uint32_t interval = 20; // 当前时间+20s后唤醒
DateTime currentTime;
currentTime = RTClib::now();
uint32_t currentSeconds = currentTime.unixtime();
DateTime alarmTime(currentSeconds + interval);
Serial.print("唤醒时间:");
Serial.print(alarmTime.hour(), DEC);
Serial.print(':');
Serial.print(alarmTime.minute(), DEC);
Serial.print(':');
Serial.print(alarmTime.second(), DEC);
Serial.println("");
rtc.setA1Time(
alarmTime.day(),
alarmTime.hour(),
alarmTime.minute(),
alarmTime.second(),
0x00001110, // AlarmBits = signal when the seconds match
false, // A1Dy false = A1Day means the date in the month;
// true = A1Day means the day of the week
false, // A1h12 false = A1Hour in range 0..23;
// true = A1Hour in range 1..12 AM or PM
false // A1PM false = A1Hour is a.m.;
// true = A1Hour is p.m.
);
rtc.turnOffAlarm(1); // clear the A1 enable bit in register 0Eh
rtc.checkIfAlarm(1); // clear the A1 alarm flag bit in register 0Fh
rtc.checkIfAlarm(2); // clear the A1 alarm flag bit in register 0Fh
rtc.turnOnAlarm(1); // set the A1 enable bit in register 0Eh
digitalWrite(DS3231VCC, LOW);
delay(100);
Serial.println("进入睡眠");
ESP.deepSleep(0);
}
void loop()
{
// put your main code here, to run repeatedly:
}
因此我可以通过设置ds3231的闹钟,然后进入deepsleep,时间到达后,唤醒设备,继续重复运行。
美中不足,继续优化
但是在进入deepsleep时,ds3231依然运行,这也是耗电的一部分,我需要将vcc引脚连接到GPIO上,deepsleep时将ds3231的外部供电关闭,以达到最佳低功耗效果。
但是当我兴奋的改完代码->编译->上传->运行时发现,在ds3231断电后,单靠纽扣电池是无法触发闹钟唤醒设备的
幸运的是,我又看到了这两篇文章:
DS3231 - No Alarm when powered Off. Why?
Using a $1 DS3231 Real-time Clock Module with Arduino
将ds3231的上拉电阻移除,即可在纽扣电池供电时也能触发闹钟
我只移除了图片中1的上拉电阻组,如果你想实现完全将功耗降到最低,可以将2(led)3(电池充电电阻)一同拆下。
别忘了,还需要在SQW引脚接一个10K上拉电阻(就是将一个10K电阻一头接SQW,一头接MCU供电的VCC)
像这样修改单模态电路:
这里的10K电阻在我购买的配件里正好有,接上即可
现在我可以实现在任意时间点唤醒,最长可以一个月唤醒一次。
此时就基本完成了。看一下我的项目运行日志:
2023-08-08 07:41:13
设备进入深度休眠,下次唤醒时间:2023-08-08 18:00:30(1691488830)
logTag:屏幕内容更新完成
2023-08-08 07:40:44
更新屏幕
2023-08-08 07:40:35
设备上线:{ "wakeupType": 2, "temperature": 29.75, "unixtime": 1691451635, "time": "2023-8-8 7:40:42", "lastGetdataTime": 1691424064 }
2023-08-08 00:01:13
设备进入深度休眠,下次唤醒时间:2023-08-08 07:40:30(1691451630)
2023-08-08 00:00:48
logTag:屏幕内容更新完成
2023-08-08 00:00:44
更新屏幕
2023-08-08 00:00:35
设备上线:{ "wakeupType": 2, "temperature": 30.5, "unixtime": 1691424035, "time": "2023-8-8 0:0:42", "lastGetdataTime": 1691409664 }
2023-08-07 20:01:13
设备进入深度休眠,下次唤醒时间:2023-08-08 00:00:30(1691424030)
2023-08-07 20:00:48
logTag:屏幕内容更新完成
如果您有更好的解决方案,请留言共同学习吧!