Criar interrupções ou ativar o modo sleep pode deixar seu projeto com um melhor funcionamento dependendo da aplicação necessária. Portanto, vamos aprender a utilizar as interrupções e como ativar o modo sleep no NodeMcu.
Na aula anterior, aprendemos a controlar o NodeMcu por um aplicativo Android.

Informações importantes

Interrupções

As interrupções são bem úteis para otimizar a programação e reduzir certar verificações desnecessárias do código. Na aula 11 do curso do Arduino, expliquei em detalhes sobre as interrupções e suas vantagens. Portanto, recomendo a leitura do post.

Entretanto, explicando resumidamente seu funcionamento, temos que: a interrupção é um evento que ocorre no microcontrolador quando certa condição é cumprida. Esse evento é basicamente uma interrupção, de fato, no programa principal que passa para uma função secundária, que é a função de interrupção. O motivo da interrupção pode ser mais de um, mas o mais comum é ela ocorrer por causa de uma mudança no estado de um pino digital.

Ou seja, se um pino digital passou de nível alto para baixo, por exemplo, o programa é interrompido praticamente imediatamente e a função de interrupção é executada.

Modo sleep

O modo sleep é extremamente necessário em aplicações onde o microcontrolador fica um tempo ocioso sem executar nada importante. Novamente, na aula 12 do curso do Arduino, expliquei em detalhes sobre o modo sleep e suas finalidades. Portanto, recomendo a leitura do post.

De qualquer forma, explicando resumidamente seu funcionamento: o modo sleep é um estado do microcontrolador em que ele “hiberna” e deixa de executar algumas de suas rotinas internas. Logo, o microcontrolador deixa de consumir tanta potência quanto o normal e há uma redução no gasto de energia.

Ou seja, podemos utilizar o modo sleep para economizar a energia de baterias que alimentam um sistema.

Interrupções

Comandos

O primeiro passo para definir uma interrupção é configurar o pino como interrupção. Isso pode ser feito configurando ou não o resistor de pull-up interno:
1
2
3
gpio.mode(pino, gpio.INT) -- Sem pull-up

gpio.mode(pino, gpio.INT, gpio.PULLUP) -- Com pull-up interno
Após isto, basta utilizar o seguinte comando com os parâmetros adequados:
1
gpio.trig(pino, tipo, função_associada)

Sendo os parâmetros:

  • pino: pino responsável pela interrupção externa. O pino 0 não pode receber interrupções.
  • tipo: tipo da interrupção. Dentre os tipos, temos:
    • “up”: A interrupção ocorre em uma borda de subida.
    • “down”: A interrupção ocorre em uma borda de descida.
    • “both”: A interrupção ocorre em ambas as bordas de subida e descida.
    • “low”: A interrupção ocorre em nível baixo.
    • “high”: A interrupção ocorre em nível alto.
  • função_associada: função que será executada quando a interrupção ocorrer.

Portanto, é preciso configurar o pino como interrupção, criar uma função para a interrupção e associar a interrupção à função criada com o comando acima. Agora, vamos a um exemplo de como fazer isso.

Exemplo

Para explicar como fazer as interrupções no NodeMcu, vou considerar o caso de comutar um LED (ligado no pino 2) quando uma interrupção (borda de descida) ocorrer no pino 1.

Circuito

Para o circuito, será necessário ligar o LED no pino 2 em série com um resistor (220Ω). No pino 1, iremos conectar um botão, que está ligado ao GND. Como vamos definir o pull-up pela programação, não será necessário adicionar um resistor ao circuito do botão.
Circuito de interrupção com NodeMcu

Programação

Não pretendo explicar o código, pois os comentários do código devem ser suficientes para entender o procedimento. Qualquer coisa releia a parte dos comandos para entender o que cada um faz e o que é cada parâmetro.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- Cria variáveis associadas aos pinos
pino_int = 1
led1 = 2

gpio.mode(pino_int, gpio.INT, gpio.PULLUP) -- Define o pino de interrupção como uma interrupção e ativa o pull-up
gpio.mode(led1, gpio.OUTPUT) -- Define o pino do led como saída

-- Cria a função associada à interrupção
function interrupcao()
    gpio.write(led1, 1-gpio.read(led1)) -- Comuta o estado do LED
    tmr.delay(1000 * 100) -- Delay de 100ms para gerar atraso
end

-- Cria a interrupção
gpio.trig(pino_int, "down", interrupcao)
Obs: nos testes que fiz, o pino de interrupção foi extremamente sensível à flutuações (sem usar o pull-up). Portanto, tenha certeza que o pull-up interno esteja habilitado, ou então adicione um resistor de pull-up ao circuito (ou pull-down dependendo do caso).

Modo sleep

Comandos

Para colocar o NodeMcu em modo sleep, existem dois comandos: node.sleep(), que é para o modo “light sleep” (sono leve); e node.dsleep(), que é para o modo “deep sleep” (sono profundo). A diferença entre os dois é que, o primeiro pode ser configurado para sair do sleep a partir de uma interrupção, enquanto o segundo só sai do sleep depois de um tempo definido. Além disso, teoricamente, o segundo comando abaixa mais o consumo de corrente do que o primeiro.

Os comandos são:

1ª opção

1
node.sleep(pino, tipo_int, funçao_wifi, preservar_modo)

Os parâmetros deste primeiro comando são:

  • pino: pino associado à interrupção.
  • tipo_int: tipo de interrupção, podendo ser:
    • node.INT_UP: A interrupção ocorre em uma borda de subida.
    • node.INT_DOWN: A interrupção ocorre em uma borda de descida.
    • node.INT_BOTH: A interrupção ocorre em ambas as bordas de subida e descida.
    • node.INT_LOW: A interrupção ocorre em nível baixo.
    • node.INT_HIGH: A interrupção ocorre em nível alto.
  • função_wifi: função para executar quando o WiFi sai da suspensão.
    • Esta função é opcional e pode ser desabilitada e ignorada com o próximo parâmetro.
  • preservar_modo: preserva o modo do WiFi definido atualmente. Se for ‘false’ descarta o modo definido pelo WiFi. Se for ‘true’, apenas mantem o estado antigo.

2ª opção

1
node.dsleep(tempo, opção, instante)

Os parâmetros deste segundo comando são:

  • tempo: tempo em microssegundos que o microcontrolador ficará em modo sleep.
  • opção: muda a forma como ficará a corrente depois que o microcontrolador “acordar”. Os principais valores estão listados abaixo:
    • ‘1’ executa RF_CAL e a corrente será alta depois de “acordar”.
    • ‘2’ não executa RF_CAL e a corrente será baixa depois de “acordar”.
    • ‘4’ desabilita o RF_CAL e a corrente será a mais baixa depois de “acordar”.
  • Instante: define se o microcontrolador entrará imediatamente em modo sleep caso o valor seja numérico diferente de zero. Se for ‘nil’ ou zero, o sleep esperará o módulo Wi-Fi desligar.

Depois que o NodeMcu “acordar” depois do comando acima ser utilizado, ele é resetado. E, para o comando funcionar, o pino RST e o pino D0 (GPIO16) devem estar interligados. Entretanto, quando os dois estão interligados o botão de reset para de funcionar, então fique atento.

Considerações

Portanto, é preciso apenas usar um dos dois comandos acima de acordo com a forma como você deseja que o NodeMcu “acorde”.

Observação importante: não consegui fazer o primeiro comando funcionar, pois é necessário criar uma build do firmware com certos parâmetros habilitados. Esse processo é bem trabalhoso, mas, quem tiver interesse, é só usar este link para baixar os acessórios necessários para criar a build customizada. Na build customizada, você deve alterar o arquivo “app/include/user_config.h” descomentando as linhas que tiverem definindo os parâmetros: “PMSLEEP_ENABLE” e “ENABLE_TIMER_SUSPEND”.

Exemplo

Como o primeiro comando exige um processo complicado para ser habilitado, vou mostrar um exemplo com o comando dsleep. A lógica do programa será acender um LED ligado ao pino 1 por dois segundos e deixá-lo apagado por 1 segundo. Após isto, o NodeMcu entrará no modo de deep sleep.

Programação

Não pretendo explicar o código, pois os comentários do código devem ser suficientes para entender o procedimento. No caso do comando node.dsleep(), só configurei o primeiro parâmetro, pois os outros dois não são de interesse.

1
2
3
4
5
6
7
8
9
10
-- Cria variável associada ao pino do LED
led1 = 1

gpio.mode(led1, gpio.OUTPUT) -- Define o pino do led como saída

gpio.write(led1, 1) -- Liga o LED
tmr.delay(1000 * 2000) -- Delay de 2 s
gpio.write(led1, 0) -- Desliga o LED
tmr.delay(1000 * 1000) -- Delay de 1 s
node.dsleep(5000000) -- Entra no modo sleep por 5 segundos

Obs.: Não recomendo colocar o programa acima como o init.lua (código que inicia automaticamente toda vez), pois o NodeMcu pode ficar travado.

Resultados

Depois de fazer upload do código, alimentei o NodeMcu com uma fonte externa e medi a corrente que passava por ele durante a execução do código. Os resultados obtidos podem ser vistos na tabela abaixo:

ModoCorrente
Normal84.6mA
Sleep10.06mA

As medições acima foram sem o LED (na hora que ele estava desligado). Portanto, foi percebido uma redução de cerca de 88% do valor total da corrente. Ou seja, poderia aumentar a duração de uma bateria em até 8 vezes.