iotIntermediárioESP32IoTSensoresWebWiFi

Sistema de Monitoramento IoT com ESP32

Sistema completo de monitoramento de temperatura e umidade com ESP32, sensores DHT22 e interface web em tempo real com dashboard responsivo.

18/07/2025
2-3 horas
R$ 45-60
Fixtron Circuits

🔧 Componentes Principais

ESP32 DevKit V1Sensor DHT22Resistor 10kΩProtoboardJumpers

🛠️ Ferramentas Necessárias

Arduino IDENavegador web

Sistema de Monitoramento IoT com ESP32

Neste projeto, vamos construir um sistema completo de monitoramento ambiental usando ESP32, que coleta dados de temperatura e umidade e os disponibiliza através de uma interface web em tempo real. O sistema também registra histórico dos dados e permite configuração remota.

Visão Geral do Projeto

O que você vai aprender:

  • Conexão de sensores ao ESP32
  • Programação de servidor web embarcado
  • Criação de interface web responsiva
  • Armazenamento de dados em SPIFFS
  • Conectividade WiFi e configuração

Funcionalidades:

  • ✅ Monitoramento em tempo real
  • ✅ Interface web responsiva
  • ✅ Histórico de dados (24h)
  • ✅ Alertas configuráveis
  • ✅ Configuração via web
  • ✅ Modo AP para configuração inicial

Lista de Materiais

Componentes Eletrônicos

| Item | Quantidade | Preço Aprox. | |------|------------|--------------| | ESP32 DevKit V1 | 1 | R$ 30,00 | | Sensor DHT22 | 1 | R$ 15,00 | | Resistor 10kΩ | 1 | R$ 0,10 | | Protoboard 400 pontos | 1 | R$ 8,00 | | Jumpers macho-macho | 10 | R$ 5,00 | | Total | | R$ 58,10 |

Ferramentas Necessárias

  • Computador com Arduino IDE
  • Cabo USB-C ou Micro-USB (dependendo do ESP32)
  • Navegador web

Esquema de Ligação

ESP32 DevKit V1        DHT22
┌─────────────────┐    ┌──────┐
│              3V3├────┤ VCC  │
│                 │    │      │
│             D4  ├────┤ DATA │
│                 │    │      │
│             GND ├────┤ GND  │
└─────────────────┘    └──────┘
                          │
                         ┌┴┐
                         │ │ 10kΩ (pull-up)
                         │ │
                         └┬┘
                          │
                        3V3

Pinout Detalhado:

  • ESP32 3V3DHT22 VCC
  • ESP32 D4DHT22 DATA
  • ESP32 GNDDHT22 GND
  • Resistor 10kΩ entre DATA e VCC (pull-up)

Código do Projeto

1. Bibliotecas Necessárias

Primeiro, instale as bibliotecas no Arduino IDE:

# Via Library Manager:
- DHT sensor library by Adafruit
- Adafruit Unified Sensor
- ArduinoJson
- ESPAsyncWebServer

2. Código Principal

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <DHT.h>
#include <ArduinoJson.h>

// Configurações do DHT22
#define DHT_PIN 4
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);

// Servidor web na porta 80
AsyncWebServer server(80);

// Variáveis globais
struct SensorData {
  float temperature;
  float humidity;
  unsigned long timestamp;
};

SensorData currentData;
SensorData dataHistory[144]; // 24h com leituras a cada 10min
int historyIndex = 0;

// Configurações WiFi
String ssid = "";
String password = "";
bool wifiConfigured = false;

// Configurações de alerta
float tempMin = 18.0;
float tempMax = 28.0;
float humMin = 40.0;
float humMax = 70.0;

void setup() {
  Serial.begin(115200);
  Serial.println("Iniciando Sistema de Monitoramento IoT...");
  
  // Inicializar DHT22
  dht.begin();
  
  // Inicializar SPIFFS
  if (!SPIFFS.begin(true)) {
    Serial.println("Erro ao inicializar SPIFFS!");
    return;
  }
  
  // Carregar configurações
  loadConfig();
  
  // Configurar WiFi
  setupWiFi();
  
  // Configurar servidor web
  setupWebServer();
  
  // Primeira leitura
  readSensor();
  
  Serial.println("Sistema inicializado com sucesso!");
}

void loop() {
  static unsigned long lastReading = 0;
  static unsigned long lastSave = 0;
  
  // Ler sensor a cada 2 segundos
  if (millis() - lastReading > 2000) {
    readSensor();
    lastReading = millis();
  }
  
  // Salvar no histórico a cada 10 minutos
  if (millis() - lastSave > 600000) {
    saveToHistory();
    lastSave = millis();
  }
  
  delay(100);
}

void readSensor() {
  float temp = dht.readTemperature();
  float hum = dht.readHumidity();
  
  if (!isnan(temp) && !isnan(hum)) {
    currentData.temperature = temp;
    currentData.humidity = hum;
    currentData.timestamp = millis();
    
    // Verificar alertas
    checkAlerts();
    
    Serial.printf("Temp: %.1f°C, Umidade: %.1f%%\n", temp, hum);
  } else {
    Serial.println("Erro na leitura do sensor!");
  }
}

void checkAlerts() {
  bool alert = false;
  String message = "ALERTA: ";
  
  if (currentData.temperature < tempMin) {
    message += "Temperatura baixa (" + String(currentData.temperature, 1) + "°C) ";
    alert = true;
  }
  if (currentData.temperature > tempMax) {
    message += "Temperatura alta (" + String(currentData.temperature, 1) + "°C) ";
    alert = true;
  }
  if (currentData.humidity < humMin) {
    message += "Umidade baixa (" + String(currentData.humidity, 1) + "%) ";
    alert = true;
  }
  if (currentData.humidity > humMax) {
    message += "Umidade alta (" + String(currentData.humidity, 1) + "%) ";
    alert = true;
  }
  
  if (alert) {
    Serial.println(message);
    // Aqui você pode adicionar notificações (email, push, etc.)
  }
}

void saveToHistory() {
  dataHistory[historyIndex] = currentData;
  historyIndex = (historyIndex + 1) % 144;
  
  // Salvar no SPIFFS
  saveHistoryToFile();
}

void setupWiFi() {
  if (ssid.length() > 0) {
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid.c_str(), password.c_str());
    
    Serial.print("Conectando ao WiFi");
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 30) {
      delay(1000);
      Serial.print(".");
      attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
      wifiConfigured = true;
      Serial.println("\nWiFi conectado!");
      Serial.print("IP: ");
      Serial.println(WiFi.localIP());
    } else {
      Serial.println("\nFalha na conexão WiFi!");
      startAPMode();
    }
  } else {
    startAPMode();
  }
}

void startAPMode() {
  WiFi.mode(WIFI_AP);
  WiFi.softAP("IoT-Monitor-Config", "12345678");
  Serial.println("Modo AP iniciado!");
  Serial.print("IP: ");
  Serial.println(WiFi.softAPIP());
}

void setupWebServer() {
  // Servir arquivos estáticos
  server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
  
  // API para dados atuais
  server.on("/api/current", HTTP_GET, [](AsyncWebServerRequest *request){
    DynamicJsonDocument doc(200);
    doc["temperature"] = currentData.temperature;
    doc["humidity"] = currentData.humidity;
    doc["timestamp"] = currentData.timestamp;
    
    String response;
    serializeJson(doc, response);
    request->send(200, "application/json", response);
  });
  
  // API para histórico
  server.on("/api/history", HTTP_GET, [](AsyncWebServerRequest *request){
    DynamicJsonDocument doc(8192);
    JsonArray array = doc.createNestedArray("data");
    
    for (int i = 0; i < 144; i++) {
      int index = (historyIndex + i) % 144;
      if (dataHistory[index].timestamp > 0) {
        JsonObject entry = array.createNestedObject();
        entry["temperature"] = dataHistory[index].temperature;
        entry["humidity"] = dataHistory[index].humidity;
        entry["timestamp"] = dataHistory[index].timestamp;
      }
    }
    
    String response;
    serializeJson(doc, response);
    request->send(200, "application/json", response);
  });
  
  // API para configuração WiFi
  server.on("/api/wifi", HTTP_POST, [](AsyncWebServerRequest *request){
    if (request->hasParam("ssid", true) && request->hasParam("password", true)) {
      ssid = request->getParam("ssid", true)->value();
      password = request->getParam("password", true)->value();
      
      saveConfig();
      request->send(200, "text/plain", "Configuração salva! Reiniciando...");
      
      delay(1000);
      ESP.restart();
    } else {
      request->send(400, "text/plain", "Parâmetros inválidos");
    }
  });
  
  // API para configuração de alertas
  server.on("/api/alerts", HTTP_POST, [](AsyncWebServerRequest *request){
    if (request->hasParam("tempMin", true)) {
      tempMin = request->getParam("tempMin", true)->value().toFloat();
    }
    if (request->hasParam("tempMax", true)) {
      tempMax = request->getParam("tempMax", true)->value().toFloat();
    }
    if (request->hasParam("humMin", true)) {
      humMin = request->getParam("humMin", true)->value().toFloat();
    }
    if (request->hasParam("humMax", true)) {
      humMax = request->getParam("humMax", true)->value().toFloat();
    }
    
    saveConfig();
    request->send(200, "text/plain", "Alertas configurados!");
  });
  
  server.begin();
  Serial.println("Servidor web iniciado!");
}

void loadConfig() {
  if (SPIFFS.exists("/config.json")) {
    File file = SPIFFS.open("/config.json", "r");
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, file);
    file.close();
    
    ssid = doc["ssid"].as<String>();
    password = doc["password"].as<String>();
    tempMin = doc["tempMin"] | 18.0;
    tempMax = doc["tempMax"] | 28.0;
    humMin = doc["humMin"] | 40.0;
    humMax = doc["humMax"] | 70.0;
  }
}

void saveConfig() {
  DynamicJsonDocument doc(1024);
  doc["ssid"] = ssid;
  doc["password"] = password;
  doc["tempMin"] = tempMin;
  doc["tempMax"] = tempMax;
  doc["humMin"] = humMin;
  doc["humMax"] = humMax;
  
  File file = SPIFFS.open("/config.json", "w");
  serializeJson(doc, file);
  file.close();
}

void saveHistoryToFile() {
  // Implementar salvamento do histórico se necessário
  // Para este exemplo, mantemos apenas em RAM
}

3. Interface Web (HTML/CSS/JS)

Crie o arquivo data/index.html no projeto:

<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Monitor IoT - ESP32</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        h1 {
            text-align: center;
            color: white;
            margin-bottom: 30px;
            font-size: 2.5em;
            text-shadow: 0 2px 4px rgba(0,0,0,0.3);
        }
        
        .cards {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }
        
        .card {
            background: white;
            border-radius: 15px;
            padding: 30px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            text-align: center;
            transition: transform 0.3s ease;
        }
        
        .card:hover {
            transform: translateY(-5px);
        }
        
        .card h2 {
            color: #333;
            margin-bottom: 15px;
            font-size: 1.5em;
        }
        
        .value {
            font-size: 3em;
            font-weight: bold;
            margin: 10px 0;
        }
        
        .temp { color: #ff6b6b; }
        .hum { color: #4ecdc4; }
        
        .unit {
            font-size: 0.5em;
            color: #666;
        }
        
        .status {
            padding: 10px;
            border-radius: 8px;
            margin-top: 15px;
            font-weight: bold;
        }
        
        .status.ok { background: #d4edda; color: #155724; }
        .status.alert { background: #f8d7da; color: #721c24; }
        
        .chart-container {
            background: white;
            border-radius: 15px;
            padding: 30px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
            margin-bottom: 30px;
        }
        
        .config {
            background: white;
            border-radius: 15px;
            padding: 30px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #333;
        }
        
        input {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 16px;
        }
        
        button {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 12px 30px;
            border-radius: 8px;
            font-size: 16px;
            cursor: pointer;
            transition: opacity 0.3s ease;
        }
        
        button:hover {
            opacity: 0.9;
        }
        
        .last-update {
            text-align: center;
            color: white;
            margin-top: 20px;
            font-size: 0.9em;
        }
        
        @media (max-width: 768px) {
            .cards {
                grid-template-columns: 1fr;
            }
            
            h1 {
                font-size: 2em;
            }
            
            .value {
                font-size: 2.5em;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🌡️ Monitor IoT - ESP32</h1>
        
        <div class="cards">
            <div class="card">
                <h2>🌡️ Temperatura</h2>
                <div class="value temp" id="temperature">--<span class="unit">°C</span></div>
                <div class="status ok" id="temp-status">Normal</div>
            </div>
            
            <div class="card">
                <h2>💧 Umidade</h2>
                <div class="value hum" id="humidity">--<span class="unit">%</span></div>
                <div class="status ok" id="hum-status">Normal</div>
            </div>
        </div>
        
        <div class="chart-container">
            <h2>📊 Histórico (24h)</h2>
            <canvas id="chart" width="800" height="400"></canvas>
        </div>
        
        <div class="config">
            <h2>⚙️ Configurações</h2>
            
            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px;">
                <div>
                    <h3>🌡️ Limites de Temperatura</h3>
                    <div class="form-group">
                        <label>Mínima (°C):</label>
                        <input type="number" id="tempMin" value="18" step="0.1">
                    </div>
                    <div class="form-group">
                        <label>Máxima (°C):</label>
                        <input type="number" id="tempMax" value="28" step="0.1">
                    </div>
                </div>
                
                <div>
                    <h3>💧 Limites de Umidade</h3>
                    <div class="form-group">
                        <label>Mínima (%):</label>
                        <input type="number" id="humMin" value="40" step="0.1">
                    </div>
                    <div class="form-group">
                        <label>Máxima (%):</label>
                        <input type="number" id="humMax" value="70" step="0.1">
                    </div>
                </div>
            </div>
            
            <button onclick="saveAlerts()">💾 Salvar Configurações</button>
        </div>
        
        <div class="last-update" id="lastUpdate">
            Última atualização: --
        </div>
    </div>

    <script>
        let chart = null;
        
        // Atualizar dados a cada 5 segundos
        setInterval(updateData, 5000);
        updateData();
        
        // Atualizar histórico a cada minuto
        setInterval(updateChart, 60000);
        updateChart();
        
        async function updateData() {
            try {
                const response = await fetch('/api/current');
                const data = await response.json();
                
                document.getElementById('temperature').innerHTML = 
                    `${data.temperature.toFixed(1)}<span class="unit">°C</span>`;
                document.getElementById('humidity').innerHTML = 
                    `${data.humidity.toFixed(1)}<span class="unit">%</span>`;
                
                // Verificar status
                updateStatus(data);
                
                // Atualizar timestamp
                const now = new Date();
                document.getElementById('lastUpdate').textContent = 
                    `Última atualização: ${now.toLocaleTimeString()}`;
                
            } catch (error) {
                console.error('Erro ao buscar dados:', error);
            }
        }
        
        function updateStatus(data) {
            const tempMin = parseFloat(document.getElementById('tempMin').value);
            const tempMax = parseFloat(document.getElementById('tempMax').value);
            const humMin = parseFloat(document.getElementById('humMin').value);
            const humMax = parseFloat(document.getElementById('humMax').value);
            
            // Status temperatura
            const tempStatus = document.getElementById('temp-status');
            if (data.temperature < tempMin || data.temperature > tempMax) {
                tempStatus.className = 'status alert';
                tempStatus.textContent = 'ALERTA!';
            } else {
                tempStatus.className = 'status ok';
                tempStatus.textContent = 'Normal';
            }
            
            // Status umidade
            const humStatus = document.getElementById('hum-status');
            if (data.humidity < humMin || data.humidity > humMax) {
                humStatus.className = 'status alert';
                humStatus.textContent = 'ALERTA!';
            } else {
                humStatus.className = 'status ok';
                humStatus.textContent = 'Normal';
            }
        }
        
        async function updateChart() {
            try {
                const response = await fetch('/api/history');
                const data = await response.json();
                
                const canvas = document.getElementById('chart');
                const ctx = canvas.getContext('2d');
                
                // Limpar canvas
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                
                if (data.data.length === 0) return;
                
                // Configurações do gráfico
                const padding = 50;
                const width = canvas.width - 2 * padding;
                const height = canvas.height - 2 * padding;
                
                // Encontrar min/max
                const temps = data.data.map(d => d.temperature);
                const hums = data.data.map(d => d.humidity);
                const tempMin = Math.min(...temps) - 2;
                const tempMax = Math.max(...temps) + 2;
                const humMin = Math.min(...hums) - 5;
                const humMax = Math.max(...hums) + 5;
                
                // Desenhar eixos
                ctx.strokeStyle = '#ddd';
                ctx.lineWidth = 1;
                ctx.beginPath();
                ctx.moveTo(padding, padding);
                ctx.lineTo(padding, height + padding);
                ctx.lineTo(width + padding, height + padding);
                ctx.stroke();
                
                // Desenhar temperatura
                ctx.strokeStyle = '#ff6b6b';
                ctx.lineWidth = 2;
                ctx.beginPath();
                
                data.data.forEach((point, index) => {
                    const x = padding + (index / (data.data.length - 1)) * width;
                    const y = padding + (1 - (point.temperature - tempMin) / (tempMax - tempMin)) * height;
                    
                    if (index === 0) {
                        ctx.moveTo(x, y);
                    } else {
                        ctx.lineTo(x, y);
                    }
                });
                ctx.stroke();
                
                // Desenhar umidade
                ctx.strokeStyle = '#4ecdc4';
                ctx.lineWidth = 2;
                ctx.beginPath();
                
                data.data.forEach((point, index) => {
                    const x = padding + (index / (data.data.length - 1)) * width;
                    const y = padding + (1 - (point.humidity - humMin) / (humMax - humMin)) * height;
                    
                    if (index === 0) {
                        ctx.moveTo(x, y);
                    } else {
                        ctx.lineTo(x, y);
                    }
                });
                ctx.stroke();
                
                // Legenda
                ctx.fillStyle = '#ff6b6b';
                ctx.fillRect(padding, 10, 20, 10);
                ctx.fillStyle = '#333';
                ctx.font = '12px Arial';
                ctx.fillText('Temperatura', padding + 25, 20);
                
                ctx.fillStyle = '#4ecdc4';
                ctx.fillRect(padding + 120, 10, 20, 10);
                ctx.fillStyle = '#333';
                ctx.fillText('Umidade', padding + 145, 20);
                
            } catch (error) {
                console.error('Erro ao buscar histórico:', error);
            }
        }
        
        async function saveAlerts() {
            const formData = new FormData();
            formData.append('tempMin', document.getElementById('tempMin').value);
            formData.append('tempMax', document.getElementById('tempMax').value);
            formData.append('humMin', document.getElementById('humMin').value);
            formData.append('humMax', document.getElementById('humMax').value);
            
            try {
                const response = await fetch('/api/alerts', {
                    method: 'POST',
                    body: formData
                });
                
                if (response.ok) {
                    alert('Configurações salvas com sucesso!');
                } else {
                    alert('Erro ao salvar configurações!');
                }
            } catch (error) {
                console.error('Erro ao salvar:', error);
                alert('Erro ao salvar configurações!');
            }
        }
    </script>
</body>
</html>

Upload de Arquivos SPIFFS

Para que a interface web funcione, você precisa fazer upload dos arquivos para o SPIFFS:

  1. Instale o plugin ESP32 Sketch Data Upload
  2. Crie a pasta data no diretório do projeto
  3. Coloque o arquivo index.html na pasta data
  4. Use Tools > ESP32 Sketch Data Upload

Configuração e Uso

1. Primeira Configuração

  1. Carregue o código no ESP32
  2. Abra o Serial Monitor (115200 baud)
  3. Conecte-se ao WiFi "IoT-Monitor-Config" (senha: 12345678)
  4. Acesse http://192.168.4.1
  5. Configure sua rede WiFi nas configurações

2. Operação Normal

Após configurar o WiFi:

  • O ESP32 se conectará automaticamente à sua rede
  • Acesse o IP mostrado no Serial Monitor
  • Monitor os dados em tempo real
  • Configure alertas conforme necessário

Funcionalidades Avançadas

1. Histórico de Dados

O sistema mantém 24 horas de histórico com pontos a cada 10 minutos:

// Estrutura para armazenar dados históricos
struct SensorData {
  float temperature;
  float humidity;
  unsigned long timestamp;
};

SensorData dataHistory[144]; // 24h * 6 pontos/hora

2. Sistema de Alertas

Alertas configuráveis para valores fora da faixa:

void checkAlerts() {
  if (currentData.temperature < tempMin) {
    // Enviar alerta de temperatura baixa
  }
  // ... outros alertas
}

3. Interface Responsiva

A interface se adapta automaticamente a diferentes tamanhos de tela usando CSS Grid e Media Queries.

Melhorias Possíveis

1. Banco de Dados Externo

  • Integração com InfluxDB
  • Grafana para visualização avançada
  • Retenção de dados de longo prazo

2. Notificações

  • E-mail via SMTP
  • Push notifications
  • Integração com Telegram

3. Sensores Adicionais

  • Pressão atmosférica (BMP280)
  • Qualidade do ar (MQ-135)
  • Luminosidade (BH1750)

4. Atuadores

  • Ventilação automática
  • Aquecimento/resfriamento
  • Umidificação

Troubleshooting

Problemas Comuns

1. Sensor não lê valores:

  • Verifique as conexões
  • Confirme se o resistor pull-up está correto
  • Teste com outro sensor DHT22

2. WiFi não conecta:

  • Verifique SSID e senha
  • Use o modo AP para reconfigurar
  • Verifique se a rede é 2.4GHz

3. Interface web não carrega:

  • Confirme se os arquivos foram uploadados para SPIFFS
  • Verifique se o servidor web está funcionando
  • Use F12 no navegador para ver erros

Códigos de Debug

// Adicione debug no código principal
void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true); // Habilita debug WiFi
  
  // ... resto do código
}

Expansões do Projeto

1. Versão com Display

Adicione um display OLED para mostrar dados localmente:

#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.printf("Temp: %.1f C\n", currentData.temperature);
  display.printf("Umid: %.1f %%\n", currentData.humidity);
  display.display();
}

2. Integração MQTT

Para integração com sistemas IoT maiores:

#include <PubSubClient.h>

WiFiClient espClient;
PubSubClient mqtt(espClient);

void publishData() {
  String payload = "{\"temp\":" + String(currentData.temperature) + 
                   ",\"hum\":" + String(currentData.humidity) + "}";
  mqtt.publish("home/sensors/living_room", payload.c_str());
}

Conclusão

Este projeto demonstra como criar um sistema IoT completo usando ESP32, desde a coleta de dados até a visualização web. O sistema é escalável e pode ser facilmente expandido com novos sensores e funcionalidades.

Próximos Passos:

  1. Teste o projeto básico e familiarize-se com o código
  2. Experimente diferentes sensores (pressão, luz, etc.)
  3. Implemente notificações via e-mail ou telegram
  4. Integre com plataformas IoT como Blynk ou ThingSpeak
  5. Adicione controle de atuadores para automação completa

Código completo disponível no GitHub: rafaelabrantest2/iot-monitoring-esp32

Próximo projeto: "Central de Automação Residencial com ESP32 e Relés"