12 de abril de 2021

Do bit Ao Byte

Embarcados, Linux e programação

4 Sleep Modes com ESP32

modem sleep | UART wakeup | sleep modes com ESP32

Recentemente escrevi sobre um dos modos: desabilitando (e reabilitando) somente o RF. Mas como cada situação pode exigir uma estratégia diferente, veremos nesse artigo 4 sleep modes com ESP32.

ESP32 Active Mode

O modo padrão é ativo, tudo funcionando e consumindo energia livremente. Nesse modo o consumo pode chegar aos 240mA com WiFi (o tipo de processamento influencia no consumo, como mostrado em detalhes no vídeo “Fonte de bancada“) e pode chegar aos 800mA com bluetooth junto, um display e coisinhas assim. Se precisa utilizar ambos, é bom considerar alimentação constante na tomada ou um no-break, porque quase 1Ah é realmente alto. Se for alimentar por bateria, então um desses 4 sleep modes com ESP32 deverão ser a solução para seu problema.

ESP32 Modem Sleep

Nesse modo, apenas o RF é desabilitado. Se precisa de monitoramento contínuo com baixo consumo, é o link do primeiro parágrafo que você está procurando. O resultado também foi mostrado no vídeo “Como desativar o WiFi no ESP8266“. Deveria ter iniciado a série do sleep modes com ESP32 antes de ter publicado esse artigo, mas tudo bem, continua propício.

O ESP32 somente entrará em modem sleep se estiver em modo STA (Station), sendo que nesse caso ele ficará conectado ao roteador através do mecanismo de beacon DTIM (Delivery Traffic Indicator Message). Assim, são desativados WiFi e bluetooth com intervalos de retomada para anúncio do beacon.

ESP32 Light Sleep

Nesse modo acontecem alguns procedimentos a mais do que no Modem Sleep. Partes do circuito tem pulsos de clock reduzido, no ponto suficiente para que não haja mudanças de estado nos flip-flops. Nesse modo, a CPU é pausada, enquanto o RTC e o co-processador ULP permanecem ativos.

O light sleep é padrão no ESP-IDF, mas a API do Arduino utiliza um loop infinito em main() (que por sua vez chama setup() e em um loop infinito, chama a função loop()). Ainda assim, foi possível derrubar pra <10mA o consumo do ESP32 rodando com clock de 80MHz e light sleep de 3s.

ESP32 Deep Sleep

Nesse modo, aí sim as coisas são desligadas. Periféricos, rádio, núcleos (exceto o co-processador e RTC) e boa parte da RAM são desativados. Desse modo, realmente se economiza, como mostrado mais adiante.

Provavelmente esse é o modo de trabalho ideal para quem precisa de algumas poucas conexões por dia para enviar algum dado, e pode ser também o modo de trabalho ideal para estações meteorológicas.

Os dados de conexão são armazenados na memória do RTC antes de desabilitar os periféricos. Claro que se trata de um recurso finito e limitado, mas o legal é que podemos armazenar alguma coisa em uma variável que poderá ser consultada no reinício! Para isso, siga essa dica mágica pra armazenar a variável no RTC do ESP32:

RTC_DATA_ATTR int counter = 0;//contador pra qualquer coisa

Qualquer dado que não estiver na memória do RTC será perdido, porque será como desligar um computador ou notebook; só permanece o RTC trabalhando. Se precisa guardar algum parâmetro para a retomada, siga a dica acima e lembre-se de declarar sua variável na memória do RTC.

O ESP32 também tem um suporte interessante, chamado “Deep Sleep Wake Stubs”, que é uma função que roda imediatamente após o chip levantar e “antes” de qualquer processo da inicialização normal. Seu código é carregado onde? – Nada mais, nada menos que no RTC – mais especificamente no “RTC Fast Memory”. Variáveis que sejam usadas por essa porção de código também devem ser carregados para a memória do RTC. Veremos detalhes de tudo, não se preocupe, apenas aproveite a leitura.

ESP32 Hibernation Mode

No modo de hibernação não existe a menor possibilidade de preservar qualquer tipo de dado volátil. Se for necessário utilizar esse modo e precisar armazenar algum valor, funções que façam uso do sistema de arquivos ou EEPROM devem ser proporcionadas, atuando antes de entrar em modo de hibernação. Mas existem também muitos casos que só importa coletar um conjunto de valores em sensores e enviar para a nuvem, por exemplo. Assim sendo, é um modo bastante válido para telemetria com grande dispersão, ou dispositivos de checagem periódica de longos intervalos.

No modo de hibernação, apenas o timer do RTC e alguns GPIO do RTC permanecem ativos, sendo eles responsáveis pelo retorno do modo de hibernação. Dos sleep modes com ESP32, esse é o mais profundo, mas não deve-se usar o RTC para nada.

Consumos do ESP32

Aqui temos uma tabela básica dos valores aproximados, lembrando que pode variar conforme o clock e a demanda de processamento nos modos menos econômicos.

ModoConsumo
Active Mode~240mA
Modem Sleep~3mA slow speed – ~20mA high speed
Light Sleep~0.75mA
Deep Sleep~0.15mA à 10uA
Hibernation~2.5uA

Gatilhos de wakeup

Existem diversas formas de retomar as tarefas do processador, inclusive esses gatilhos podem ser combinados. O ESP32 tem as APIs esp_sleep_enable_X_wakeup para retomada e, para tomada, esp_sleep_disable_wakeup_source(). O código de wakeup pode ser configurado a qualquer momento antes do deep ou light sleep. “Tem” que ser antes ou never more.

Uma vez que os fontes de wakeup estejam configurados, podemos entrar em sleep mode usando esp_light_sleep_start() ou esp_deep_sleep_start().

Antes de entrar em deep sleep ou light sleep, é necessário desabilitar WiFi e BT com as chamadas esp_bluedroid_disable(), esp_bt_controller_disable() e esp_wifi_stop().

Wakeup from timer

Como citado anteriormente, o RTC do ESP32 tem uma participação importante no processo, sendo um dos principais gatilhos para wakeup automático. A precisão é de microssegundos. A função esp_sleep_enable_timer_wakeup() pode ser usada para levantar a CPU de um deep sleep usando timer.

Touch pad

O módulo RTC contém uma lógica para levantar de um evento de interrupção advindo de um sensor de toque. É necessário configurar a interrupção antes de entrar em deep sleep.

A função relacionada é o esp_sleep_enable_touchpad_wakeup().

Wakeup externo (EXT0)

Usando o modo ext0, podemos usar resistores de pullup/down internos. Eles precisam ser configurados pela aplicação usando as funções rtc_gpio_pullup_en() e rtc_gpio_puuldown_en() antes de chamar esp_sleep_start().

Nas revisões 0 e 11 do ESP32 este modo de wakeup é incompatível com a ULP e com o touch. A função usada para essa fonte é esp_sleep_enable_ext0_wakeup().

O último ponto importante a considerar é que quando retornando do sleep com esse modo, o IO usado precisará ser reconfigurado, porque ele retorna como RTC IO. Para configurá-lo como um pino de IO digital, usa-se a função rtc_gpio_deinit(gpio_num). Veremos tudo em detalhes.

Outro wakeup externo (EXT1)

O RTC interno contém lógica para múltiplos gatilhos. As funções para trigger que podem ser usadas são ESP_EXT1_WAKEUP_ANY_HIGH, que levanta se qualquer um dos pinos selecionados estiver em HIGH; ou ESP_EXT1_WAKEUP_ALL_LOW, que levanta se todos os pinos selecionados estiverem em LOW.

Aqui temos algumas observações a fazer. Nesse modo podemos também desabilitar periféricos e memória, mas se os periféricos do RTC forem desabilitados, os resistores de pullup/down também o serão. Se precisa dos resistores de pullup/down para levantar a MCU, mantenha os periféricos do RTC em execução. Antes de entrar em sleep, execute:

esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); gpio_pullup_dis(gpio_num); 
gpio_pulldown_en(gpio_num);

Para ativar esse wakeup, use a função esp_sleep_enable_ext1_wakeup().

Wakeup pelo coprocessador ULP

Além dos dois núcleos do ESP32, ainda temos um coprocessador que nos permite fazer algumas interações como sondar sensores, monitorar o ADC ou tocar o valor dos sensores, então levantar quando um desses eventos específicos forem detectados. O coprocessador ULP é parte do RTC e ele roda o programa armazenado na memória lenta do RTC.

Nas revisões 0 e 1 do ESP32 o suporte a esse modo só é válido quando os periféricos do RTC não são forçados a ligar.

A função que pode ser usada com esse modo é a esp_sleep_enable_ulp_wakeup().

GPIO wakeup

Wakeup por GPIO pode ser feito em modo light sleep. Nesse modo, cada pino pode ser configurado individualmente como gatilho em nível HIGH ou LOW usando a função gpio_wakeup_enable(). Diferente de EXT0 e EXT1 citados anteriormente, esse modo de wakeup pode ser usado com qualquer IO, inclusive o RTC.

Para ativar esse modo, podemos usar a chamada esp_sleep_enable_gpio_wakeup().

UART wakeup

Esse modo também só funciona em light sleep, mas provavelmente você já havia deduzido isso no próprio tópico, já que no parágrafo anterior citei o uso de GPIO digital.

O periférico UART do ESP32 contém uma característica que permite levantar o chip do light sleep quando um certo número de bordas positivas são vistas em RX. O número de bordas positivas pode ser configurado com a função uart_set_wakeup_threshould(). Os caracteres utilizados para fazer wakeup não serão recebidos pela UART ao levantar, por isso atende-se em compor a mensagem precedida pelo gatilho. Não use, por exemplo, o primeiro byte da mensagem como gatilho, exceto se esse byte não fizer parte do que deve ser recebido.

Para ativar essa esse modo de wakeup, usa-se a função esp_sleep_enable_uart_wakeup().

Desligando periféricos e memórias do RTC

Já abordei sobre o tema mais acima, mas alguns pontos precisam ser enfatizados: são conceitos importantes que nos auxiliam na hora da implementação.

Por padrão, quando chamamos as funções esp_deep_sleep_start() e esp_light_sleep_start() todos os recursos que não forem essenciais para o wakeup pelo RTC serão desligados. “Se” for necessário mudar algo nesse comportamento, usa-se a função esp_sleep_pd_config().

Na revisão 0 do ESP32, a memória rápida do RTC sempre será mantida ativa em deep sleep. Se a aplicação não precisa de um reset limpo, esse comportamento pode ser sobrescrito.

Colocando alguma variável na memória lenta do RTC (como exemplificado mais acima), essa memória será mantida ativa por padrão.

Configurando os IOs

Essa dica é importante.

Alguns ESP32 têm pullup e pulldown internos ativos por padrão. Se um circuito externo controlar esse pino em deep sleep, o consumo de corrente pode aumentar devido ao fluxo de corrente. Para isolar um pino, prevenindo assim a drenagem de corrente, chama-se a função rtc_gpio_isolate(). A documentação exemplifica a chamada de uma maneira muito simples, em um ESP32-WROVER, no GPIO12:

rtc_gpio_isolate(GPIO_NUM_12);

Manipulação UART

O comportamento da UART dependerá do modo escolhido. Em deep sleep, todo o conteúdo dos FIFOs UART serão descartados. Quando em light sleep, esses dados serão retidos nos FIFOs e transmitidos após o wakeup.

Checar a causa do sleep wakeup

A função esp_sleep_get_wakeup_cause() pode ser usada para verificar a origem do wakeup.

Para touch pad, a função esp_sleep_get_touchpad_wakeup_status() pode ser usada.

Para EXT1, usa-se a função esp_sleep_get_ext1_wakeup_status().

Já está agendado um artigo que exemplifica essa validação em um dos sleep modes com ESP32. Acompanhe no blog porque temos artigos novos ao menos 1 vez por dia.

Desabilitar sleep wakeup

Se necessário for, para desabilitar um wakeup configurado, podemos chamar a função esp_sleep_disable_wakeup_source(), que desabilita o gatilho para uma determinada origem. Todos os gatilhos podem ser desabilitados se o argumento for ESP_SLEEP_WAKEUP_ALL.

Exemplos de sleep modes com ESP32

Nesse artigo vimos todas as possibilidades de sleep com ESP32 para podermos definir a melhor solução para nosso projeto. Tendo escolhido sua implementação desejada, siga para o exemplo de implementação:

Acompanhe os demais artigos relacionados, tem informações interessantes sobre como sair do sleep por interrupção externa, avaliar a causa do wakeup etc. Vale uns minutos de leitura!

Revisão: Ricardo Amaral de Andrade