Início Etapa 2 - Mobile Atividade 11
11

Estação Meteorológica IoT

ESP32 + DHT22 + Banco de Dados + Pilhas

🔥 Nível: Avançado ⏱️ 90 min

🎯 O que vamos fazer?

Neste projeto incrível, você vai criar uma Estação Meteorológica Profissional! Não usaremos cabo USB para energia; o ESP32 rodará com 4 pilhas AA. Para economizar bateria, ele usará a técnica de Deep Sleep (Hibernação) - acordando a cada X minutos para ler o sensor e enviar para um Banco de Dados MySQL via PHP.

🔋

Bateria & Deep Sleep

Alimentação via 4 pilhas AA. O chip dorme para durar meses.

💾

Banco de Dados MySQL

Os dados não se perdem! Ficam salvos no servidor para sempre.

📊

Dashboard Histórico

Gráficos dinâmicos com histórico diário, semanal e mensal.

🛠️ Material Necessário

  • 1x Placa ESP32 O cérebro do projeto
  • 🌡️
    1x Sensor DHT22 Branco, com 4 pernas ou módulo com 3
  • 🔋
    1x Suporte + 4 Pilhas AA Fornecerá ~6V (ou 4.8V se recarregáveis)
  • 🔌
    Breadboard e Jumpers Para fazer as conexões
  • 🔘
    1x Botão Push-button (Opcional) Para "Acordar" o ESP32 manualmente (Wake Up Externo)

🔌 Esquema de Montagem

⚠️ ATENÇÃO COM A ENERGIA!

  • O fio Vermelho do Suporte de Pilhas DEVE ser ligado no pino VIN (ou 5V) do ESP32, e não no 3.3V!
  • O pino VIN possui um regulador interno que baixa a tensão das pilhas para os 3.3V seguros para o chip.
  • Ligar 6V direto no pino 3.3V queimará seu ESP32.
Componente Pino no Componente Conexão no ESP32 Cor Sugerida/Observação
Suporte 4 Pilhas Cabo Positivo (+) VIN ou 5V
Vermelho
Cabo Negativo (-) GND
Preto
Sensor DHT22 Pino 1 (VCC) ou + 3.3V Vermelho (O DHT22 trabalha em 3.3V)
Pino 2 (DATA) ou OUT GPIO 4
Verde/Azul
Pino 4 (GND) ou - GND
Preto
Botão (Wake Up) Terminal 1 3.3V (HIGH)
Laranja/Vermelho
Terminal 2 GPIO 33 Ligamos GPIO no botão em modo Pull-Down interno

💻 Códigos do Projeto

1

Criar Tabela no MySQL

Rode este código no seu phpMyAdmin

Este SQL cria a estrutura onde os dados climáticos viverão. Ele precisa rodar no banco de dados configurado no api/config.php.

clima_db.sql
-- Criar a tabela para armazenar os dados de clima
CREATE TABLE IF NOT EXISTS estacao_meteorologica (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ambiente VARCHAR(50) NOT NULL,
    temperatura FLOAT NOT NULL,
    umidade FLOAT NOT NULL,
    bateria FLOAT DEFAULT NULL,
    data_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
2

API PHP (api/clima.php)

Recebe os POSTs do ESP e fornece GETs ao Gráfico

Crie este arquivo api/clima.php. Ele faz a ponte mágica entre o hardware (ESP32) e o software Front-end.

api/clima.php
<?php
require_once 'config.php';
setCorsHeaders();

$method = $_SERVER['REQUEST_METHOD'];

try {
    $pdo = getConnection();
    
    // GET: Enviar os dados ao Gráfico do Dashboard
    if ($method === 'GET') {
        $filtro = $_GET['filtro'] ?? 'hoje';
        
        $sql = "SELECT * FROM estacao_meteorologica WHERE 1=1";
        
        switch ($filtro) {
            case 'hoje': $sql .= " AND DATE(data_registro) = CURDATE()"; break;
            case 'semana': $sql .= " AND data_registro >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)"; break;
            case 'mes': $sql .= " AND data_registro >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)"; break;
            case 'ano': $sql .= " AND data_registro >= DATE_SUB(CURDATE(), INTERVAL 365 DAY)"; break;
        }
        
        $sql .= " ORDER BY data_registro ASC";
        
        $stmt = $pdo->prepare($sql);
        $stmt->execute();
        
        jsonResponse([
            'success' => true,
            'dados' => $stmt->fetchAll(PDO::FETCH_ASSOC)
        ]);
    }
    
    // POST: Receber os dados do ESP32 via Wi-FI
    else if ($method === 'POST') {
        $data = getPostData();
        
        $ambiente = sanitize($data['ambiente'] ?? 'N/A');
        $temp = filter_var($data['temperatura'], FILTER_VALIDATE_FLOAT);
        $umid = filter_var($data['umidade'], FILTER_VALIDATE_FLOAT);
        
        if ($temp === false || $umid === false) {
            jsonResponse(['success' => false, 'error' => 'Temp/Umidade invalidos'], 400);
        }
        
        $sql = "INSERT INTO estacao_meteorologica (ambiente, temperatura, umidade) VALUES (?, ?, ?)";
        $pdo->prepare($sql)->execute([$ambiente, $temp, $umid]);
        
        jsonResponse([
            'success' => true,
            'message' => 'Salvo!'
        ], 201);
    }
} catch (PDOException $e) {
    jsonResponse(['success' => false, 'error' => 'Erro de DB'], 500);
}
?>
3

Arduino: ESP32 com Deep Sleep

Embarque na placa após configurar o IP da URL.

Este é o cérebro físico! Ele dorme quase o tempo todo consumindo o mínimo absoluto de bateria. Acorda a cada 30 minutos, lê as métricas, atira na API e pisca o LED de sono dinovo!

⚠️ MUDE O IP: Não se esqueça de alterar http://192.168.../api/clima.php para o IP da sua máquina local no XAMPP ou seu domínio.

arduino/clima_deepsleep.ino
#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>

// ⚠️ CONFIGURE SUA REDE AQUI
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";
const char* serverUrl = "http://192.168.0.XYZ/iot/api/clima.php"; 
const String NOME_AMBIENTE = "Sala de Estar";

#define DHTPIN 4
#define DHTTYPE DHT22
#define BOTAO_WAKEUP 33
#define TEMPO_DORMINDO 30 * 60 * 1000000ULL // 30 Minutos em Microsegundos

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  delay(1000); 

  Serial.println("--- ESP32 ACORDOU! ---");
  dht.begin();
  delay(2000); // DHT precisa estabilizar
  
  float umidade = dht.readHumidity();
  float temperatura = dht.readTemperature();

  if (isnan(umidade) || isnan(temperatura)) {
    Serial.println("Falha Sensor!");
    voltarADormir();
    return;
  }

  WiFi.begin(ssid, password);
  int t = 0;
  while (WiFi.status() != WL_CONNECTED && t < 20) {
    delay(500);
    t++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    enviarDados(temperatura, umidade);
  }

  voltarADormir();
}

void loop() {
  // O chip dorme no SETUP. O LOOP NUNCA RODA no Deep Sleep
}

void enviarDados(float temp, float umid) {
  HTTPClient http;
  http.begin(serverUrl);
  http.addHeader("Content-Type", "application/json");

  String payload = "{\"ambiente\":\"" + NOME_AMBIENTE + "\",\"temperatura\":" + String(temp) + ",\"umidade\":" + String(umid) + "}";
  http.POST(payload);
  http.end();
}

void voltarADormir() {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  
  esp_sleep_enable_timer_wakeup(TEMPO_DORMINDO);
  esp_sleep_enable_ext0_wakeup((gpio_num_t)BOTAO_WAKEUP, 1);
  
  Serial.println("ZzZzZzZ");
  Serial.flush(); 
  esp_deep_sleep_start();
}
4

Front-end: Dashboard Interativo

Biblioteca Chart.js e Requisição Fetch

Este JavaScript deve ser adicionado no final da sua página HTML (antes de fechar o </body>). É ele que busca os dados da API e "desenha" o gráfico bonito na tela!

Script HTML (Dashboard)
// 1. Variável global e Estado
let myChart = null;
let ambienteAtual = 'Todos';

// 2. Modificado: Busca dados do ambiente selecionado também!
async function carregarDados(filtro) {
    try {
        const url = `api/clima.php?filtro=${filtro}&ambiente=${encodeURIComponent(ambienteAtual)}`;
        const resposta = await fetch(url);
        const json = await resposta.json();

        if (json.success && json.dados.length > 0) {
            atualizarGraficoDashboard(json.dados);
        } else {
            console.warn("Sem dados...");
        }
    } catch (erro) { console.error(erro); }
}

// 3. Recebe o JSON do banco e prepara os arrays
function atualizarGraficoDashboard(dados_bd) {
    const labels = dados_bd.map(r => new Date(r.data_registro).toLocaleString('pt-BR'));
    const temps = dados_bd.map(r => parseFloat(r.temperatura));
    const umids = dados_bd.map(r => parseFloat(r.umidade));

    renderizarChart(labels, temps, umids);
}

// 4. Desenha o Gráfico Linha Vermelha / Azul usando Chart.js
function renderizarChart(labels, temps, umids) {
    const ctx = document.getElementById('climaChart').getContext('2d');
    
    // Se já existe um gráfico, destrói e recria (para animação funcionar)
    if (myChart) myChart.destroy();

    myChart = new Chart(ctx, {
        type: 'line',
        data: {
            labels: labels,
            datasets: [
                {
                    label: 'Temperatura (°C)',
                    data: temps,
                    borderColor: '#f59e0b',
                    borderWidth: 3,
                    tension: 0.4
                },
                {
                    label: 'Umidade (%)',
                    data: umids,
                    borderColor: '#3b82f6',
                    borderWidth: 2,
                    borderDash: [5, 5],
                    tension: 0.4
                }
            ]
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
            interaction: { mode: 'index', intersect: false }
        }
    });
}

// Carrega automático ao abrir a página!
window.addEventListener('load', () => carregarDados('hoje'));

📊 Dashboard da Estação Metereológica

📍 Ambiente:
🌍 Visão Geral (Todos)
🌡️ Temp Média --°C
💧 Umidade Média --%
🔥 Temp Máx --°C
❄️ Temp Mín --°C
Última atualização: --
API Conectada

🎮 Desafio Final

🏆

Em Breve: Quiz da Atividade 11