Найти в Дзене
Mega.Animeshnik

ESP8266 экономия заряда (low current consumption)

пикча для обложки В прошлый раз(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> v
Оглавление
пикча для обложки
пикча для обложки

В прошлый раз(https://dzen.ru/a/Ztsk__8Fj3Gl7maD) я делал замеры потребления esp8266 и обещал написать о методах снижения энергопотребления, чему и будет посвящена данная статья.

Инструменты и метод тестирования

Для теста будут использованы:

  • esp-01
  • самодельный тестер на базе ina INA219

Esp-01 был выбран из-за минимальной обвязки, чтобы не добавлять лишнее потребление на тестах.

По поводу точности самодельного тестера сравнение с мультиметром HT113C.

Замер тока осуществляется до стабилизатора HT7333, поэтому на тестере всегда 5V, тогда как esp8266 питается от 3.3.

Для прошивки использовались следующие параметры.

-3

Методы экономии

Не подключать wi-fi если не нужен.

В прошлой статье я уже писал про потребление пустого скетча, это где-то так

-4

Если просто добавить в скетч #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.

-5

Подключать 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();
}
}

график потребления выглядит так

-6

период низкого потребления немного смазывается, потому что программа игнорирует одинаковые точки, но это не мешает увидеть то, что мы хотим.

Если 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сек. Кажется мало? Но если мы говорим про батарейное питание, экономить надо всё.

-7

еще момент, в

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

не стоит делать длинную задержку, если здесть 100, то результат уже чуть меняется

-8

ещё, вместо цикла можно использовать функцию 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.

-9

delay(10)

даже с моей не высокой точностью, видно, что в среднем есть ещё небольшое падение.

-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);
}

потребление в режиме легкого сна минимальное

-11

Я не уверен что все правильно сделал, но, этот вариант единственный, что у меня получился. К примеру вариант от сюда 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);
}
-12

если вместо легкого сна сделать глубокий, то будет еще более экономично :)

тот же скетч но на 160МГц, изменение в потребление минимальное.

-13

Вот и всё.