Um tipo de aplicação comum é aquela em que um dispositivo realiza um procedimento apenas em um determinado horário do dia e não precisa fazer mais nada durante os outros horários. Por conta disso, é interessante deixar o dispositivo dormindo nos horários de ócio para diminui o gasto energético. Sendo assim, neste post, aprenderemos a manter o Arduino em modo sleep e acordá-lo apenas em horários predeterminados com a ajuda de um módulo RTC.
Informações básicas
O que é o projeto
Este projeto consiste na integração de um RTC ao Arduino para permitir a ele utilizar o modo sleep para dormir durante boa parte do dia e acordar apenas em horários predeterminados para executar as ações que queremos. Com isso, o objetivo é minimizar o gasto de energia do projeto. Imaginando que o projeto seja alimentado por uma bateria, o objetivo é também aumentar a duração da bateria consideravelmente.
No caso, mostrarei a proposta utilizando dois módulos RTC diferentes (imagens abaixo): o DS1302 e o DS3231. É mais tranquilo e eficiente fazer isso com o DS3231, pois ele permite a criação de alarmes que podem acionar uma interrupção externa do Arduino. Por outro lado, o DS1302 não possui alarmes, então a implementação é um pouco mais complicada, mas ainda assim bem tranquila.
Antes de iniciar o projeto, recomendo a leitura de três posts fundamenteis para ajudar no entendimento das etapas que serão apresentadas aqui:
- Post explicando sobre o modo sleep do Arduino.
- Post ensinando a usar o RTC DS1302.
- Post ensinando a usar o RTC DS3231.
Baixando bibliotecas
Tanto o RTC DS1302, quanto o DS3231, usarão as mesmas bibliotecas do Arduino. Serão duas bibliotecas: uma para lidar com o RTC e outra para lidar com o modo sleep.
Elas podem ser baixadas na própria IDE do Arduino em Sketch -> Incluir Biblioteca -> Gerenciar Bibliotecas…
- Para a biblioteca RTC, pesquise por “DS1302” e baixe a opção que tem como autor “Rtc_by_Makuna”.
- Para a biblioteca do modo sleep, pesquise por “lowpower” e baixe a opção que tem como autor “LowPowerLab”.
Acordando com RTC DS1302
Circuito
A ligação do Arduino com o DS1302 está indicada abaixo. É possível ligar os pinos CLK, DAT e RST em outros pinos digitais do Arduino. O importante é fazer a mudança no código depois.
Lógica do código
Conforme falei, o DS1302 não possui alarmes configuráveis, então teremos que fazer uma “gambiarra” para permitir ao Arduino acordar apenas no horário desejado.
Na realidade, o Arduino não acordará apenas no horário desejado, ele ficará dormindo e acordando a cada 8 segundos. Toda vez que ele acordar, ele verificará se a hora atual é igual a hora de algum horário predeterminado. Se sim, ele irá executar o procedimento desejado. Se não, ele voltará a dormir. São 8 segundos, pois este é o tempo máximo para fazer o Arduino acordar sozinho.
Comentei no início do post que o procedimento com o DS3231 é mais eficiente, pois aqui o Arduino fica acordando para verificar a hora, o que acaba não economizando tanta energia quanto poderia. Entretanto, esse processo é muito rápido (milésimos de segundos ou até menos), então, no fim das contas, podemos considerar que ele fica dormindo praticamente o tempo todo.
Código completo
Observações:
- Como exemplo, defini que o procedimento que o Arduino executa quando acorda é apenas o acionamento do LED ligado ao pino 13 (o LED que já vem na plaquinha do Arduino UNO).
- No início do código existem alguns #defines para configurar os pinos do módulo RTC e o pino do LED.
- Para configurar os horários de acordar, você precisa alterar o #define QUANTIDADE_ALARMES para a quantidade de horários que você deseja. Em seguida, basta alterar a variável “horas_alarmes” para os horários que você quer.
Leia os comentários para entender o código por 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | // ----- Bibliotecas ----- // RTC #include <ThreeWire.h> #include <RtcDS1302.h> // Sleep #include "LowPower.h" // ----- Definições ----- // Pinos do RTC #define DS1302_RST 3 #define DS1302_DAT 4 #define DS1302_CLK 5 // Quantidade de horários para acordar #define QUANTIDADE_ALARMES 6 // Pino do LED #define LED_PINO 13 // ----- Variáveis globais ----- // Instância da ligação e do RTC ThreeWire ligacao(DS1302_DAT, DS1302_CLK, DS1302_RST); RtcDS1302<ThreeWire> rtc(ligacao); // Horas que o Arduino sairá do sleep para fazer um procedimento // Defini os horários de 4 em 4 horas const uint8_t horas_alarmes[QUANTIDADE_ALARMES] = {0, 4, 8, 12, 16, 20}; // ----- Protótipo das funções ----- bool verificar_alarme(); void procedimento_acordar(); void setup () { // Inicia a comunicação serial Serial.begin(9600); // Configura o pino do LED pinMode(LED_PINO, OUTPUT); // Inicia a comunicação com o RTC rtc.Begin(); // Desabilita a proteção contra escrita if (rtc.GetIsWriteProtected()) { rtc.SetIsWriteProtected(false); } // Se não estava rodando, começa a funcionar if (!rtc.GetIsRunning()) { rtc.SetIsRunning(true); } // Obtém o valor da hora quando o código foi compilado RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); // Se a data/hora não é valida // Pode ocorrer na primeira vez que o RTC é usado // Ou então se o RTC estiver sem a bateria ou ela estiver com tensão baixa if (!rtc.IsDateTimeValid()) { Serial.println("Data e hora inválidos!"); rtc.SetDateTime(compiled); } else { // Se a hora/data for válida, mas estiver desatualizada, atualiza RtcDateTime now = rtc.GetDateTime(); if (now < compiled) { rtc.SetDateTime(compiled); } Serial.println("Horario e data atualizados!"); } } void loop () { // Verifica se está a hora atual é igual a algum horário pré-determinado if(verificar_alarme()) { Serial.println("Hora de acordar!!"); procedimento_acordar(); } // Antes de dormir, termina de enviar os dados da serial Serial.flush(); // Coloca o Arduino pra dormir por 8 segundos LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); } /** * @brief Verifica se está na hora do Arduino acordar e realizar um procedimento * @return true se está na hora e false se não */ bool verificar_alarme() { bool hora_certa = false; // Obtem o horário atual RtcDateTime now = rtc.GetDateTime(); // Verifica se está no começo da hora (minutos = 0 e segundos baixos) // Ex: 8:00:2 sim, mas 8:27:0 não if (now.Minute() == 0 && now.Second() < 8) { // Verifica se a hora bate com a hora de algum alarme for(uint8_t i = 0; i < QUANTIDADE_ALARMES; i++) { if (now.Hour() == horas_alarmes[i]) { hora_certa = true; break; } } } return hora_certa; } /** * @brief Procedimento realizado pelo Arduino quando ele está * em um horário pré-definido */ void procedimento_acordar() { // Liga LED por 1 segundo digitalWrite(LED_PINO, 1); delay(1000); digitalWrite(LED_PINO, 0); } |
Acordando com RTC DS3231
Circuito
A ligação do Arduino com o DS3231 está indicada abaixo. É possível ligar o pino SQW em outro pino digital do Arduino. Se você fizer isso, lembre-se que apenas os pinos 2 e 3 aceitam a interrupção por borda de descida/subida. Os demais só aceitam interrupção por mudança de estado. Leia mais neste post.
Lógica do código
A lógica do código é bem direta: o Arduino dorme indefinidamente e só acorda se o pino 2 receber uma borda de descida. E quando acorda, ele executa o procedimento desejado, configura o alarme para o próximo horário previsto e volta a dormir.
Código completo
Observações:
- Como exemplo, defini que o procedimento que o Arduino executa quando acorda é apenas o acionamento do LED ligado ao pino 13 (o LED que já vem na plaquinha do Arduino UNO).
- No início do código existem alguns #defines para configurar o pino de interrupção do módulo RTC e o pino do LED.
- Para configurar os horários de acordar, você precisa alterar o #define QUANTIDADE_ALARMES para a quantidade de horários que você deseja. Em seguida, basta alterar a variável “horas_alarmes” para os horários que você quer.
Leia os comentários para entender o código por 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | // ----- Bibliotecas ----- // RTC #include <Wire.h> #include <RtcDS3231.h> // Sleep #include "LowPower.h" // ----- Definições ----- // Pino interrupção #define DS3231_SQW 2 // Quantidade de horários para acordar #define QUANTIDADE_ALARMES 3 // Pino do LED #define LED_PINO 13 // ----- Variáveis globais ----- // Instância do RTC RtcDS3231<TwoWire> rtc(Wire); // Horas que o Arduino sairá do sleep para fazer um procedimento // Defini os horários de 8 em 8 horas const uint8_t horas_alarmes[QUANTIDADE_ALARMES] = {0, 8, 16}; // Informa se o alarme disparou ou não bool disparouAlarme = false; // ----- Protótipo das funções ----- void atualizar_alarme(); void procedimento_acordar(); // Rotina de interrupção do alarme void ISR_ATTR funcao_alarme() { disparouAlarme = true; } void setup () { // Inicia a comunicação serial Serial.begin(9600); // Configura o pino de interrupção pinMode(DS3231_SQW, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(DS3231_SQW), funcao_alarme, FALLING); // Configura o pino do LED pinMode(LED_PINO, OUTPUT); // Inicia a comunicação com o RTC rtc.Begin(); // Obtém o valor da hora quando o código foi compilado RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); // Se a data/hora não é valida // Pode ocorrer na primeira vez que o RTC é usado // Ou então se o RTC estiver sem a bateria ou ela estiver com tensão baixa if (!rtc.IsDateTimeValid()) { Serial.println("Data e hora inválidos!"); rtc.SetDateTime(compiled); } else { // Se a hora/data for válida, mas estiver desatualizada, atualiza RtcDateTime now = rtc.GetDateTime(); if (now < compiled) { rtc.SetDateTime(compiled); } } // Desabilita o pino de geração da onda quadrada rtc.Enable32kHzPin(false); rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); } void loop () { // Obs: O if abaixo é desnecessário, pois se o Arduino está acordado, quer dizer que o alarme disparou // deixei ele para o caso de alguém precisar usar if(disparouAlarme) { disparouAlarme = false; Serial.println("Hora de acordar!!"); procedimento_acordar(); } // Atualiza o alarme atualizar_alarme(); // Antes de dormir, termina de enviar os dados da serial Serial.flush(); // Coloca o Arduino pra dormir indefinidamente LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); } /** * @brief Atualiza o alarme para o próximo horário previsto */ void atualizar_alarme() { bool hora_certa = false; uint8_t proxima_hora = 25; uint8_t menor_hora = 23; // Obtem o horário atual RtcDateTime now = rtc.GetDateTime(); // Procura o próximo horário for(uint8_t i = 0; i < QUANTIDADE_ALARMES; i++) { // Descobre a menor hora if (horas_alarmes[i] < menor_hora) { menor_hora = horas_alarmes[i]; } // Acha o próximo horário if (horas_alarmes[i] > now.Hour() && horas_alarmes[i] < proxima_hora) { proxima_hora = horas_alarmes[i]; } } // Se não achou um horário maior, é porque o próximo horário é o menor if (proxima_hora == 25) { proxima_hora = menor_hora; } Serial.print("Próximo horário: "); Serial.println(proxima_hora); // Configura alarme 1 para disparar no início de uma hora DS3231AlarmOne alarm1( 0, proxima_hora, 0, 0, DS3231AlarmOneControl_HoursMinutesSecondsMatch); rtc.SetAlarmOne(alarm1); } /** * @brief Procedimento realizado pelo Arduino quando ele está * em um horário pré-definido */ void procedimento_acordar() { // Liga LED por 1 segundo digitalWrite(LED_PINO, 1); delay(1000); digitalWrite(LED_PINO, 0); } |