基于ESP8266的wemos D1开发板,需要定时读取传感器数据并上报记录,由于该设备使用电池供电,所以希望最小化整个系统的功耗。
相信大家大部分的项目都不需要考虑系统功耗问题,插上插头或者电脑USB口,代码能顺利跑起来就行了。但有些时候项目需要放置在室外或者其他没有恒定供电源的条件下,系统一般采用干电池、锂电池或者太阳能等供电方式。这时候一节电池能撑一天还是一年,除了选择更大容量的电池,系统的功耗就显得至关重要了。那如果降低系统功耗呢?小编就以ESP8266开发板为例子来总结降低功耗的具体思路,希望对遇到类似问题的人有所帮助。
一个系统的功耗,即单位时间的耗电量。降低功耗跟整个系统的硬件设计和软件设计都有关系,就像肉体和灵魂一样,二者密不可分。有些人光注重精简代码,但硬件上有耗能大户,再好的代码也是徒劳;同样,再完美的硬件也经不起烂代码的折腾。所以,降低系统功耗要同时从硬件和软件两方面入手。
硬件端:精简元件,尽量选择低功耗模组和最小化系统
理论上,只要电路板一通电,板上所有的元件都在耗电,每个元件耗电量不同且能量的有效使用率也不一样,比如有的元件耗能的80%是用于干正事的,而其余的20%都以热量的形式散发掉了。最理想的情况下,我们当然希望能逐一挑选各部分的元件和封装,每个元件尽可能低功耗高效率,且能使用最少数量的元件。但由于时间、工具和能力的限制,并不是每个人都能像专业电子工程师自己定制硬件。大部分时候,爱好者们的项目都是使用市面上的公版开发板。
尽管如此,我们在选择开发板时还是有一定的选择余地的。就ESP8266而言,市面上的开发板/模组就超过了十多款。常见的如ESP-01、NodeMCU、wemos D1、D1 mini等等...即使让每一款运行相同的点灯程序,它们的功耗也不尽相同。
那具体如何挑选呢?首先我们要大致了解学习了解每款开发板的布局结构和功能特点。以wemos D1和D1 mini而言,除了大小区别,wemos D1比D1 mini多了外接电源管理部分。如果这一部分如果在你的项目中没有用到,那它就是冗余的耗能部分,应该去掉。
又比如NodeMCU,虽然跟D1 mini相似,都有串口驱动和5V转3.3V的LDO线性稳压器部分,但NodeMCU采用的LDO芯片是AMS1117,而D1 mini的LDO一般是RT9013。查看芯片的datasheet可以知道,AMS1117工作时的静态电流明显高于RT9013,这代表它的功耗也就更大。
另外,有些功能模块比如串口驱动在系统工作时并不是必须的,只有在烧录代码或者debug时才用到,那么是否可以去掉采用外接的USB转串口模块代替呢?
总之,我们可以根据自己的具体系统需求定制硬件部分,尽量做到“最少化”硬件,不多不少,让每个元件都物尽其用。但大部分时候这个过程很难做到极致,是一个不断权衡利弊的结果。
代码端:精简功能,善用睡眠模式
选定了硬件开发板,我们接下去就可以在软件端来考虑如何进一步降低功耗了。其实,总体思路也很明确:装死!能不干的活就不干,一定要干的活少干,没活干的时候就睡觉。
首先多看芯片手册,具体了解系统芯片有没有提供实现低功耗的功能。以ESP8266芯片为例,官方就提供了三种非常实用的低功耗运行的睡眠模式来满足不同的需求:
Modem-sleep
Modem-sleep自动模式是ESP8266在WiFi STA(Station)模式下会自动开启的模式,不要我们做任何设置。在STA模式下,ESP8266连接到无线路由器或者热点,一般路由器会以一个特定时间间隔(DTIM beacon间隔)向网络广播信号,然后客户端ESP8266接受完信号就可以休眠,直到下个间隔开启再接受。因此,DTIM beacon间隔越久,ESP8266的Modem-sleep的休眠时间越久,也就越省电。但DTIM间隔时间一般是在路由器后台设置,ESP8266端并不能修改。⼀般路由器的 DTIM Beacon间隔为100 ms ~ 1000 ms。
我们也可以强制开启Modem-sleep模式,关闭WiFi射频信号,这个功能在我们不需要WiFi功能的时候非常实用。
Light-sleep
Light-sleep模式与Modem-sleep相似,不同的是,除了关闭Wi-Fi模块电路以 外,在Light-sleep模式下,还会关闭内部时钟并暂停CPU,⽐Modem-sleep 功耗更低。
Light-sleep模式也分自动和强制两种模式。自动Light-sleep可⽤于需要保持与路由器的连接,可以实时响应路由器发来的数据的场合。在未接收到命令时,CPU处于空闲状态。⽐如Wi-Fi开关的应⽤,⼤部分时间CPU都是空闲的,直到收到控制命令,CPU才需要进⾏GPIO的操作。
在强制Light-sleep模式下,CPU在暂停状态下不会响应来⾃外围硬件接⼝的信号与中断,因此需要通过外部GPIO信号将ESP8266唤醒,硬件唤醒过程⼤约为 3ms。由于Wi-Fi初始化过程还需要⼤约1ms,一般建议5ms之后再对芯⽚进⾏操作。一般代码构造如下:
//Force Light-sleep
extern "C" {
#include "gpio.h"
}
extern "C" {
#include "user_interface.h"
}
void setup() {
Serial.begin(115200);
Serial.println();
gpio_init(); // Initilise GPIO pins
}
void loop() {
...
delay(200);
Serial.println("Going to sleep now");
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); // set sleep type
wifi_fpm_open(); // Enables force sleep
wifi_enable_gpio_wakeup(GPIO_ID_PIN(12), GPIO_PIN_INTR_LOLEVEL); //set wakeup pin
wifi_fpm_do_sleep(0xFFFFFFF); // Sleep for longest possible time
delay(200);
Serial.println("Wake up");
...
}
Deep-sleep
Deep-sleep是比Modem-sleep和Light-sleep更节能的模式,它会关闭除了GPIO状态和RTC之外的所有电路来最小化电流,最低仅为20uA。如果我们用1000mA的锂电供电,那么这个系统理论上可以工作:
T = 1000mAh / 20µA = 50,000小时 = 2083 天 = 5.7 年
Deep-sleep可以通过短接GPIO16针脚和RST针脚,代码里设定特定的时间自动唤醒,以NodeMCU开发板为例,我们可以这样连接:
代码构造如下:
//Deep sleep
void setup() {
Serial.begin(115200);
Serial.setTimeout(2000);
// 等待串口初始化
while(!Serial) { }
Serial.println("I'm awake.");
Serial.println("Going into deep sleep for 20 seconds");
// 一但遇到以下方法ESP就会进入deepSleep状态
//ESP.deepSleep(time_in_us)
ESP.deepSleep(20e6); // 20e6 = 20 microseconds
}
void loop() {
}
另外,Deep-sleep模式还支持外部唤醒,可以通过外部IO在RST针脚产生一个低电平脉冲唤醒。
Deep-sleep模式非常适合传感器应⽤,或者⼤部分时间都不需要进⾏数据传输的情况。设备可以每隔⼀段时间从Deep-sleep状态醒来测量数据并上传,之后继续进⼊Deep-sleep。也可以将多个数据存储于RTC memory(RTC memory在 Deep-sleep 模式 下仍然可以保存数据),然后⼀次发送出去。
除了灵活运动以上三种睡眠模式,还有一些降低功耗的细节可以抠:
比如可以在setup()里把所有没有用到的处于OUTPUT状态的针脚脚改为INPUT状态,消除针脚上的漏电,蚊子肉再小也是肉。
另外如果项目中没有用到ESP8266的WiFi功能,那可以在setup()里通过以下代码关闭WiFi射频。
void setup() {
WiFi.mode( WIFI_OFF );
WiFi.forceSleepBegin();
delay( 1 );
...
}
至此,我们分别从硬件和代码部分介绍了如何降低ESP8266系统的总体功耗,其他Arduino平台也大同小异,希望能帮到有类似需求的朋友,遗漏不足之处也希望大佬指教。