Estação Meteorológica 🌡️📱
Monitore temperatura e umidade em tempo real no seu celular usando o DHT11!
🎯 Objetivo desta atividade
Criar um dashboard mobile que exibe temperatura e umidade em tempo real, com gráficos e indicadores visuais. O ESP32 lê o sensor DHT11 e envia os dados para a API PHP usando WiFiManager (configuração automática de WiFi)!
🏗️ Como funciona?
DHT11
Sensor
ESP32
WiFiManager
API PHP
Servidor
App
Dashboard
📚 Conceitos Novos
WiFiManager
Biblioteca que cria um portal de configuração WiFi. Não precisa mais alterar SSID/SENHA no código!
DHT11
Sensor digital que mede temperatura (0-50°C) e umidade (20-90%). Usa comunicação de 1 fio (one-wire).
Gauge (Indicador Circular)
Componente visual tipo "velocímetro" que mostra valores de forma intuitiva com cores e arcos.
Conforto Térmico
Combinação de temperatura e umidade que indica se o ambiente está confortável para humanos.
Backend - API PHP
Editar no VS Code
A API recebe os dados de temperatura e umidade do ESP32 via POST e retorna os dados para o app mobile via GET.
📁 Onde criar: Salve este arquivo em
C:\xampp\htdocs\iot\api\dht.php
<?php
/**
* API para dados do sensor DHT11
* Recebe temperatura e umidade do ESP32
*/
// CORS Headers
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");
header("Content-Type: application/json");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
$arquivo = __DIR__ . '/dht_dados.json';
// Inicializar arquivo
if (!file_exists($arquivo)) {
file_put_contents($arquivo, json_encode([
'temperatura' => 0,
'umidade' => 0,
'timestamp' => time()
]));
}
// GET: Retorna dados atuais
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$dados = json_decode(file_get_contents($arquivo), true);
echo json_encode([
'success' => true,
'temperatura' => $dados['temperatura'] ?? 0,
'umidade' => $dados['umidade'] ?? 0,
'timestamp' => $dados['timestamp'] ?? time()
]);
exit();
}
// POST: Recebe dados do ESP32
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$dados = json_decode(file_get_contents('php://input'), true);
if (isset($dados['temperatura']) && isset($dados['umidade'])) {
$novosDados = [
'temperatura' => floatval($dados['temperatura']),
'umidade' => floatval($dados['umidade']),
'timestamp' => time()
];
file_put_contents($arquivo, json_encode($novosDados));
echo json_encode(['success' => true]);
} else {
http_response_code(400);
echo json_encode(['error' => 'Campos obrigatórios']);
}
exit();
}
?>
ESP32 - Código com WiFiManager
Upload via Arduino IDE
📶 WiFiManager: Este código usa configuração automática de WiFi!
Na primeira vez, conecte na rede "ESP32-Config"
com senha "12345678" para configurar.
⚠️ Bibliotecas necessárias: Instale pelo Gerenciador de Bibliotecas:
• WiFiManager (por tzapu)
• DHT sensor library (por Adafruit)
/************************************************
* ESTAÇÃO METEOROLÓGICA - ESP32 + DHT11
*
* Lê temperatura e umidade e envia para a API.
* Usa WiFiManager para configuração automática!
*
* Bibliotecas necessárias:
* - WiFiManager (por tzapu)
* - DHT sensor library (por Adafruit)
***********************************************/
#include <WiFiManager.h> // Configuração automática de WiFi
#include <HTTPClient.h> // Para requisições HTTP
#include "DHT.h" // Biblioteca do sensor
// ========== CONFIGURAÇÕES ==========
// ALTERE PARA O IP DO SEU COMPUTADOR!
const char* URL_API = "http://192.168.1.100/iot/api/dht.php";
// Pinos
const int PINO_DHT = 12; // Pino do sensor DHT11
const int PINO_BOTAO = 4; // Botão para resetar WiFi
// Cria o objeto do sensor DHT
DHT dht(PINO_DHT, DHT11);
void setup() {
Serial.begin(115200);
pinMode(PINO_BOTAO, INPUT);
// Inicializa o sensor DHT11
dht.begin();
Serial.println("Iniciando WiFiManager...");
// ========== WiFiManager ==========
// Cria o objeto WiFiManager
WiFiManager wm;
// Se o botão estiver pressionado no boot, reseta WiFi
if (digitalRead(PINO_BOTAO) == LOW) {
Serial.println("⚠️ Resetando configurações WiFi...");
wm.resetSettings();
}
// Timeout de 3 minutos para configurar
wm.setConfigPortalTimeout(180);
// Tenta conectar. Se falhar, abre portal de configuração
// Portal: rede "ESP32-Config" com senha "12345678"
bool conectou = wm.autoConnect("ESP32-Config", "12345678");
if (conectou) {
Serial.println("✅ WiFi conectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("❌ Falha na conexão! Reiniciando...");
ESP.restart();
}
}
void loop() {
// Verifica conexão WiFi
if (WiFi.status() != WL_CONNECTED) {
Serial.println("⚠️ WiFi desconectado! Reiniciando...");
ESP.restart();
}
// Lê temperatura e umidade
float temperatura = dht.readTemperature();
float umidade = dht.readHumidity();
// Verifica se a leitura foi válida
if (isnan(temperatura) || isnan(umidade)) {
Serial.println("❌ Erro ao ler o DHT11!");
delay(2000);
return;
}
Serial.print("🌡️ Temp: ");
Serial.print(temperatura);
Serial.print("°C | 💧 Umidade: ");
Serial.print(umidade);
Serial.println("%");
// Envia para a API via POST
HTTPClient http;
http.begin(URL_API);
http.addHeader("Content-Type", "application/json");
// Monta o JSON
String json = "{\"temperatura\":" + String(temperatura, 1) +
",\"umidade\":" + String(umidade, 1) + "}";
int httpCode = http.POST(json);
if (httpCode == 200) {
Serial.println("✅ Dados enviados!");
} else {
Serial.print("❌ Erro HTTP: ");
Serial.println(httpCode);
}
http.end();
// Aguarda 5 segundos
delay(5000);
}
📶 Como funciona o WiFiManager?
- 1. Na primeira vez, o ESP32 cria a rede "ESP32-Config"
- 2. Conecte seu celular nessa rede (senha: 12345678)
- 3. Uma página abre automaticamente para escolher seu WiFi
- 4. O ESP32 salva e conecta automaticamente nas próximas vezes!
- 5. Para resetar: segure o botão (GPIO 4) ao ligar
App Mobile - Dashboard
Programar em snack.expo.dev
Este código cria um dashboard bonito com indicadores de temperatura e umidade, além de um indicador de conforto térmico!
⚠️ Importante: Altere a variável URL_API
para o IP do seu computador!
/**
* Estação Meteorológica - Dashboard Mobile
* Tutorial do Prof. Reginaldo
*/
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
ActivityIndicator
} from 'react-native';
// ⚠️ ALTERE PARA O IP DO SEU COMPUTADOR!
const URL_API = 'http://192.168.1.100/iot/api/dht.php';
export default function App() {
// Estados do componente
const [temperatura, setTemperatura] = useState(0);
const [umidade, setUmidade] = useState(0);
const [carregando, setCarregando] = useState(true);
const [erro, setErro] = useState(null);
const [ultimaAtualizacao, setUltimaAtualizacao] = useState('');
// Função para buscar dados da API
const buscarDados = async () => {
try {
const resposta = await fetch(URL_API);
const dados = await resposta.json();
if (dados.success) {
setTemperatura(dados.temperatura);
setUmidade(dados.umidade);
setUltimaAtualizacao(new Date().toLocaleTimeString());
setErro(null);
}
} catch (e) {
setErro('Erro ao conectar');
}
setCarregando(false);
};
// Atualiza a cada 3 segundos
useEffect(() => {
buscarDados();
const intervalo = setInterval(buscarDados, 3000);
return () => clearInterval(intervalo);
}, []);
// Determina o conforto térmico
const getConforto = () => {
if (temperatura >= 20 && temperatura <= 26 && umidade >= 40 && umidade <= 60) {
return { texto: 'Confortável 😊', cor: '#22c55e' };
} else if (temperatura > 30 || umidade > 70) {
return { texto: 'Desconfortável 🥵', cor: '#ef4444' };
} else if (temperatura < 18) {
return { texto: 'Frio 🥶', cor: '#3b82f6' };
}
return { texto: 'Aceitável 😐', cor: '#f59e0b' };
};
// Cor baseada na temperatura
const getCorTemperatura = (temp) => {
if (temp < 18) return '#3b82f6'; // Azul (frio)
if (temp < 25) return '#22c55e'; // Verde (agradável)
if (temp < 30) return '#f59e0b'; // Amarelo (quente)
return '#ef4444'; // Vermelho (muito quente)
};
// Cor baseada na umidade
const getCorUmidade = (umi) => {
if (umi < 30) return '#f59e0b'; // Amarelo (seco)
if (umi < 60) return '#22c55e'; // Verde (ideal)
if (umi < 80) return '#3b82f6'; // Azul (úmido)
return '#8b5cf6'; // Roxo (muito úmido)
};
const conforto = getConforto();
if (carregando) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#22d3ee" />
<Text style={styles.carregandoTexto}>Carregando...</Text>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.titulo}>🌡️ Estação Meteorológica</Text>
<Text style={styles.subtitulo}>Monitoramento em tempo real</Text>
// Cards de Temperatura e Umidade
<View style={styles.cardsContainer}>
// Card Temperatura
<View style={[styles.card, { borderColor: getCorTemperatura(temperatura) }]}>
<Text style={styles.cardEmoji}>🌡️</Text>
<Text style={[styles.cardValor, { color: getCorTemperatura(temperatura) }]}>
{temperatura.toFixed(1)}°C
</Text>
<Text style={styles.cardLabel}>Temperatura</Text>
</View>
// Card Umidade
<View style={[styles.card, { borderColor: getCorUmidade(umidade) }]}>
<Text style={styles.cardEmoji}>💧</Text>
<Text style={[styles.cardValor, { color: getCorUmidade(umidade) }]}>
{umidade.toFixed(1)}%
</Text>
<Text style={styles.cardLabel}>Umidade</Text>
</View>
</View>
// Indicador de Conforto Térmico
<View style={[styles.confortoContainer, { backgroundColor: conforto.cor + '20', borderColor: conforto.cor }]}>
<Text style={[styles.confortoTexto, { color: conforto.cor }]}>
{conforto.texto}
</Text>
</View>
// Botão de Atualizar
<TouchableOpacity style={styles.botaoAtualizar} onPress={buscarDados}>
<Text style={styles.botaoTexto}>🔄 Atualizar</Text>
</TouchableOpacity>
{erro && (
<Text style={styles.erro}>⚠️ {erro}</Text>
)}
<Text style={styles.ultimaAtualizacao}>
Última atualização: {ultimaAtualizacao}
</Text>
<Text style={styles.rodape}>
Tutorial do Prof. Reginaldo
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#0f172a',
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
titulo: {
fontSize: 24,
fontWeight: 'bold',
color: '#22d3ee',
marginBottom: 4,
},
subtitulo: {
fontSize: 14,
color: '#64748b',
marginBottom: 30,
},
carregandoTexto: {
color: '#64748b',
marginTop: 10,
},
cardsContainer: {
flexDirection: 'row',
gap: 16,
marginBottom: 24,
},
card: {
backgroundColor: '#1e293b',
borderRadius: 20,
padding: 24,
alignItems: 'center',
borderWidth: 2,
minWidth: 140,
},
cardEmoji: {
fontSize: 40,
marginBottom: 8,
},
cardValor: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 4,
},
cardLabel: {
fontSize: 14,
color: '#64748b',
},
confortoContainer: {
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 20,
borderWidth: 1,
marginBottom: 24,
},
confortoTexto: {
fontSize: 18,
fontWeight: 'bold',
},
botaoAtualizar: {
backgroundColor: '#334155',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 12,
marginBottom: 16,
},
botaoTexto: {
color: '#fff',
fontSize: 16,
},
erro: {
color: '#ef4444',
fontSize: 14,
marginBottom: 10,
},
ultimaAtualizacao: {
color: '#475569',
fontSize: 12,
marginBottom: 20,
},
rodape: {
position: 'absolute',
bottom: 30,
color: '#475569',
fontSize: 12,
},
});
🧪 Testando o Sistema
-
1
Inicie o XAMPP (Apache)
Crie o arquivo api/dht.php
-
2
Faça upload do código no ESP32
Instale as bibliotecas WiFiManager e DHT
-
3
Configure o WiFi
Conecte na rede "ESP32-Config" e escolha seu WiFi
-
4
Teste o app no Expo Snack
Use o Expo Go para escanear o QR Code
🎉 Parabéns! Você criou uma estação meteorológica IoT com dashboard mobile e configuração automática de WiFi!
📝 Teste seus Conhecimentos
Quiz Gamificado
Responda às perguntas para ganhar XP e desbloquear a próxima atividade!
Carregando quiz...