Coletores de dados ou data loggers, como também são conhecidos, são dispositivos que efetuam a leitura (ou coleta) de variáveis de todos os tipos (em função dos sensores utilizados) e as armazenam, em intervalos de tempo definidos, em dispositivos como cartões de memória SD ou até na nuvem. Os registros armazenados constituem uma base de dados das variáveis coletadas, sempre relacionados com o instante (data e hora) da coleta. Aqui apresentaremos um dispositivo que efetuará a leitura de variáveis ambientais (temperatura, umidade relativa do ar e pressão atmosférica), as mostrará, juntamente com data e hora atuais em um display LCD e as armazenará em um cartão de memória SD.
Serão apresentados diversos conceitos importantes em projetos como este, como a leitura das informações em si e da data e hora, o armazenamento dos dados e a verificação de falha, tanto nos sensores quanto no relógio utilizado e no cartão SD, através de indicações no próprio display e também em um led bicolor.
Serão necessárias ao projeto as bibliotecas dos módulos utilizados. O procedimento de utilização (download, instalação e declaração no código) dessas bibliotecas é o normalmente utilizado.
A visualização dos dados em forma de gráfico poderá ser feita através do Excel, a partir do arquivo de extensão .csv, que o sistema armazenará no cartão SD.
Um coletor de dados precisa de, no mínimo:
Os módulos e componentes utilizados neste projeto serão descritos rapidamente a seguir:
O BME280 (não confundir com o BMP280, que efetua leituras só de temperatura e pressão atmosférica) é um sensor minúsculo (2,5 X 2,5 X 0,93mm) com interface I2C incorporada. Os endereços possíveis para o módulo (selecionáveis por um jump de solda na placa) podem ser 76h ou 77h. A folha de informações do dispositivo pode ser encontrada em: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf.
O RTC (Real Time Clock– Relógio de Tempo Real) é um dispositivo baseado no chip DS3231 que fornece data e hora com grande precisão (ver: https://blog.eletrogate.com/rtc-real-time-clock-ds1302-1307-e-3231/).
É alimentado por uma bateria independente e permanece em funcionamento mesmo com a energia que alimenta o sistema desligada. O consumo da bateria é muito pequeno, o que faz com que ela dure anos. A interface de comunicação é a I2C, no endereço 68h, mas como esse endereço é o único possível para o dispositivo, não é necessário especifica-lo, pois a biblioteca o faz automaticamente. É claro que não pode haver outro dispositivo I2C com o mesmo endereço.
Cartões SD trabalham com níveis elétricos (alimentação e sinais) da ordem de 3,3V. O módulo utilizado pode ser usado diretamente no barramento SPI do Nano (5V) e alimentado pela fonte de 5V do sistema, mas é preciso se certificar que o módulo é desse tipo antes de ligá-lo! O pino de seleção do dispositivo (chip select – CS ou SS) utilizado foi o padrão, que é o 10. Embora não fosse necessária neste projeto, foi mantida neste módulo a modificação (transistor e resistor) descrita em outro artigo publicado neste blog (ver: https://blog.eletrogate.com/utilizando-modulos-para-cartoes-sd/).
Em sistemas onde existe uma rotina computacional sendo executada, não basta um led aceso para indicar que o equipamento está energizado. É necessária a confirmação de que a rotina está efetivamente em execução. Neste projeto, um led bicolor verde e vermelho, foi usado como uma indicação simples e eficiente do estado operacional do sistema.
O led verde, piscando aproximadamente uma vez por segundo, indica que o código está em execução e que todos os módulos (com exceção do display LCD, que não é testado) estão funcionando normalmente.
O led vermelho, piscando também aproximadamente uma vez por segundo, indica que o código está em execução, mas que um ou mais dos módulos (RTC, BME280, cartão SD) estão em falha.
Se o microcontrolador deixar de executar o código (“travar”) por algum motivo, o led ficará ou aceso ou apagado (não piscará), condição que indica que o sistema está inoperante. Este método, denominado heart beat (ou batida de coração), é utilizado em muitos sistemas, como por exemplo nos alarmes de incêndio, para indicar que estão em funcionamento.
Mensagens de erro, detalhando qual é o módulo em falha, são enviadas ao Monitor Serial, mas só é possível ver essas mensagens quando o dispositivo está conectado a um computador.
Em uso normal, um coletor de dados alimentado à bateria, pode estar instalado em um lugar pouco acessível. Medições como as de uma condição denominada microclima (microclimate data loggers) coletam as variáveis ambientais de um local específico, que pode ser no topo de uma árvore em uma praça cercada de concreto (ver: https://www.agriexpo.online/prod/onset/product-186107-113235.html). Em uma situação como essa, saber, à distância, se o módulo está operacional ou não (pela cor do led piscando) é fundamental.
Opcionalmente, o código pode ser alterado para piscar de forma diferente em cada tipo de erro. Muitas informações podem ser fornecidas apenas com o piscar de um led! O código POST dos computadores faz isso através dos bips de um sinalizador acústico. Veja: https://pt.wikipedia.org/wiki/Power-on_self-test.
Quando é feita uma gravação no cartão SD (uma vez a cada minuto), o led vermelho acende brevemente.
O BME280 e o RTC são verificados a cada segundo; o cartão SD apenas a cada minuto, quando é feita a tentativa de gravação. Após a falha ser corrigida, o led verde geralmente volta a funcionar de forma automática, mas em alguns casos poderá ser necessário reinicializar o Arduino Nano.
Detalhes sobre os componentes básicos: https://blog.eletrogate.com/componentes-basicos-do-arduino-o-que-e-resistor-led-potenciometro-push-button/.
Foi utilizado um módulo Arduino Nano para esta aplicação, pelo baixo custo e tamanho compacto, embora qualquer outro da linha Arduino ou ESP32 possa ser utilizado, com as devidas modificações de pinagem e, talvez, de bibliotecas. Apesar da aplicação ser relativamente simples, a utilização da memória de programa do módulo (com capacidade para cerca de 30KB) ultrapassou os 80%. Foram utilizadas as interfaces I2C (módulo BME280, display LCD e módulo RTC) e SPI (módulo do cartão SD). Dois ports I/O foram usados para acionar o led bicolor (verde e vermelho) de sinalização. O diagrama de blocos do dispositivo é mostrado mais abaixo. Ver: https://blog.eletrogate.com/guia-completo-do-arduino-nano-driver-ch340-e-bootloader/ .
O display LCD de 2 linhas e 16 caracteres por linha foi acrescido de um módulo I2C, (ver: https://blog.eletrogate.com/guia-completo-do-display-lcd-arduino/ ) que tem baixo custo e diminui as ligações elétricas necessárias (de 8 para 4, incluindo a alimentação; o módulo também possui um trimpot para ajuste de contraste e um jump para ligar a iluminação bakcklight). Os endereços da interface I2C utilizada podem ser selecionados entre 20h e 27h através de jumps de solda, sendo o padrão, 27h, o que foi o utilizado.
Para poder mostrar todas as informações ambientes em uma única linha, foram criados caracteres especiais para representar ºC (temperatura – graus Celsius), %UR (Umidade Relativa do ar – porcentagem) e mb (pressão atmosférica – milibares). A indicação de temperatura negativa foi limitada a -9,9ºC (o número de caracteres destinado à temperatura é de quatro; valores inferiores ao limite ocupariam mais posições). Caso se deseje mostrar temperaturas menores, a indicação da parte decimal pode ser eliminada. Outra opção é utilizar um display com 20 caracteres por linha.
Este fluxograma não mostra, necessariamente, a sequência da execução do código desenvolvido (como o código é feito), e sim o que o código faz, e é extremamente útil para entendê-lo.
Uma das etapas essenciais para um projeto com a plataforma Arduino é a instalação das bibliotecas (libraries) específicas adequadas. Provavelmente todos já tiveram problemas com isso. A seguir, são listadas as que foram utilizadas neste projeto, e que podem ser facilmente encontradas através do Gerenciador de Bibliotecas.
Esta versão apresentou um problema: apenas o primeiro caractere de cada string enviada ao display era mostrada.
A correção foi encontrada na Internet, bastando apenas mudar, no arquivo LiquidCrystal_I2C.cpp, um valor de retorno de ‘0’ para ‘1’ (return 1; //ALTERAÇÃO, mostrado abaixo):
// Based on the work by DFRobot #include "LiquidCrystal_I2C.h" #include <inttypes.h> #if defined(ARDUINO) && ARDUINO >= 100 #include "Arduino.h" #define printIIC(args) Wire.write(args) inline size_t LiquidCrystal_I2C::write(uint8_t value) { send(value, Rs); //return 0; //ORIGINAL return 1; //ALTERAÇÃO }
Serão detalhados apenas os pontos que não são comuns às rotinas deste tipo. Existem vários comentários no próprio código que ajudam a compreendê-lo. O fluxograma apresentado anteriormente também é útil para entender como o programa funciona; o código mostra como é feita a implementação.
O código começa, como todos, com a inclusão das bibliotecas e a definição de parâmetros, seguidas da declaração das variáveis globais.
Em seguida são criados os caracteres especiais “ºC”, “UR” e “mb”. Isso foi necessário para possibilitar a visualização dessas três grandezas simultaneamente em um display de 16 caracteres por linha como o utilizado. Os resultados no display e a criação do caractere “ºC” podem ser vistos a seguir.
//CRIAÇÃO DOS CARACTERES ESPECIAIS PARA O LCD //BITS EM 1 PONTOS ACESOS; EM ZERO, APAGADOS //CARACTERE ºC byte grau[8] = { B11100, B10100, B11100, B00011, B00100, B00100, B00100, B00011, };
Em void setup() os caracteres (com limite de oito) são efetivamente criados e um número sequencial (de 0 a 7) é atribuído a cada um deles:
//CRIAÇÃO DOS CARACTERES ESPECIAIS PARA O LCD lcd.createChar(0, grau); //0 = CARACTERE grau (ºC)
Na função void escreve_lcd(), para escrever esse caractere é utilizado o comando:
lcd.write(byte(0)); //SÍMBOLO ºC
A função void dateTime(uint16_t* date, uint16_t* time) possibilita salvar o arquivo de dados no cartão SD com a data e hora fornecidas pelo RTC. Essa função é chamada na rotina void sd_card():
SdFile::dateTimeCallback(dateTime);
O resultado será este:
A última vez que este arquivo foi gravado foi às 23:59h do dia 22/07/2021, data e hora obtidos do módulo RTC. Os dados são gravados uma vez a cada minuto, através da função void sd_card(), exatamente no momento em que o minuto no RTC é incrementado, em uma condição (if) definida dentro da função void loop():
if((min_atual > min_ini) || (min_atual == 0 && min_ini == 59))
O tipo de arquivo que será salvo tem extensão .csv, que significa “Valores Separados por Vírgulas” (do inglês Comma-Separated Values), e é automaticamente associada ao Excel. Isso significa que o código teria de inserir uma vírgula entre os valores, e cada linha do arquivo gravado ficaria assim:
22/07/21,00:00,17,1,76,1026
Os dados acima são: data (22/07/21), hora (00:00), temperatura (17,1), umidade (76) e pressão (1026). No código, o separador decimal utilizado para temperatura foi a vírgula, que é o padrão brasileiro, interpretado automaticamente pelo Excel na versão em português. O problema é que arquivos .csv utilizam a vírgula como separador, e na importação para o Excel, os valores 17 e 1 da temperatura seriam separados em colunas diferentes. Para evitar isso, foi feita uma pequena alteração no arquivo: ao invés de virgulas foram utilizados espaços como separador. A importação no Excel funciona perfeitamente (TAB ou espaço são entendidos como separadores também). Dessa forma, cada linha do arquivo ficou assim:
22/07/21 00:00 17,1 76 1026
A gravação, através da rotina void sd_card(), é feita da forma mostrada abaixo:
datalog = SD.open(nome_arq, FILE_WRITE); //ABRE ARQUIVO PARA GRAVAÇÃO if (datalog) //SE ABERTURA DE ARQUIVO OK { //GRAVA DADOS NO SD datalog.print(dia); datalog.print("/"); datalog.print(mes); datalog.print("/"); datalog.print(ano); datalog.print(" ");-----| datalog.print(hora); | datalog.print(":"); | datalog.print(minuto); | datalog.print(" ");-----| datalog.print(temp_s); | --------- ESPAÇOS SEPARADORES datalog.print(" ");-----| datalog.print(rh_s); | datalog.print(" ");-----| datalog.println(p_atm_s); datalog.close(); //FECHA ARQUIVO
O nome do arquivo é composto das letras “VA” de Variáveis Ambientais, seguidas da data lida do RTC, no formato AAMMDD (ano, mês, dia). O motivo da inversão é possibilitar a indexação dos arquivos através do gerenciador de arquivos do Windows (Explorer). Caso fosse utilizado o formato DDMMAA a ordem dos arquivos no gerenciador não seria apresentada de forma cronológica. Um detalhe importante é que, na versão utilizada da biblioteca SDFat, o nome dos arquivos podem ter no máximo 8 caracteres, como no antigo sistema operacional DOS.
É gravado um arquivo por dia, iniciando em 00:00h e terminando em 23:59h. Um registro é acrescentado ao arquivo a cada minuto.
primeiro registro do dia: 22/07/21 00:00 17,1 76 1026
último registro do dia: 22/07/21 23:59 18,3 77 1027
Teremos, portanto, 60 registros por hora durante 24 horas, o que totaliza 1440 (60 X 24) registros ou linhas por arquivo. Cada registro tem 27 caracteres (contando números, sinais gráficos e espaços). Com essas informações podemos calcular aproximadamente o tamanho que o arquivo terá: 38800 (27 X 1440) bytes, o que não é muito diferente do que informa o Explorer, mais acima: 41KB. Um velho cartão SD de 1GB será então suficiente para armazenar as informações de 24390 (1GB/41KB) dias… ou quase 67 anos!
O nome do arquivo é definido na função void sd_card() através da data do RTC, e recriado quando o dia muda:
sprintf(data_arq_nova, "%s%s%s", ano, mes, dia); //FORMATA DATA CORRENTE COMO AAMMDD //PARA FACILITAR A INDEXAÇÃO DOS ARQUIVOS //FORAM USADAS CONVERSÕES DE STRING PARA LONG (ATOL) //PORQUE AS STRINGS AAMMDD TEM 6 DÍGITOS E SÃO MAIORES QUE UM INTEIRO (INT) if(atol(data_arq_nova) > atol(data_arq_ini)) //se a data no momento da gravação for superior à lida na { //inicialização, atualiza data de inicialização sprintf(nome_arq, "%s%s%s", "VA", data_arq_nova, ".csv"); //NOME ARQUIVO = DATA ATUALIZADA //VAAAMMDD.csv sprintf(data_arq_ini, "%s", data_arq_nova); //(PASSOU DE 1/2 NOITE) } else sprintf(nome_arq, "%s%s%s", "VA", data_arq_ini, ".csv"); //NOME ARQUIVO = DATA DA INICIALIZAÇÃO //(NÃO PASSOU DE 1/2 NOITE)
As falhas de acesso ao módulo de cartão SD podem ocorrer em duas situações específicas: retirada do cartão do módulo ou cartão sem espaço para gravação, e são devidamente sinalizadas através do led vermelho.
A função void init_sd_card() executa a inicialização do cartão SD:
void init_sd_card() //FUNÇÃO PARA INICIALIZAR E VERIFICAR O ESTADO DO CARTÃO SD { if (!SD.begin(10)) //INICIALIZA CARTÃO SD { Serial.println("ERRO: falha no cartão SD"); st_sd = 0; //SD NOK } else st_sd = 1; //SD OK }
A verificação do retorno do comando de inicialização do cartão SD, SD.begin(10) (‘0’ para falha e ‘1’ para normal), funciona bem para verificar se o cartão está inserido no módulo, mas não para a situação onde o cartão não tem mais espaço para gravação. Testes feitos em um cartão de 1GB completamente ocupado (sem espaço para novas gravações) mostraram que, quando ocorria a tentativa de gravação, a rotina literalmente travava por cerca de 20 segundos (durante esse intervalo de tempo o led vermelho que indica gravação fica aceso e os pontos separadores de hora/minuto deixam de piscar). Foi criada então, uma pequena rotina na função void loop(), mostrada abaixo, que verifica quanto tempo dura a gravação (o normal, neste caso, é menos de 1 segundo). Se esse tempo for superior a 5 segundos, o estado de falha é detectado, o led verde deixa de piscar e o vermelho começa a piscar. Quando a falha for restabelecida (um cartão com espaço para gravação for inserido no slot) a rotina automaticamente volta a funcionar.
//SALVA NO SD AS VARIÁVEIS QUANDO INCREMENTA O MINUTO if((min_atual > min_ini) || (min_atual == 0 && min_ini == 59)) { msg_err_bme = 1; //HABILITA MSG ERRO BME280 UMA VEZ POR MINUTO DateTime now = rtc.now(); //LEITURA DE DATA E HORA ATUAIS DO RTC byte seg = now.second(); //TEMPO (SEGUNDOS) ANTES DE TENTAR GRAVAR NO SD digitalWrite(3,HIGH); //ACENDE LED VERMELHO FALHA GRAVAÇÃO SD digitalWrite(2,LOW); //APAGA LED VERDE (HEART BIT) min_ini = min_atual; sd_card(); //SALVA OS RESULTADOS NO SD now = rtc.now(); //LEITURA DE DATA E HORA ATUAIS DO RTC //SE TEMPO DE GRAVAÇÃO NO SD FOR SUPERIOR A 5 SEGUNDOS SINALIZA ERRO (SD SEM ESPA-ÇO) if(now.second() > seg + 5) { Serial.println("ERRO: cartão SD sem espaço"); sp_sd = 0; //SEM ESPAÇO PARA GRAVAÇÃO NO SD } else sp_sd = 1; }
Como é mostrado de forma mais visual no fluxograma, a função void loop() chama as outras funções utilizadas na rotina. Essas funções são detalhadas a seguir:
data_hora() efetua a leitura do RTC. As variáveis dia, mês, hora e minuto são lidas uma vez por segundo e formatadas para sempre ocuparem duas posições, com um zero na frente do valor, se este for menor que 10: se, por exemplo, hora ou dia for 7 será formatado como 07. Isso permite ocupar sempre o mesmo espaço no LCD e no arquivo gravado. O ano também é formatado para apenas dois dígitos: 2021 fica apenas 21. Em caso de erro no RTC, hifens (“–“) aparecem no display, como mostrado ao lado e nenhuma informação é gravada no cartão SD.
var_atm() efetua a leitura das variáveis do sensor BME280 uma vez a cada segundo e as formata adequadamente. Se ocorrer falha na comunicação com o módulo ou valores de temperatura fora da faixa especificada para o projeto (-9,9ºC a 85ºC), hifens (“–“) aparecem no display, como mostrado ao lado e nenhuma informação é gravada no cartão SD. O valor da pressão atmosférica fornecida pelo módulo é em Pascais. Para ser convertida em milibares, é preciso dividir o valor lido por 100, o que é feito no código. A função dtostrf() foi usada para converter a leitura de temperatura (float) em uma string formatada (XX.X), com ponto como separador decimal, que é substituído por vírgula em seguida, como mostrado abaixo. O problema com a função dtostrf() é o uso de memória de programa (quase 1,5KB!), o que pode comprometer a pouca memória disponível.
dtostrf(temp, 4, 1, temp_s); //CONVERTE FLOAT PARA STRING temp_s[2] = ','; //SUBSTITUI O PONTO DECIMAL DA STRING POR VÍRGULA
Os valores gravados no cartão SD são os lidos no primeiro segundo de cada minuto. Seria possível, também, fazer a média de todos os 60 valores lidos do minuto, mas no caso de variáveis com mudanças lentas como estas, isso não é normalmente necessário.
escreve_lcd() escreve, uma vez por segundo, as informações formatadas de data e hora e também as variáveis lidas do sensor BME280 (ou hifens, em caso de erro) no display. Uma variável booleana, pisca_pontos permite piscar os pontos separadores de horas e minutos, de forma a obter uma visualização, juntamente com os leds, do correto funcionamento do sistema. Os três símbolos especiais criados para poupar espaço no display são utilizados aqui. Não foi utilizada a função lcd.clear(), pois cria um efeito de flicker (cintilação, ou variação de brilho dos caracteres). Ao invés disso, sempre que se envia um texto para o LCD, todos os caracteres da linha (16, neste caso) são reescritos. Quando qualquer das variáveis escritas no display é substituída por hifens, nada é escrito no cartão SD, caso contrário ocorrerá erro ao tentar plotar um gráfico com o Excel.
sd_card() uma vez a cada minuto testa o cartão, define o nome do arquivo que será gravado, grava os dados no cartão SD e envia essas informações para o computador via porta USB de gravação do módulo Nano, para serem mostradas no Monitor Serial. Um registro só será gravado no cartão se todos os dados forem válidos (não sejam hifens).
É também na função void loop() que os leds verde e vermelho são acesos e apagados.
Finalmente, o comando delay(1000) interrompe a rotina durante 1 segundo, após o que, todo o ciclo se repete.
A função void setup(), além do que já foi explicado, tem também uma rotina específica para acerto do RTC, que está comentada (não é compilada) e que só deve ser utilizada quando:
O acerto de hora, neste caso, usa o horário do computador utilizado. Após sua execução, o tempo do RTC costuma ficar alguns segundos atrasado em relação à hora do computador.
As mensagens enviadas pelo módulo Arduino através da porta USB para o computador, através dos comandos Serial.print e Serial.println, são muito úteis durante a fase de desenvolvimento da rotina, para teste e debugging. Mas, neste projeto, onde subentende-se que o sistema vai operar de forma independente do computador, com fonte de energia própria, todas essas mensagens podem ser comentadas depois que a rotina estiver funcionado dentro do esperado. Abaixo são mostradas algumas delas.
Obs.: No comentário “DEMORA DE 22 SEG. PARA RESPONDER AO ERRO” entenda-se que a tentativa de gravação começou em 12:32:14 e terminou em 12:32:36.
Abaixo é mostrado o arquivo criado no cartão SD pelo sistema, visualizado à esquerda pelo Bloco de Notas e à direita no Excel, que transforma o ano para quatro dígitos. A primeira linha da planilha, com legenda do conteúdo de cada coluna, foi acrescentada manualmente, e reflete na legenda do gráfico mais abaixo.
A pressão atmosférica (curva vermelha) é lida na escala da esquerda (900 a 1100mb); temperatura (em azul) e umidade relativa do ar (em laranja) são lidas na escala da direita (0 a 100).
Um modelo do gráfico pode ser salvo no Excel para reutilização sempre que necessário.
Os dados mostrados são reais, obtidos na cidade de Santos, SP, em um dia de muito calor durante o inverno. Apenas durante o pico de temperatura o sensor ficou exposto ao sol.
Este projeto apresenta uma série de soluções e sugestões para o desenvolvimento de um coletor de dados, usando como exemplo as variáveis ambientais fornecidas pelo sensor BME280. Sensores para medir velocidade do vento, quantidade de radiação UV e muitos outros, podem ser agregados, para constituir uma estação meteorológica completa. Valores máximos, mínimos e médios, horários ou diários, das variáveis obtidas, podem também ser disponibilizados no display, instalando um botão de seleção do que será mostrado ou utilizando um display que tenha mais linhas. Comunicação via wifi para transferir as informações direto para um celular também seria algo muito útil.
Um arquivo específico para guardar os erros também pode ser criado, e é uma solução interessante para conhecer as eventuais falhas nos módulos utilizados (com exceção do cartão SD e do próprio Arduino Nano, é óbvio).
O mais importante, é que o projeto pode servir de base para a aquisição e armazenamento de dados diversos, como tensão e corrente elétricas, temperatura de equipamentos, aceleração, vibração e muitos mais. As opções dependem basicamente dos sensores utilizados e da velocidade de aquisição desejada.
Por fim, o projeto ainda pode ser melhorado, otimizado e adaptado às necessidades de cada usuário, o que, com certeza, será feito pelos leitores interessados.
Tenha a Metodologia Eletrogate na sua Escola! Conheça nosso Programa de Robótica Educacional.
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!