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:

tabela dos modos de sleep
Fonte: Datasheet do Atmega328p (pág 34)

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