// ===================== BIBLIOTECAS =====================
#include <WiFi.h> // WiFi → conexão com internet
#include <Adafruit_GFX.h> // Display TFT
#include <Adafruit_ILI9341.h>
#include <HTTPClient.h> // Requisições HTTP (API e webhook)
#include <ArduinoJson.h> // Manipulação de JSON (API clima)
#include <WiFiClientSecure.h> // HTTPS (necessário para webhook/email)
#include <DHT.h> // Sensor DHT22
// ===================== CONFIGURAÇÕES =====================
// WiFi do Wokwi
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// API OpenWeather
const String apiKey = "bf4e467b0f60b367480aa319980dcef3";
const String cidade = "Volta%20Redonda";
const String estado = "BR";
// Webhook (n8n ou outro serviço de email)
String webhook = "https://thaynapatricio.app.n8n.cloud/webhook-test/aulaiotthaynapatricio";
// ===================== DISPLAY TFT =====================
// Definição dos pinos do display
#define TFT_CS 5
#define TFT_DC 2
#define TFT_RST 4
// Cria objeto do display
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// ===================== SENSOR DHT22 =====================
#define DHTPIN 15 // Pino de dados
#define DHTTYPE DHT22 // Tipo do sensor
DHT dht(DHTPIN, DHTTYPE); // Cria objeto do sensor
// ===================== CONTROLE =====================
bool alertaEnviado = false; // Evita envio repetido de alertas (spam)
// ===================== ESTRUTURAS =====================
// Estrutura para armazenar dados da API
struct ClimaAPI {
String cidade;
String temperatura;
String umidade;
};
// Estrutura para armazenar dados do sensor
struct ClimaAmbiente {
float temperatura;
float umidade;
};
// ===================== FUNÇÃO WIFI =====================
void conectarWiFi() {
// Tela inicial
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(20, 100);
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.println("Conectando WiFi...");
// Inicia conexão
WiFi.begin(ssid, password);
// Aguarda conexão
while (WiFi.status() != WL_CONNECTED) {
delay(200);
Serial.print(".");
}
Serial.println("WiFi OK!");
// Confirmação na tela
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_GREEN);
tft.setCursor(20, 100);
tft.println("WiFi OK!");
}
// ===================== LEITURA DHT =====================
ClimaAmbiente lerDHT() {
ClimaAmbiente clima;
// Lê temperatura e umidade
clima.temperatura = dht.readTemperature();
clima.umidade = dht.readHumidity();
// Verifica erro de leitura
if (isnan(clima.temperatura) || isnan(clima.umidade)) {
Serial.println("Erro DHT22");
// Define valores padrão em caso de erro
clima.temperatura = 0;
clima.umidade = 0;
}
return clima;
}
// ===================== API CLIMA =====================
ClimaAPI obterClimaAPI() {
ClimaAPI clima;
// Valores padrão
clima.cidade = cidade;
clima.temperatura = "--";
clima.umidade = "--";
// Só executa se estiver conectado
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
// Monta URL da API
String url = "http://api.openweathermap.org/data/2.5/weather?q="
+ cidade + "," + estado + "&appid=" + apiKey + "&units=metric";
http.begin(url);
// Faz requisição GET
int httpCode = http.GET();
if (httpCode > 0) {
// Recebe resposta JSON
String payload = http.getString();
Serial.println(payload);
// Processa JSON
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
// Verifica se deu certo
if (doc["cod"] == 200) {
clima.cidade = doc["name"].as<String>();
clima.temperatura = String((float)doc["main"]["temp"], 1);
clima.umidade = String((int)doc["main"]["humidity"]);
}
}
http.end();
}
return clima;
}
// ===================== DETECÇÃO DE ALERTA =====================
bool temAlerta(ClimaAPI api, ClimaAmbiente amb) {
float tempAPI = api.temperatura.toFloat();
float umidAPI = api.umidade.toFloat();
// Diferença entre API e ambiente
bool diferencaTemp = abs(tempAPI - amb.temperatura) >= 20;
bool diferencaUmid = abs(umidAPI - amb.umidade) >= 20;
// Verifica faixa segura de temperatura
bool tempForaRange = (amb.temperatura < 20 || amb.temperatura > 35);
// Retorna se houver qualquer alerta
return (diferencaTemp || diferencaUmid || tempForaRange);
}
// ===================== ENVIO DE ALERTA NO EMAIL ===============
void enviarEmail(String mensagem) {
// Cliente seguro HTTPS
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
// Inicia conexão com webhook
http.begin(client, webhook);
http.addHeader("Content-Type", "application/json");
// Monta JSON
String json = "{\"mensagem\":\"" + mensagem + "\"}";
// Envia POST
int httpCode = http.POST(json);
Serial.print("HTTP: ");
Serial.println(httpCode);
http.end();
}
// ===================== VERIFICA ALERTAS =====================
void verificarAlertas(ClimaAPI api, ClimaAmbiente amb) {
float tempAPI = api.temperatura.toFloat();
float umidAPI = api.umidade.toFloat();
bool diferencaTemp = abs(tempAPI - amb.temperatura) >= 20;
bool diferencaUmid = abs(umidAPI - amb.umidade) >= 20;
bool tempForaRange = (amb.temperatura < 20 || amb.temperatura > 35);
// Se houver alerta e ainda não enviou email
if ((diferencaTemp || diferencaUmid || tempForaRange) && !alertaEnviado) {
String msg = "ALERTA!\n";
if (diferencaTemp) msg += "Diferenca de temperatura alta\n";
if (diferencaUmid) msg += "Diferenca de umidade alta\n";
if (tempForaRange) msg += "Temperatura fora do range (20-35C)\n";
msg += "Temp API: " + String(tempAPI) + "C\n";
msg += "Temp Local: " + String(amb.temperatura) + "C\n";
msg += "Umid API: " + String(umidAPI) + "%\n";
msg += "Umid Local: " + String(amb.umidade) + "%";
enviarEmail(msg);
// Marca como enviado
alertaEnviado = true;
}
// Se voltou ao normal, permite novo envio
if (!diferencaTemp && !diferencaUmid && !tempForaRange) {
alertaEnviado = false;
}
}
// ===================== DISPLAY =====================
void setDisplay(ClimaAPI api, ClimaAmbiente amb, bool alerta) {
// Fundo muda de cor conforme alerta
tft.fillScreen(alerta ? ILI9341_RED : ILI9341_BLACK);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
// Exibe aviso grande
if (alerta) {
tft.setTextSize(3);
tft.setCursor(10, 10);
tft.println("ALERTA!");
tft.setTextSize(2);
}
int yOffset = alerta ? 50 : 10;
// Cidade
tft.setCursor(10, yOffset);
tft.print(api.cidade + " - " + estado);
// Dados da API
tft.setTextColor(ILI9341_CYAN);
tft.setCursor(10, yOffset + 40);
tft.println("CLIMA DA CIDADE");
tft.setCursor(10, yOffset + 60);
tft.print("Temp: " + api.temperatura + " C");
tft.setCursor(10, yOffset + 80);
tft.print("Umid: " + api.umidade + "%");
// Dados do sensor
tft.setTextColor(ILI9341_YELLOW);
tft.setCursor(10, yOffset + 120);
tft.println("CLIMA AMBIENTE");
tft.setCursor(10, yOffset + 140);
tft.print("Temp: " + String(amb.temperatura) + " C");
tft.setCursor(10, yOffset + 160);
tft.print("Umid: " + String(amb.umidade) + "%");
}
// ===================== SETUP =====================
void setup() {
Serial.begin(115200);
// Inicializa display
tft.begin();
tft.setRotation(0);
// Inicializa sensor
dht.begin();
// Conecta no WiFi
conectarWiFi();
}
// ===================== LOOP =====================
void loop() {
// Lê sensor
ClimaAmbiente ambiente = lerDHT();
// Lê API
ClimaAPI api = obterClimaAPI();
// Verifica alerta
bool alerta = temAlerta(api, ambiente);
// Envia email se necessário
verificarAlertas(api, ambiente);
// Atualiza tela
setDisplay(api, ambiente, alerta);
delay(5000);
}