A previsão do tempo se mostra muito importante para nossos dias. Com a previsão do tempo podemos saber se hoje irá chover ou se irá ficar ensolarado, informação muito útil, pois podemos nos decidir se iremos levar um guarda-chuva ou não. A previsão do tempo se mostra presente nas atividades agrícolas, pois auxilia o produtor rural a se decidir à que dia irá efetuar o plantio, a colheita, a irrigação, auxilia no conhecimento de eventuais eventos severos do tempo, etc.
E neste post, iremos fazer o desenvolvimento da visualização da Previsão do Tempo para os próximos 3 dias em um display LCD 16×2, além de também visualizar o estado do tempo atual.
Para o desenvolvimento da visualização de Previsão do Tempo, será necessário os seguintes componentes:
Antes de continuarmos, é recomendado que o leitor veja os seguintes artigos aqui do blog da Eletrogate sobre alguns dos componentes usados:
Também será necessário uma conta HG Brasil Weather. Mais à frente iremos demonstrar o processo de criação da conta no plano gratuito.
Para criar a conta e as credenciais da API do Tempo da HG Brasil, siga os passos abaixo:
Para o desenvolvimento do circuito, siga o diagrama abaixo:
O hardware é composto de um Arduino MEGA como microcontrolador, um módulo Ethernet, que oferecerá à placa Arduino uma Conexão de Internet e um display LCD 16×2 com um Módulo Serial I2C para Display LCD para transformá-lo em um display LCD I2C.
A grande vantagem em utilizar o adaptador I2C no display é o aumento de número de portas disponíveis, já que se utilizarmos o display LCD sem o adaptador, o display utiliza 6 pinos digitais do microcontrolador. Já com o adaptador I2C, o display utiliza apenas dois pinos do microcontrolador (SDA e SCL).
Para desenvolvermos o software para o microcontrolador Arduino MEGA, devemos fazer o download de duas bibliotecas: a ArduinoJson (permitirá que receberemos os dados da API do Tempo) e a LiquidCrystal_I2C (permite controlar displays I2C com funções extremamente semelhantes à biblioteca LiquidCrystal).
Para a instalação da biblioteca, siga os passos abaixo:
Faça o mesmo processo de instalação para a biblioteca LiquidCrystal_I2C, substituindo o termo de pesquisa de ‘ArduinoJson’ para ‘LiquidCrystal_I2C ‘ e saiba que o autor da biblioteca é o Marco Schwartz.
Após instaladas as bibliotecas, cole o seguinte código na IDE Arduino:
/*************************************************** Previsão do Tempo com Arduino MEGA e Display LCD Exemplo Criado em 15 de Julho de 2021 por Michel Galvão ****************************************************/ // Inclusão das Biblitecas #include <LiquidCrystal_I2C.h> #include <ArduinoJson.h> #include <Ethernet.h> #include <SPI.h> //Configuração Display LCD I2C /* Para saber o endereço I2C do Display LCD, faça o upolad do código de exemplo i2c_scanner. Você pode acessar o código em: Arquivo -> Exemplos -> Wire -> i2c_scanner. Faça o upload, já com o circuito previamente montado, e abra o Monitor Serial. Você verá a mensagem 'I2C device found at address 0x27!' indicando o endereço à que o Display LCD está endereçado. */ LiquidCrystal_I2C lcd(0x27, 16, 2); // LiquidCrystal_I2C nomeDoObjeto(endereço I2C, Quantidade de Colunas, Quantidade de Linhas); // Variáveis de Armazenamento dos dados do Tempo int temperatura; char* dataConsulta; char* horarioConsulta; char* descricaoTempo; char* diaOuNoite; char* LocalParaConsulta; int umidade; char* velocidadeVento; char* nascerDoSol; char* poenteDoSol; char* dataPrevisao1; char* diaDaSemanaPrevisao1; int maxPrevisao1; int minPrevisao1; char* descricaoTempoPrevisao1; char* dataPrevisao2; char* diaDaSemanaPrevisao2; int maxPrevisao2; int minPrevisao2; char* descricaoTempoPrevisao2; char* dataPrevisao3; char* diaDaSemanaPrevisao3; int maxPrevisao3; int minPrevisao3; char* descricaoTempoPrevisao3; char* dataPrevisao4; char* diaDaSemanaPrevisao4; int maxPrevisao4; int minPrevisao4; char* descricaoTempoPrevisao4; //Criação de matriz para um caractere personalizado (símbolo grau) /* Para mais detalhes de como criar um caractere personalizado: acesse o site https://blog.eletrogate.com/guia-completo-do-display-lcd-arduino/#titulo3; */ byte grau[] = { B00111, B00101, B00111, B00000, B00000, B00000, B00000, B00000 }; String woeid = "INSIRA_AQUI_SEU_WOIED"; // WOIED de sua cidade String chave = "INSIRA_AQUI_SUA_CHAVE_DA_API"; // Sua Chave da API HG Brasil Weather void setup() { lcd.init();// Inicializa o Display LCD lcd.createChar(0, grau); // Armazena na memória do LCD o caractere criado; lcd.backlight(); // Deixa a luz de fundo do siplay LCD ligada lcd.clear(); // Limpa a tela LCD Serial.begin(9600); // Configura a taxa de transferência para transmissão serial Ethernet.init(53); // Configura o pino CS (seleção de chip) para o módulo Ethernet byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // Define o endereço MAC virtual para o módulo Ethernet o utilizar if (Ethernet.begin(mac) == 0) { // Se o módulo Ethernet não inicializar, ... Serial.println(F("Falha ao configurar Ethernet")); if (Ethernet.hardwareStatus() == EthernetNoHardware) { // Se o módulo Ethernet não encontrado, ... Serial.println("A placa Ethernet não foi encontrada. Desculpe, não pode ser executado sem hardware. :("); while (Ethernet.hardwareStatus() == EthernetNoHardware) { // Tenta encontrar o módulo Ethernet delay(100); // Espera 100 ms entre as tentativas } } if (Ethernet.linkStatus() == LinkOFF) { // Se o link estiver desligado, ... Serial.println("O cabo Ethernet não está conectado."); while (Ethernet.linkStatus() == LinkOFF) { // Tenta fazer link Ligado delay(100); // Espera 100 ms entre as tentativas } } } else { Serial.println(F("Ethernet OK!")); } // Exibe uma tela inicial (Splash screen) lcd.setCursor(0, 0); lcd.print("Previsao Tempo "); lcd.setCursor(0, 1); lcd.print("com Arduino MEGA"); delay(2500); } void loop() { atualizarDadosDoTempo(); // Chama função para atualizar variáveis do Tempo e da Previsão do tempo /* Exibição de 'Telas' no display LCD com informações do Tempo e da Previsão do tempo com delays necessários para não exceder o limite de requisições da API do Tempo que no plano gratuito é 2500 requisições */ telaLcd0(); delay(1000); telaLcd1(); delay(14000); telaLcd2(); delay(4000); telaLcd3(); delay(4000); telaLcd4(); delay(4000); telaLcd5(); delay(4000); telaLcd6(); delay(4000); telaLcd7PrevisaoDia1(); delay(4000); telaLcd8PrevisaoDia1(); delay(4000); telaLcd9PrevisaoDia2(); delay(4000); telaLcd10PrevisaoDia2(); delay(4000); telaLcd11PrevisaoDia3(); delay(4000); telaLcd12PrevisaoDia3(); delay(4000); telaLcd13PrevisaoDia4(); delay(4000); telaLcd14PrevisaoDia4(); delay(4000); telaLcd15(); delay(1000); /* Tempo delay Total: 60000 milissegundos = 60 segundos; Total de Requisições para API por dia: [(60 segundos tem em 1 minuto)/(60 segundos de intervalo)]*(60 minutos)*(24 horas) = 1440 requisições; Máximo de requisões para a API no plano gratuito: 2500 requisições */ } void atualizarDadosDoTempo() { // Função para atualizar variáveis do Tempo e da Previsão do tempo Serial.println(F("Conectando à API do Tempo...")); EthernetClient client; // Cria um cliente para se conectar ao servidor da API do Tempo client.setTimeout(10000); // Define o máximo de milissegundos para aguardar os dados de fluxo if (!client.connect("api.hgbrasil.com", 80)) { // Se conectado ao endereço do servidor, na porta 80, com sucesso... Serial.println(F("Conexão falhou :(")); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Conexao com API falhou. Veja a internet"); while (!client.connect("api.hgbrasil.com", 80)) { // enquanto não estiver conectado ao endereço do servidor,... delay(450); lcd.scrollDisplayLeft(); // rola para a esquerda } lcd.clear(); lcd.setCursor(0, 0); lcd.print("Conexao com API restaurada!"); // Função para rolagem de LCD personalizada. informe a velocidade de Rolagem e a quantidad e de caracteres, respectivamente. Para mais detalhes, veja a criação da função mais abaixo. rolamentoLCD(5, strlen("Conexao com API restaurada!") + strlen("API connection restored!")); return; } Serial.println(F("Conectado!")); // Envia pedido HTTP ao servidor da API do Tempo, solicitando dados de 4 dias (hoje + 3 dias à frente) em array_limit=4. //O número de array_limit é um inteiro limitando o número de itens em arrays do retorno client.println("GET /weather?array_limit=4&fields=only_results,temp,date,time,description,currently,city,humidity,wind_speedy,sunrise,sunset,forecast,date,weekday,max,min,description,&key=" + chave + "&woeid=" + woeid + " HTTP/1.0"); client.println(F("Host: api.hgbrasil.com")); client.println(F("Connection: close")); if (client.println() == 0) { // se o retorno de dados do servidor conectado for dde 0 bytes,... Serial.println(F("Falha ao enviar pedido")); client.stop(); // Desconecta-se do servidor return; } // Verifica o status do HTTP char status[32] = {0}; client.readBytesUntil('\r', status, sizeof(status)); // Deve ser "HTTP / 1.0 200 OK" ou "HTTP / 1.1 200 OK" if (strcmp(status + 9, "200 OK") != 0) { Serial.print(F("Resposta inesperada: ")); Serial.println(status); client.stop(); return; } // Pular cabeçalhos HTTP char endOfHeaders[] = "\r\n\r\n"; if (!client.find(endOfHeaders)) { Serial.println(F("Resposta inválida")); client.stop(); return; } // Alocar o documento JSON // Use arduinojson.org/v6/assistant para calcular a capacidade. const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 600; // Necessário 600 bytes de memória reservada para o processo de Desserialização do documento JSON DynamicJsonDocument doc(capacity); // Analisa o objeto JSON DeserializationError error = deserializeJson(doc, client); if (error) { Serial.print(F("deserializeJson() falhou: ")); Serial.println(error.f_str()); client.stop(); return; } // Extrai os valores do tempo e repassa para as variáveis globais Serial.println(F("Resposta:")); temperatura = doc["temp"].as<int>(); Serial.println(doc["temp"].as<int>()); dataConsulta = doc["date"].as<char*>(); Serial.println(doc["date"].as<char*>()); horarioConsulta = doc["time"].as<char*>(); Serial.println(doc["time"].as<char*>()); descricaoTempo = doc["description"].as<char*>(); Serial.println(doc["description"].as<char*>()); diaOuNoite = doc["currently"].as<char*>(); Serial.println(doc["currently"].as<char*>()); LocalParaConsulta = doc["city"].as<char*>(); Serial.println(doc["city"].as<char*>()); umidade = doc["humidity"].as<int>(); Serial.println(doc["humidity"].as<int>()); velocidadeVento = doc["wind_speedy"].as<char*>(); Serial.println(doc["wind_speedy"].as<char*>()); nascerDoSol = doc["sunrise"].as<char*>(); Serial.println(doc["sunrise"].as<char*>()); poenteDoSol = doc["sunset"].as<char*>(); Serial.println(doc["sunset"].as<char*>()); // Extrai os valores da Previsão do tempo e repassa para as variáveis globais Serial.println("\nPrevisão do Tempo\n"); int indice = 1; for (JsonObject elem : doc["forecast"].as<JsonArray>()) { const char* date = elem["date"]; const char* weekday = elem["weekday"]; int max = elem["max"]; int min = elem["min"]; const char* description = elem["description"]; switch (indice) { case 1: dataPrevisao1 = date; diaDaSemanaPrevisao1 = weekday; maxPrevisao1 = max; minPrevisao1 = min; descricaoTempoPrevisao1 = description; break; case 2: dataPrevisao2 = date; diaDaSemanaPrevisao2 = weekday; maxPrevisao2 = max; minPrevisao2 = min; descricaoTempoPrevisao2 = description; break; case 3: dataPrevisao3 = date; diaDaSemanaPrevisao3 = weekday; maxPrevisao3 = max; minPrevisao3 = min; descricaoTempoPrevisao3 = description; break; case 4: dataPrevisao4 = date; diaDaSemanaPrevisao4 = weekday; maxPrevisao4 = max; minPrevisao4 = min; descricaoTempoPrevisao4 = description; break; } Serial.print("dia "); Serial.println(date); Serial.println(weekday); Serial.println(max); Serial.println(min); Serial.println(description); Serial.println("\n"); indice++; } // Desconecta-se do servidor client.stop(); } // Funções de Exibição das Telas no display LCD void telaLcd0() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Tempo Atual "); lcd.setCursor(0, 1); lcd.print(" AGORA "); } void telaLcd1() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(temperatura); lcd.write(0);//grau caractere lcd.print("C "); lcd.print(umidade); lcd.print("%RH "); lcd.print(diaOuNoite); lcd.print(" "); lcd.setCursor(0, 1); lcd.print(horarioConsulta); lcd.print(" "); lcd.print(dataConsulta); } void telaLcd2() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(descricaoTempo); lcd.setCursor(0, 1); lcd.print(" "); lcd.setCursor(0, 0); rolamentoLCD(5, strlen(descricaoTempo)); } void telaLcd3() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Local: "); lcd.setCursor(0, 1); lcd.print(LocalParaConsulta); rolamentoLCD(5, strlen(LocalParaConsulta)); } void telaLcd4() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Vento "); lcd.print(velocidadeVento); lcd.print(" "); lcd.setCursor(0, 1); lcd.print(" "); } void telaLcd5() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Aurora "); lcd.print(nascerDoSol); lcd.setCursor(0, 1); lcd.print("Poente "); lcd.print(poenteDoSol); } void telaLcd6() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Previsao para os"); lcd.setCursor(0, 1); lcd.print("proximos 3 dias "); } void telaLcd7PrevisaoDia1() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao1); lcd.print(" "); lcd.print(diaDaSemanaPrevisao1); lcd.setCursor(0, 1); lcd.print("max "); lcd.print(maxPrevisao1); lcd.write(0);//grau caractere lcd.print("C min "); lcd.print(minPrevisao1); lcd.write(0);//grau caractere } void telaLcd8PrevisaoDia1() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao1); lcd.print(" "); lcd.print(descricaoTempoPrevisao1); rolamentoLCD(5, strlen(dataPrevisao1) + 1 + strlen(descricaoTempoPrevisao1)); } void telaLcd9PrevisaoDia2() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao2); lcd.print(" "); lcd.print(diaDaSemanaPrevisao2); lcd.setCursor(0, 1); lcd.print("max "); lcd.print(maxPrevisao2); lcd.write(0);//grau caractere lcd.print("C min "); lcd.print(minPrevisao2); lcd.write(0);//grau caractere } void telaLcd10PrevisaoDia2() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao2); lcd.print(" "); lcd.print(descricaoTempoPrevisao2); rolamentoLCD(5, strlen(dataPrevisao2) + 1 + strlen(descricaoTempoPrevisao2)); } void telaLcd11PrevisaoDia3() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao3); lcd.print(" "); lcd.print(diaDaSemanaPrevisao3); lcd.setCursor(0, 1); lcd.print("max "); lcd.print(maxPrevisao3); lcd.write(0);//grau caractere lcd.print("C min "); lcd.print(minPrevisao3); lcd.write(0);//grau caractere } void telaLcd12PrevisaoDia3() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao3); lcd.print(" "); lcd.print(descricaoTempoPrevisao3); rolamentoLCD(5, strlen(dataPrevisao3) + 1 + strlen(descricaoTempoPrevisao3)); } void telaLcd13PrevisaoDia4() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao4); lcd.print(" "); lcd.print(diaDaSemanaPrevisao4); lcd.setCursor(0, 1); lcd.print("max "); lcd.print(maxPrevisao4); lcd.write(0);//grau caractere lcd.print("C min "); lcd.print(minPrevisao4); lcd.write(0);//grau caractere } void telaLcd14PrevisaoDia4() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(dataPrevisao4); lcd.print(" "); lcd.print(descricaoTempoPrevisao4); rolamentoLCD(5, strlen(dataPrevisao4) + 1 + strlen(descricaoTempoPrevisao4)); } void telaLcd15() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Fim da Previsao "); lcd.setCursor(0, 1); lcd.print("do tempo "); } /* rolamentoLCD(velocidade, quantidadeDeCaracteres); velocidade: velocidade de rolagem no LCD. Quanto mais baixo o número mais rápido. Velocidade de rolagem rápida máxima: 1; Velocidade de rolagem lenta máxima: infinito, desde que o usuário queira; quantidadeDeCaracteres: quantidade de caracteres utilizado no display. Deve-se passar um número inteiro. */ void rolamentoLCD(int velocidade, int deslocamento) { velocidade = velocidade * 100; if (deslocamento > 16) { deslocamento = deslocamento - 16; delay(velocidade * 2); for (int i = 0; i < deslocamento + 1; i++) { lcd.scrollDisplayLeft();//fazer scrool delay(velocidade); } delay(velocidade * 1.5); } }
No código, inclui-se primeiro as bibliotecas:
Após isso vem a configuração do Display LCD I2C:
Em seguida vem a declaração de Variáveis Globais de Armazenamento dos dados do Tempo:
Depois criamos um array para fazermos um caractere personalizado para exibição no display LCD (símbolo grau). Para mais detalhes da criação de um caractere personalizado, veja o post aqui do blog da Eletrogate: Guia completo do Display LCD – Arduino
Logo após, criamos as variáveis de WOIED da cidade e da Chave da API. Você deve substituir INSIRA_AQUI_SEU_WOIED
pelo número WOID anotado anteriormente. Também substitua INSIRA_AQUI_SUA_CHAVE_DA_API
pela sua Chave anotada anteriormente por você.
Em void Setup (inicialização do programa) configuramos:
Inicializamos o Display LCD, armazenamos na memória do LCD o caractere personalizado criado, ligamos a luz de backlight do LCD e limpamos a tela do LCD.
Configuramos a taxa de transferência para transmissão serial do Serial para 9600.
Configuramos o pino de seleção de chip para o módulo Ethernet (pino 53 do Arduino MEGA), Definimos o endereço MAC virtual da placa Ethernet e verificamos se o hardware do módulo Ethernet está conectado e com o link de rede ligado.
Em void Loop (programa principal em loop infinito) atualizamos as variáveis do Tempo e da Previsão do tempo através da função atualizarDadosDoTempo();
. Chamamos as funções que exibem as ‘Telas’ no display LCD com informações do Tempo e da Previsão do tempo. Entre as chamadas das funções de exibição das Telas, colocamos um delay() para permitir que o usuário consiga visualizar as informações na tela, além de aumentar o intervalo entre as chamadas da função que atualiza as variáveis (o limite de requisições da API do Tempo no plano gratuito é 2500 requisições).
Na função atualizarDadosDoTempo()
, de atualização de variáveis do Tempo e da Previsão do tempo, começamos criando um cliente Ethernet para se conectar ao servidor da API do Tempo. Depois definimos o máximo de milissegundos para aguardar os dados de fluxo (10000 ms).
Depois verificamos se estamos conectado ao endereço do servidor.
Se não estivermos, tentamos fazer continuamente a conexão ao endereço do servidor até que haja uma conexão.
Se estivermos conectado, enviamos um pedido HTTP ao servidor da API do Tempo .
O retorno do servidor é:
{ "temp": 22, "date": "15/07/2021", "time": "21:01", "description": "Parcialmente nublado", "currently": "noite", "city": "Brasília, DF", "humidity": 43, "wind_speedy": "2.57 km/h", "sunrise": "06:39 am", "sunset": "05:55 pm", "forecast": [ { "date": "15/07", "weekday": "Qui", "max": 27, "min": 13, "description": "Tempo limpo" }, { "date": "16/07", "weekday": "Sex", "max": 28, "min": 16, "description": "Tempo limpo" }, { "date": "17/07", "weekday": "Sáb", "max": 29, "min": 17, "description": "Tempo limpo" }, { "date": "18/07", "weekday": "Dom", "max": 30, "min": 17, "description": "Tempo limpo" } ] }
E então verificamos o status HTTP retornado pelo servidor.
E então alocamos na memória do Arduino uma quantidade de bytes para o microcontrolador poder trabalhar com os dados JSON.
Logo após, extraímos os valores do tempo e os repassamos para as variáveis globais correspondentes.
Extraímos também os valores da previsão do Tempo e os repassamos para as variáveis globais correspondentes. Após isso, nos desconectamos do servidor conectado anteriormente.
As funções abaixo são para fazerem a exibição das informações do tempo e da Previsão do Tempo no display LCD.
Veja o vídeo abaixo para ver o funcionamento final:
Com este exemplo de saber a previsão do tempo com o arduino, abre-se portas para projetos mais complexos. Pode-se implementar um alerta de chuva ou tempestades. Ou até mesmo se decidir se irá efetuar uma irrigação com base na previsão do tempo para o próximo dia.
Caso tenha ficado alguma dúvida, nos contate através dos comentários.
Espero que o tenha ajudado e até a próxima.
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!