Economizar energia é crucial em sistemas embarcados. Principalmente quando você está utilizando uma bateria com pouca capacidade de carga. Portanto, vamos aprender a utilizar a funcionalidade sleep do Arduino para amenizar este problema.
Na aula anterior, vimos como implementar as interrupções no Arduino.
Conceito
Vou utilizar o nome ‘Arduino’ como sendo o microcontrolador por conveniência, embora já tenhamos discutido a diferença dos termos na aula 1.
O modo sleep do Arduino é um estado em que ele desativa alguns recursos internos para economizar energia. Dentro deste estado, existem alguns modos diferentes para fazer a economia. E os recursos desativados são justamente alguns dos clocks utilizados pelo microcontrolador. Pois, dessa forma, o Arduino (o microcontrolador) reduz a quantidade de operações internas a cada intervalo de tempo e consome menos potência. Para exemplificar melhor, considere a tabela abaixo com os diferentes modos de sleep:
Na primeira seção da tabela, é possível ver quais clocks são desativados em cada modo. Para ter uma ideia melhor sobre o que cada clock faz, dê uma lida no datasheet do atmega328p na página 24. De acordo com a tabela, vemos também que os modos ‘Power-down’ e ‘Standby’ desativam todos os clocks listados. Portanto, são os modos que mais economizam recursos. A diferença entre os dois é que o modo ‘Standby’ mantém o oscilador funcionando (no caso de um oscilador externo).
Se o Arduino desativa os clocks, então como ele faz para acordar e voltar a funcionar normalmente? Para este caso, as interrupções continuam funcionando e são a fonte primária utilizada para acordar o microcontrolador. Isto pode ser visto na seção “Wake-up Sorces” da tabela. Existem outras formas de acordá-lo, mas vamos focar só na interrupção.
Economia de energia
A economia real de energia vai depender muito do seu código e do seu circuito. Porque um circuito que o Arduino fica 90% do tempo em modo sleep economiza bem mais do que um que ele precisa ficar acordado boa parte do tempo. Outro fator que pode influenciar é a utilização da plaquinha do Arduino, já que os elementos dela geram um gasto de energia. Por exemplo o regulador de tensão, que pode consumir cerca de 10mA.
Entretanto, para efeito de comparação, farei as medições do gasto de corrente do Arduino UNO no modo normal e no modo sleep. Farei a comparação utilizando o código blink (LED do pino 13 acende e apaga a cada 1 segundo). Adicionalmente, removerei o microcontrolador atmega328p da placa e farei as medições com o circuito igual mostrado na aula 10.
Modo | Arduino UNO | Microcontrolador à parte |
---|---|---|
Normal | 85,7mA | 10,4mA |
Sleep | 58,8mA | 0,32mA |
No caso de utilizar o microcontrolador à parte, a medição foi sem o LED. Com o LED, o modo normal teve uma corrente de 36,42mA (sem usar resistor para o LED). Os valores podem variar de um Arduino para outro, já que o meu talvez não seja o original.
Potencial de economia
Enfim, é bem perceptível a diferença de gastos entre os dois modos. Pensando em um sistema que utilize uma bateria pequena, o modo sleep pode garantir que ele dure muito mais tempo.
Considere, por exemplo, o caso de usar uma bateria de 110mAh e que o sistema fique em modo normal 10% do tempo. Para o caso acima do microcontrolador fora da plaquinha, o sistema poderia durar cerca de 28h, 9 vezes mais do que sem o sleep.
Para o cálculo anterior, considerei a equação do consumo de bateria como sendo:
C = 36,42 \times 0,1\times t + 0,32\times 0,9 \times t
Sendo t o tempo em horas. Bastou igualar C a 110 e achar o valor de t. Para o caso do gasto contínuo sem usar sleep, utilizei C=36,42t e fiz o mesmo procedimento.
Como fazer
Para demonstrar o funcionamento do recurso sleep, utilizarei a lógica do código Blink. Portanto, um LED ligado ao pino 13 fica aceso por 2 segundos e apagado por 1. A ideia é que, depois de um ciclo, o Arduino entre em modo sleep.
Biblioteca
O Arduino naturalmente não possui funções para lidar com o modo sleep. Então, é necessário importar uma biblioteca com o seguinte comando:
#include <avr/sleep.h>
Declarações iniciais
Como utilizaremos uma interrupção e um LED, temos que declará-los. E, no setup, precisamos definir cada um como saída ou entrada e ativar o pull-up na interrupção.
1 2 3 4 5 6 7 8 9 10 | #define interrupcao 2 #define led 13 void setup() { // Define o pino 2 como entrada e ativa o resistor de pull-up pinMode(interrupcao, INPUT_PULLUP); // Define o LED como saida pinMode(led, OUTPUT); } |
Lógica Blink
Para fazer a lógica do código blink, colocarei os comandos dentro do void loop. Além disso, precisamos criar uma função chamada “dormir” e chamá-la ao final da lógica de acionamento do LED.
1 2 3 4 5 6 7 8 9 10 11 | void loop() { // Código do blink digitalWrite(led, HIGH); delay(2000); digitalWrite(led, LOW); delay(1000); // Manda o Arduino dormir dormir(); } |
Função dormir
É nesta função que acontecerá toda a mágica que manda o Arduino dormir. A primeira parte é criar a função como sendo do tipo void, já que ela não retornará nada.
void dormir(){
Em seguida, precisamos de 4 comandos para fazer o modo sleep funcionar.
- set_sleep_mode(modo);
Com este comando, definimos qual será o modo sleep. E os modos existentes são: SLEEP_MODE_PWR_DOWN; SLEEP_MODE_PWR_SAVE; SLEEP_MODE_STANDBY; SLEEP_MODE_IDLE; e SLEEP_MODE_EXT_STANDBY. Os nomes são facilmente identificados com os modos da tabela mostrada no início do post. Para o nosso exemplo, utilizarei o modo ‘Power-Down’ -> SLEEP_MODE_PWR_DOWN.
- sleep_enable();
Em seguida, utilizamos o comando acima para habilitar o Arduino a dormir. Ele não manda o Arduino dormir, apenas permite que ele durma caso seja desejado.
- attachInterrupt(pino, funcao, modo);
O comando acima já foi explanado na aula anterior. Entretanto, cabem algumas ressalvas: O único modo de interrupção permitido para o sleep funcionar é o LOW (detecta quando a interrupção ocorre em nível baixo). A única exceção é o modo IDLE do sleep, que aceita outros modos de interrupção. Além disso, a função associada que utilizarei para este exemplo receberá o nome de “acordar”.
- sleep_cpu();
Por fim, o comando acima manda o Arduino dormir e o código fica parado neste ponto. O Arduino só retoma suas funções quando ocorrer a interrupção, e, nela, nós desabilitaremos o sleep.
Temos então:
1 2 3 4 5 6 7 8 9 | void dormir(){ set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); attachInterrupt(digitalPinToInterrupt(interrupcao), acordar, LOW); sleep_cpu(); } |
Veja a documentação dos comandos aqui.
Função acordar
Na função para acordar o Arduino, devemos definir ela como sendo do tipo void. E as únicas duas coisas que precisam ser feitas são: desativar a interrupção e desativar o modo sleep.
Desativamos a interrupção para que não seja possível ficar executando a função sem motivo durante o funcionamento normal do código. Enfim, o código é bem direto:
1 2 3 4 5 6 7 | void acordar(){ // Desabilita o sleep sleep_disable(); // Desabilita a interrupção detachInterrupt(0); } |
Obs.: Na função dormir, é necessário que o comando ‘sleep_enable’ esteja antes do comando que define a interrupção. Pois, em caso contrário, a interrupção pode ocorrer antes do ‘sleep_cpu’ fazendo com que a interrupção seja desvinculada. Se isso ocorrer, o Arduino entra em modo sleep e não haverá mais como tirá-lo deste modo, já que a interrupção foi desabilitada.
Circuito
Basta ligar um botão, de um lado, no GND e, do outro lado, no pino 2. Para o caso do LED, estamos utilizando o que já existe na plaquinha do Arduino e que fica vinculado ao pino 13. O circuito da aula passada é totalmente aplicável (considerando apenas 1 botão e 1 LED).
Código completo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <avr/sleep.h> #define interrupcao 2 #define led 13 void setup() { // Define o pino 2 como entrada e ativa o resistor de pull-up pinMode(interrupcao, INPUT_PULLUP); // Define o LED como saida pinMode(led, OUTPUT); } void loop() { // Código do blink digitalWrite(led, HIGH); delay(2000); digitalWrite(led, LOW); delay(1000); // Manda o Arduino dormir dormir(); } void acordar(){ // Desabilita o sleep sleep_disable(); // Desabilita a interrupção detachInterrupt(0); } void dormir(){ // Define o modo de sleep set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Habilita o sleep sleep_enable(); // Cria a interrupção com a função auxiliar acordar attachInterrupt(digitalPinToInterrupt(interrupcao), acordar, LOW); // Manda o Arduino dormir sleep_cpu(); } |
Agora você já deve ser capaz de criar um projeto que dure mais alguns dias antes de gastar toda a bateria.
Gravar dados na EEPROM do Arduino – Aula 13 – AI
Boa explicação , mas vc poderia me falar como faço para acordar ele por tempo ?
Obrigado, Gilsomario! Existem algumas formas diferentes de se fazer isto.
A mais direta é utilizando o watchdog, e aqui tem um código de exemplo. Entretanto, o watchdog tem um tempo máximo de 8 segundos, o que talvez seja pouco para sua aplicação. Mas você pode criar uma espécie de contador que fica incrementando de 8 em 8 segundos e só “acorda de fato” quando o contador atingir um certo valor.
Outra possibilidade é utilizar o timer 2, mas o tempo máximo também seria pequeno, a não ser que você utilize um clock externo de baixa frequência.
Além disto, é possível utilizar um RTC (real time clock) que gera uma interrupção em um certo horário.
De todo modo, as últimas duas soluções são mais complicadas e nunca cheguei a implementá-las. Se você estiver com dúvida quanto à ideia do “contador” no watchdog, me mande um email (mundoprojetado@gmail.com) que eu te ajudo neste ponto.
Posso usar um pir para ele dormir e acordar com movimeto no sensor ou nao?
Olá, Miguel. Pode sim, mas depende do sensor. O sensor precisa ser capaz de gerar o sinal no pino para acordar o Arduino por conta própria.
Sim o sensor de movimento fica em LOW e vai a HIGH qundo tem movimento na frente do mesmo. o problema é que vai ficar ligando e desligando, preciso por um tempo de liga e desliga e ai ja complicou pra mim.
Nesse caso daria certo então. Mas não entendi muito bem a sua ideia. Você pode tratar dos tempos do sensor assim que o sensor acordar o Arduino.
Tem alguma maneira de fazer ele entrar nesse modo e voltar a cada 15 minutos para fazer umas medições de temperatura e depois voltar a dormir? mas de forma autonoma sem que eu precise apertar um botão ou algo assim? eu estou precisando disso para um projeto mas estou quebrando a cabeça pois nunca vejo como deixar de forma automática.
Tem sim, Sandro. Entretanto, no caso do Arduino UNO, não tem como fazer isso sem componentes externos, pois você precisa de um RTC externo para gerar uma interrupção 15min após o tempo do sleep. O RTC costuma consumir pouca corrente (relativamente), então não deve ser um problema para o seu caso. A titulo de curiosidade, o esp8266 tem um RTC interno e, assim, ele consegue sair sozinho do modo deep sleep, que consome 20μA, com um tempo configurável acima de algumas horas.
Estou com o mesmo problema, você conseguiu resolver com o módulo RTC?
Olá, Edson. Não cheguei a tentar. Veja se esta discussão no forum do Arduino te ajuda em algo. Ou então esta discussão.
e se eu quiser que por exemplo 4 botões acordem o arduino ?
acho que consegui resolver … era só ligar o pino 2 em todos os botoes pin4,5,6 no meu caso .. estou certo ?
Olá, Eric. Está sim, inclusive é uma ótima solução. Uma outra forma de fazer isso seria ligando parte dos botões no pino D2 (INT0) e outra parte no pino D3 (INT1). Nesse caso seria necessário chamar a função attachInterrupt duas vezes, uma para cada pino.
obrigado
Sem problemas. Eu que agradeço o comentário.