В прошлый раз(https://dzen.ru/a/Ztsk__8Fj3Gl7maD) я делал замеры потребления esp8266 и обещал написать о методах снижения энергопотребления, чему и будет посвящена данная статья.
Инструменты и метод тестирования
Для теста будут использованы:
- esp-01
- самодельный тестер на базе ina INA219
Esp-01 был выбран из-за минимальной обвязки, чтобы не добавлять лишнее потребление на тестах.
По поводу точности самодельного тестера сравнение с мультиметром HT113C.
Замер тока осуществляется до стабилизатора HT7333, поэтому на тестере всегда 5V, тогда как esp8266 питается от 3.3.
Для прошивки использовались следующие параметры.
Методы экономии
Не подключать wi-fi если не нужен.
В прошлой статье я уже писал про потребление пустого скетча, это где-то так
Если просто добавить в скетч #include <ESP8266WiFi.h>, ничего не меняется, как и при добавлении WiFi.mode(WIFI_OFF); казалось бы, модуль отключён, но откуда такое высокое потребление?
Загрузим такой скетч
#include <ESP8266WiFi.h>
void setup() {
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
}
void loop() {
// put your main code here, to run repeatedly:
}
теперь потребление стало 20,6мА, более чем в 3 раза снизилось, но за это мы заплатили отсутствием wi-fi.
Подключать wi-fi только когда требуется
загрузим скетч представленный ниже, там wi-fi включается/выключается каждые 5 секунд.
const char* ssid = "";
const char* password = "";
#include <ESP8266WiFi.h>
uint32_t tmr = 0;
void setup() {
Serial.begin(74880);
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
}
void loop() {
if (millis() - tmr >= 1000 * 5) {
if (WiFi.getMode() == WIFI_OFF) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP());
} else {
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
}
tmr = millis();
}
}
график потребления выглядит так
период низкого потребления немного смазывается, потому что программа игнорирует одинаковые точки, но это не мешает увидеть то, что мы хотим.
Если wi-fi не нужен, для достижения низкого потребления его можно выключать, а когда нужен, включать.
Экономия заряда и ускорение подключения к точки доступа
Если запомнить канал и bssid точки доступа, то подключаться можно быстрее
const char* ssid = "";
const char* password = "";
#include <ESP8266WiFi.h>
uint32_t tmr = 0;
uint8_t channel = 0;
uint8_t ap_mac[6];
void setup() {
Serial.begin(74880);
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
}
void loop() {
if (millis() - tmr >= 1000 * 5) {
if (WiFi.getMode() == WIFI_OFF) {
WiFi.forceSleepWake();
WiFi.mode(WIFI_STA);
if (channel == 0)
WiFi.begin(ssid, password);
else {
WiFi.begin(ssid, password, channel, ap_mac, true);
}
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
if (channel == 0) {
channel = WiFi.channel();
memcpy(ap_mac, WiFi.BSSID(), 6);
}
Serial.println(WiFi.localIP());
} else {
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
}
tmr = millis();
}
}
теперь скорость подключения увеличилась и вместо 11секунд, требуется 9сек. Кажется мало? Но если мы говорим про батарейное питание, экономить надо всё.
еще момент, в
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
не стоит делать длинную задержку, если здесть 100, то результат уже чуть меняется
ещё, вместо цикла можно использовать функцию WiFi.waitForConnectResult(60000), где 60000 это время в миллисекундах по умолчанию, в течение которого ожидается подключение.
Если нужен deep sleep, обратите внимание на ESP.rtcUserMemoryWrite и ESP.rtcUserMemoryRead, что позволит сохранять канал и bssid при перезагрузке контроллера, которая происходит пре выходе из режима сна. Примеры тут https://github.com/top-gun/SMT50-ESP/blob/master/Wemos/Sensor04-Truebner-SMT50.ino и https://www.bakke.online/index.php/2017/06/24/esp8266-wifi-power-reduction-avoiding-network-scan/ и ещё много где.
Использовать delay
Неожиданно, но факт https://elchupanibrei.livejournal.com/47610.html все варианты проверять не будем, обойдемся delay(1) и delay(10).
Тот же скетч, что и ранее, только в loop добавлен delay(1).
Без wifi с 21 до 18мА, с wi-fi с 71 до 70.
delay(10)
даже с моей не высокой точностью, видно, что в среднем есть ещё небольшое падение.
Легкий сон
Теперь представим, что нам нужно подключиться к wi-fi что-то сделать и заснуть, но не в глубокий сон, а лишь в легкий.
Будем тупо подключаться к wifi пинговать комп и засыпать. Библиотеку для пинга взял здесь https://github.com/dvarrel/ESPping она есть в каталоге библиотек ардуино иде.
const char* ssid = "";
const char* password = "";
#include <ESP8266WiFi.h>
#include <ESPping.h>
uint32_t tmr = 0;
uint8_t channel = 0;
uint8_t ap_mac[6];
bool is_light_sleep = false;
void setup() {
Serial.begin(74880);
Serial.println("Start");
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
}
void loop() {
WiFi.forceSleepWake();
delay(1);
WiFi.mode(WIFI_STA);
delay(1);
if (channel == 0)
WiFi.begin(ssid, password);
else {
WiFi.begin(ssid, password, channel, ap_mac, true);
}
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println();
if (channel == 0) {
channel = WiFi.channel();
memcpy(ap_mac, WiFi.BSSID(), 6);
}
Serial.println(WiFi.localIP());
Ping.ping(IPAddress(192, 168, 0, 2), 5);
Serial.println("Sleep");
delay(1);
start_light_sleep(1000 * 5);
}
void start_light_sleep(uint32_t wait_time_ms) {
is_light_sleep = true;
extern os_timer_t* timer_list;
timer_list = nullptr; // stop (but don't disable) the 4 OS timers
wifi_set_opmode(NULL_MODE);
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
wifi_fpm_set_wakeup_cb(light_sleep_wakeup);
wifi_fpm_open();
wifi_fpm_do_sleep(wait_time_ms * 1000);
esp_delay(wait_time_ms + 1, []() {
return is_light_sleep;
});
}
void light_sleep_wakeup(void) {
Serial.println("Wake");
wifi_fpm_close();
is_light_sleep = false;
wifi_fpm_set_sleep_type(MODEM_SLEEP_T);
wifi_fpm_do_sleep(0xFFFFFFF);
}
потребление в режиме легкого сна минимальное
Я не уверен что все правильно сделал, но, этот вариант единственный, что у меня получился. К примеру вариант от сюда https://github.com/esp8266/Arduino/issues/8913 или из примера https://github.com/esp8266/Arduino/blob/master/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino работает как то не так, wifi становится медленный, время подключения к точке доступа увеличивается, как и время пинга.
глубокий сон
а этих замеров не будет, мой тестер такие мелкие токи не видит, плюс esp01 без модификации не способен выйти из глубокого сна.
если wi-fi совсем не нужен
esp8266 достаточно дешевый, дешевле ардуины и её аналогов, поэтому в некоторые сценариях, считаю можно использовать его как контроллер без wi-fi.
Вот простой скетч, который ничего не делает, кроме как замера внутреннего напряжения, ожидания и сна.
#include <user_interface.h>
ADC_MODE(ADC_VCC);
RF_PRE_INIT() {
system_phy_set_powerup_option(2); // shut down the RFCAL at boot
//wifi_set_opmode_current(NULL_MODE); // set Wi-Fi working mode to unconfigured, don't save to flash
wifi_set_opmode(NULL_MODE);
wifi_fpm_set_sleep_type(MODEM_SLEEP_T); // set the sleep type to modem sleep
}
void preinit() {
wifi_fpm_open(); // enable Forced Modem Sleep, can't put this in RF_PRE_INIT or it doesn't sleep
wifi_fpm_do_sleep(0xFFFFFFF); // force modem to enter sleep mode, in RF_PRE_INIT this causes exceptions
// won't go into Forced Modem Sleep until it sees the delay(1) in setup()
}
bool is_light_sleep = false;
void setup() {
delay(1);
Serial.begin(74880);
}
// the loop function runs over and over again forever
void loop() {
Serial.println(ESP.getVcc());
delay(5000);
start_light_sleep(1000 * 5);
//ESP.deepSleep(10e6);
}
void start_light_sleep(uint32_t wait_time_ms) {
is_light_sleep = true;
extern os_timer_t* timer_list;
timer_list = nullptr; // stop (but don't disable) the 4 OS timers
wifi_set_opmode(NULL_MODE);
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
wifi_fpm_set_wakeup_cb(light_sleep_wakeup);
wifi_fpm_open();
wifi_fpm_do_sleep(wait_time_ms * 1000);
esp_delay(wait_time_ms + 1, []() {
return is_light_sleep;
});
}
void light_sleep_wakeup(void) {
wifi_fpm_close();
is_light_sleep = false;
wifi_set_opmode(NULL_MODE);
wifi_fpm_set_sleep_type(MODEM_SLEEP_T);
wifi_fpm_open();
wifi_fpm_set_wakeup_cb(NULL);
wifi_fpm_do_sleep(0xFFFFFFF);
}
если вместо легкого сна сделать глубокий, то будет еще более экономично :)
тот же скетч но на 160МГц, изменение в потребление минимальное.