Skip to Content

Estação Meteorológica de Internet Simples com ESP32

Estação Meteorológica de Internet Simples com ESP32

Neste tutorial vais aprender o básico de como construir a tua própria estação meteorológica online usando um ESP32.

Vamos descarregar dados meteorológicos do OpenWeather, analisar dados JSON com o ArduinoJson para extrair temperatura, humidade, localização e descrição do tempo, e mostrar esta informação num ecrã OLED.

Vamos começar!

Componentes Necessários

Abaixo encontras os componentes necessários para este projeto. Para este tutorial, estou a usar uma placa ESP32 mais antiga (ESP32 lite), que já foi descontinuada mas ainda é possível encontrar. É essa que está listada abaixo. Existe um modelo sucessor com melhores especificações, que podes encontrar here . Mas qualquer outro ESP32, ESP8266 ou Arduino com Wi-Fi também serve.

ESP32 lite Lolin32

ESP32 lite

USB data cable

Cabo de Dados USB

Dupont wire set

Conjunto de Cabos Dupont

Half_breadboard56a

Breadboard

OLED display

Ecrã OLED

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

OpenWeather

Para a nossa estação meteorológica online, obviamente vamos precisar de descarregar dados meteorológicos da internet. Existem várias formas de o fazer, mas a mais simples é usar OpenWeather .

O OpenWeather é um serviço que fornece dados meteorológicos atuais e previsões através de uma API que os programadores podem usar nos seus projetos. Eles têm um plan gratuito que permite fazer 1000 chamadas à API por dia. Isso dá uma chamada à API a cada 60* 60* 24/1000 = 86,4 segundos, o que é mais do que suficiente, já que atualizar o tempo a cada 5 ou 10 minutos é mais do que rápido o bastante.

Além disso, mesmo o plano gratuito fornece informação sobre Current Weather , 3-hour Forecast 5 days , Basic weather maps , Air Pollution e Geocoding . Mais do que suficiente para a maioria das aplicações de hobby.

Criar uma conta OpenWeather

Antes de poderes usar qualquer uma das APIs do OpenWeather, precisas de uma API-key e, para isso, precisas de uma conta. Para criar uma conta vai à sign-up página e preenche os teus dados.

Sign-up page at OpenWeather
Página de registo no OpenWeather

Criar API-key do OpenWeather

Depois de teres uma conta e estares autenticado, vai à página de api-key criação e cria uma API-key. A API-key é aquela string longa “sdfd87fakeby6apikeysf4z” que vês na imagem abaixo. A tua chave será diferente da minha, e a que aparece é obviamente falsa ; )

Create API-key at OpenWeather
Criar API-key no OpenWeather

Podes dar um nome à chave, mas não é obrigatório. Os nomes são úteis se tiveres várias aplicações que usam chaves diferentes e quiseres mantê-las separadas.

Descarregar dados meteorológicos do OpenWeather

Com a tua API-key já podes obter dados meteorológicos das várias APIs, construindo o URL correspondente. Por exemplo, se fores à Current Weather API, a documentação diz que o URL deve ser assim:

https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}

Portanto, precisas de saber a latitude e longitude da tua localização, e a tua API-key, e substituir os espaços entre chavetas ( {lat} , {lon} , {API key} ) pelos valores reais. Isto funciona, mas descobrir a latitude e longitude de várias cidades pode ser um pouco chato.

Felizmente, existe a Geocoding API que nos permite obter os dados meteorológicos de forma mais fácil, fornecendo apenas o nome da cidade e o código do país. O padrão do URL é assim:

https://api.openweathermap.org/data/2.5/weather?q={city name},{country code}&appid={API key}

Por exemplo, para obter o tempo em Melbourne, Austrália (AU), onde moro, escreves:

http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&APPID=sdfd87fakeby6apikeysf4z

Se colares este URL na barra de pesquisa do teu navegador, vais ver o seguinte resultado. Certifica-te de que marcas a opção “Pretty print” no topo.

Weather data in Kelvin
Dados meteorológicos em Kelvin

Se olhares para a temperatura vais notar um valor muito alto. No exemplo acima, o valor reportado é 283.38. Isto acontece porque, por defeito, o OpenWeather devolve as temperaturas em Kelvin. Se quiseres valores em unidades métricas (Celsius) ou imperiais (Fahrenheit), podes especificar isso no URL.

Por exemplo, para obter as temperaturas em Celsius deves usar:

https://api.openweathermap.org/data/2.5/weather?q={city name},{country code}&appid={API key}&units=metric

e para Fahrenheit escreves units=imperial . Para mais informações consulta a documentação do OpenWeather para units of measurement .

Isto é tudo o que precisas de saber para começar a usar o OpenWeather. Mas recomendo que explores as outras APIs mencionadas acima. Há muitos dados interessantes que podes usar para tornar a tua estação meteorológica ainda mais útil.

Analisar dados JSON com ArduinoJson

Por defeito, o OpenWeather devolve os dados em formato JSON . JSON é um formato de texto estruturado que precisamos de analisar para extrair os dados que queremos. Isso pode ser complicado se tentares implementar sozinho, por isso usamos a excelente biblioteca ArduinoJson .

Documento JSON

Mas primeiro, vamos ver que informação queremos extrair dos dados meteorológicos. A imagem abaixo mostra todos os dados que recebo ao pedir o tempo em Melbourne ao OpenWeather.

JSON data for Melbourne weather
Dados JSON do tempo em Melbourne

Para a nossa estação meteorológica online simples, vou extrair quatro informações: a descrição curta do tempo em weather -> main ( "Clouds" ), a temperatura atual em main -> temp ( 10.29 ), a humidade relativa atual em main -> humidity ( 82 ), e o nome da localização em name ( "Melbourne" ).

Extrair dados com ArduinoJson

Um excerto de código usando ArduinoJson para extrair estes quatro valores do tempo seria assim:

StaticJsonDocument<4096> doc;

String json = http.getString();
deserializeJson(doc, json);

const char* name = doc["name"];
const char* weather = doc["weather"][0]["main"];
float temp = doc["main"]["temp"];
int hum = doc["main"]["humidity"];

Primeiro, é preciso reservar alguma memória para carregar os dados JSON. A linha StaticJsonDocument<4096> doc; reserva 4KB de memória. Se descarregares mais dados meteorológicos, como previsões, pode ser necessário aumentar este valor.

Depois descarregamos os dados meteorológicos e escrevemos (desserializamos) para o documento. O excerto de código não mostra o cliente HTTP usado para o download, mas vais encontrá-lo no código completo abaixo.

String json = http.getString();
deserializeJson(doc, json);

Se os dados JSON devolvidos pelo OpenWeather estiverem sintaticamente incorretos (parênteses em falta, …), a desserialização vai falhar.

Desserializar significa simplesmente que o texto simples da string json é analisado e convertido numa estrutura de dados que nos permite aceder aos campos e valores do documento JSON doc mais facilmente. Para mais detalhes sobre desserialização, consulta a excelente ArduinoJson tutorial on deserialization .

As quatro linhas de código seguintes extraem a informação que queremos do doc que contém a string json desserializada.

const char* name = doc["name"];
const char* weather = doc["weather"][0]["main"];
float temp = doc["main"]["temp"];
int hum = doc["main"]["humidity"];

A primeira linha extrai o nome da localização meteorológica ( "Melbourne" ), que está guardado em doc["name"] . Como está guardado como string (vê as aspas), temos de a guardar numa variável const char* name .

A descrição curta do tempo ( "Clouds" ) está guardada em doc["weather"][0]["main"] . Nota que em doc["weather"] , temos uma lista [] e queremos o primeiro (e único) elemento dessa lista. Por isso, escrevemos doc["weather"][0] . Depois obtemos a descrição em main , e o caminho completo é doc["weather"][0]["main"] . Novamente, isto devolve uma string e deve ser guardada como const char* weather .

A temperatura atual é um valor decimal (10.29), que está em doc["main"]["temp"] . Como é um valor decimal, deve ser guardado numa variável float .

Por fim, extraímos a humidade atual ( 82 ) de doc["main"]["humidity"] e guardamos numa variável do tipo int .

Tens de ter muito cuidado para corresponder os tipos de dados dos valores aos tipos usados no documento JSON e usar os caminhos corretos para extrair esses valores. Caso contrário, vais ter erros de memória difíceis de resolver. Se quiseres extrair outros valores, começa devagar e adiciona novos valores passo a passo, para saberes quando algo correu mal.

Ligar o ESP32 ao OLED

Ligar o ESP32 ao OLED é muito simples, já que o OLED tem uma interface I2C. Primeiro liga o ground (G) do ESP32 ao GND do OLED. Depois liga os 3V do ESP32 ao VCC do OLED.

Connecting ESP32 to OLED
Ligar o ESP32 ao OLED

Depois precisamos de ligar o SCL e o SDA. Qualquer pino serve, mas os pinos I2C por defeito no ESP32-lite são o 23 para SCL e o 19 para SDA. Por isso, liga os pinos correspondentes do OLED a esses. A imagem acima mostra toda a ligação. Se tiveres dificuldades, vê o tutorial How to Interface the SSD1306 I2C OLED Graphic Display With Arduino

Se não sabes quais são os pinos I2C por defeito do teu ESP32, não te preocupes. É fácil descobrir. Basta correres o seguinte código e o teu ESP32 vai dizer-te.

void print(const char* name, int pin) {
  Serial.print(name);
  Serial.println(pin);
}
void setup() {
  Serial.begin(9600);
}
void loop() {
  print("SDA:  ", SDA);
  print("SCL:  ", SCL);
  delay(5000);
}

Para mais informações lê o nosso artigo sobre como Find I2C and SPI default pins . Em alternativa, podes definir os pinos que quiseres usar. O código nas secções seguintes mostra como fazer.

Código para a Estação Meteorológica Online

Nesta secção vamos escrever o código para descarregar dados meteorológicos do OpenWeather, extrair a localização, temperatura, humidade e descrição do tempo, e mostrar tudo num OLED. Vê primeiro o código completo abaixo e depois discutimos os detalhes.

#include "WiFi.h"
#include "HTTPClient.h"
#include "ArduinoJson.h"
#include "Adafruit_SSD1306.h"

#define WIFI_SSID "YOURSSID"
#define WIFI_PASSWORD "YOURPASSWORD"
#define URL "http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&APPID=YOURAPIKEY&units=metric"

HTTPClient http;
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
StaticJsonDocument<4096> doc;

void downloadWeather() {  
  http.begin(URL);
  if (http.GET() > 0) {
    String json = http.getString();
    auto err = deserializeJson(doc, json);
    if (err) {
      Serial.printf("Deserializion error: %s\n", err.f_str());
    }
  } else {
     Serial.println("Could not get data!");
  }
  http.end();
}

void displayWeather() {
  const char* name = doc["name"];
  const char* weather = doc["weather"][0]["main"];
  float temp = doc["main"]["temp"];
  int hum = doc["main"]["humidity"];

  oled.clearDisplay();
  oled.setCursor(0, 0);
  oled.printf(" %s\n", name);
  oled.printf(" %s\n", weather);
  oled.printf(" T: %.1f C\n", temp);
  oled.printf(" H: %d %%\n", hum);
  oled.display();
}

void oled_init() {
  Wire.begin(19, 23);  // sda, scl
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.clearDisplay();
  oled.setTextSize(2);
  oled.setTextColor(WHITE);
}

void wifi_init() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED)
    delay(500);
}

void setup(void) {
  Serial.begin(115200);
  oled_init();
  wifi_init();      
}

void loop() {
  downloadWeather();
  displayWeather();
  delay(10*60*1000); // 10min
}

Bibliotecas

O código começa por incluir as bibliotecas necessárias. WiFi.h para acesso WiFi, HTTPClient.h para descarregar dados via HTTP, ArduinoJson.h para analisar os dados descarregados e Adafruit_SSD1306.h para mostrar os dados no OLED.

WiFi.h e HTTPClient.h são bibliotecas padrão, mas ArduinoJson.h e Adafruit_SSD1306.h tens de instalar através do Library Manager.

Constantes

De seguida definimos algumas constantes. Especificamente as credenciais do WiFi e o URL da API do OpenWeather, de onde vamos descarregar os dados meteorológicos. Podes mudar o URL para obter dados de outra localização ou noutra unidade, mas não te esqueças de usar a tua API-key.

#define WIFI_SSID "YOURSSID"
#define WIFI_PASSWORD "YOURPASSWORD"
#define URL "http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&APPID=YOURAPIKEY&units=metric"

Vê a documentação da API para Current Weather , que mostra todas as formas de construir um URL. Também consulta o Geocoding API , que simplifica o uso de localizações.

Objetos

Para o resto do código vamos precisar de três objetos. Um HTTPClient que nos permite descarregar dados de um site, um StaticJsonDocument para guardar os dados desserializados e um objeto oled para mostrar os dados no OLED.

HTTPClient http;
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
StaticJsonDocument<4096> doc;

Para o OLED tens de especificar as dimensões, no meu caso são 128×64 pixels, e para o documento JSON precisamos de reservar a quantidade máxima de memória que possamos precisar. Reservei generosamente 4kB (4069 Bytes), o que é suficiente para os dados meteorológicos atuais. Se descarregares a previsão do tempo, que é maior, pode ser necessário reservar mais espaço.

downloadWeather

A função downloadWeather faz o download dos dados meteorológicos do OpenWeather via http.GET() , desserializa-os e guarda-os no StaticJsonDocument através de deserializeJson ().

void downloadWeather() {  
  http.begin(URL);
  if (http.GET() > 0) {
    String json = http.getString();
    auto err = deserializeJson(doc, json);
    if (err) {
      Serial.printf("Deserializion error: %s\n", err.f_str());
    }
  } else {
     Serial.println("Could not get data!");
  }
  http.end();
}

Se algo correr mal, mostramos uma mensagem de erro no Serial Monitor.

displayWeather

Para mostrar o tempo, primeiro extraímos as quatro informações meteorológicas do doc como descrito acima. Depois limpamos o ecrã, posicionamos o cursor e simplesmente mostramos o nome da localização, a descrição curta do tempo, a temperatura atual e a humidade relativa.

void displayWeather() {
  const char* name = doc["name"];
  const char* weather = doc["weather"][0]["main"];
  float temp = doc["main"]["temp"];
  int hum = doc["main"]["humidity"];

  oled.clearDisplay();
  oled.setCursor(0, 0);
  oled.printf(" %s\n", name);
  oled.printf(" %s\n", weather);
  oled.printf(" T: %.1f C\n", temp);
  oled.printf(" H: %d %%\n", hum);
  oled.display();
}

No OLED, a informação meteorológica será apresentada como na imagem abaixo.

Weather data on OLED
Dados meteorológicos no OLED

oledInit

Antes de podermos usar o OLED, temos de o inicializar. O mais importante é especificar os pinos usados para a interface I2C (SDA, SCL) e o endereço I2C do ecrã.

Se usares os pinos I2C por defeito do teu microcontrolador, não precisas de chamar Wire.begin(), caso contrário tens de o fazer.

O endereço I2C do OLED que estou a usar é 0x3C, que é comum, mas o teu pode ser diferente. Vê o tutorial How to Interface the SSD1306 I2C OLED Graphic Display With Arduino , se tiveres dificuldades.

void oled_init() {
  Wire.begin(19, 23);  // sda, scl
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  oled.clearDisplay();
  oled.setTextSize(2);
  oled.setTextColor(WHITE);
}

Além disso, apenas limpamos o ecrã e definimos o tamanho e cor do texto.

wifiInit

Tal como no OLED, também precisamos de inicializar a ligação WiFi. Certifica-te de que usas o ssid e password corretos, caso contrário o cliente WiFi não se vai ligar.

void wifi_init() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED)
    delay(500);
}

setup

Com as funções auxiliares acima, a função setup ficou muito simples. Apenas inicializamos a comunicação série, o OLED e o WiFi, e está feito.

void setup(void) {
  Serial.begin(115200);
  oled_init();
  wifi_init();      
}

loop

A função loop também é simples. Descarregamos os dados meteorológicos, mostramos no OLED e depois esperamos 10 minutos antes de repetir o processo.

void loop() {
  downloadWeather();
  displayWeather();
  delay(10*60*1000); // 10min
}

Ao implementar e testar, certifica-te de que tens um atraso suficientemente grande ao descarregar os dados meteorológicos. Se fizeres mais de 1000 chamadas por dia, serás bloqueado nesse dia.

De resto, é isto! Agora tens uma pequena estação meteorológica online!

Conclusão

Neste tutorial aprendeste como construir uma estação meteorológica online. Apesar de ser muito básica, é um ótimo ponto de partida para uma estação muito mais avançada.

Explora as outras APIs que o OpenWeather oferece e vê que dados queres extrair. Os dados devolvidos pelo OpenWeather até incluem ids de ícones que podes usar para mostrar ícones meteorológicos.

Podes querer passar para um ecrã maior do que o pequeno OLED que usámos aqui. Alguns dos ecrãs Elecrow com ESP32 integrado e ecrã tátil podem ser uma boa escolha. Vê o CrowPanel 2.8″ ESP32 Display : Easy Setup Guide para saberes como usar um desses.

Além do tempo online, também podes recolher dados meteorológicos locais usando sensores de temperatura, humidade e qualidade do ar.

Finalmente, reagir ativamente às condições meteorológicas e ligar regas, fechar janelas ou ativar o ar condicionado são projetos divertidos.

Diverte-te ; )