Atividade 2 versao 1.0

This commit is contained in:
2026-01-26 18:24:20 -03:00
commit 947dea7851
12 changed files with 1607 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.pio
.vscode/launch.json
.vscode/c_cpp_properties.json
Relatorio/relatorio_files/*

11
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide",
"wokwi.wokwi-vscode"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"C_Cpp.clang_format_fallbackStyle": "WebKit",
"editor.formatOnSave": true
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2022 Uri Shaked
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# Atividade 1 - Amaro
Um projeto de um ESP32 conectado a três sensores:
- Temperatura
- Umidade
- Gás
Para uma cozinha industrial, ativando três atuadores:
- Um sinal sonoro para caso de vazamento de gás
- Uma coifa para vazamento de gás e diminuição da umidade ambiente
- Ar condicionado para correção da temperatura ambiente
Use [Wokwi](https://marketplace.visualstudio.com/items?itemName=wokwi.wokwi-vscode) para simular.
O broker utilizado está hosteado em um servidor meu, assim como o dashboard.
## Compilando
Utilizei o [PlatformIO](https://platformio.org). Para compilar, [instale o PlatformIO](https://docs.platformio.org/en/latest/core/installation/index.html), e execute:
```
pio run
```
## Simulação
Para simular o projeto, instale [Wokwi for VS Code](https://marketplace.visualstudio.com/items?itemName=wokwi.wokwi-vscode).

BIN
Relatorio/nodered.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

539
Relatorio/relatorio.qmd Normal file
View File

@@ -0,0 +1,539 @@
---
title: "Sistema de Monitoramento Inteligente de Cozinha com ESP32"
subtitle: "Integração MQTT com Ubidots para IoT Industrial"
author: "Amaro Lopes"
institute: "Faculdade de Tecnologia FIAP"
date: today
format:
pdf:
documentclass: book
classoption:
- oneside
toc: true
toc-depth: 4
number-sections: true
geometry:
- margin=1in
fontfamily: libertinus
include-before-body:
text: |
\frontmatter
include-after-body:
text: |
\mainmatter
lang: pt
execute:
echo: false
---
# Introdução
Este projeto implementa um **sistema inteligente de monitoramento para cozinha industrial** utilizando um microcontrolador ESP32 conectado a múltiplos sensores. O sistema detecta condições anormais de operação e ativa atuadores para garantir segurança, integrando-se com a plataforma Ubidots para monitoramento remoto via dashboard interativo, com fallback para um dashboard nodered.
A integração é realizada através de um broker MQTT em **77.37.69.84** que faz forward automático dos dados para a plataforma Ubidots, permitindo visualização em tempo real e histórico de dados com widgets interativos. Alternativamente, um dashboard NodeRed está disponível em **77.37.69.84:1880/dashboard/page1** para monitoramento.
## Contexto
Cozinhas industriais enfrentam desafios de segurança relacionados a:
- Variações bruscas de temperatura
- Acúmulo de vapores e gases
- Condições de umidade inadequadas
- Risco de incêndios por múltiplas causas
Este sistema fornece monitoramento 24/7 com resposta automática a situações críticas.
# Objetivo do Projeto
## Objetivos Gerais
1. Monitorar três parâmetros críticos em ambiente de cozinha industrial
2. Detectar anomalias e ativar sistemas de proteção automaticamente
3. Integrar dados com plataforma IoT profissional (Ubidots)
4. Permitir configuração remota de limiares via MQTT
5. Garantir transmissão confiável de dados em tempo real
## Parâmetros Monitorados
- **Temperatura ambiente** (°C) - Faixa: -40 a +80°C
- **Umidade relativa do ar** (%) - Faixa: 0 a 100%
- **Concentração de gás** (ppm) - Qualidade do ar, detecção de vazamentos
- **Status de alarme** (estado) - Código do sistema (0-4)
## Atuadores Controlados
- **Alarme sonoro** - Ativação de sirene para emergências
- **Coifa/Exaustor** - Remoção de gases e vapores
- **Ar-condicionado** - Controle de temperatura ambiente
# Diagrama de Arquitetura
```{mermaid}
%%| fig-width: 7
graph TB
subgraph "Camada de Sensores"
DHT22["<b>DHT22</b><br/>Temperatura &<br/>Umidade<br/>GPIO 32"]
MQ2["<b>MQ2</b><br/>Qualidade do Ar<br/>(Gás)<br/>GPIO 33 ADC"]
RTC["<b>RTC DS1307</b><br/>Timer para verificação de incêndio<br/>I2C"]
end
subgraph "Camada de Processamento"
ESP32["<b>ESP32 DevKit V4</b><br/>Dual-core 240MHz<br/>WiFi + BLE"]
LOGIC["Lógica de<br/>Detecção de<br/>Alarmes"]
end
subgraph "Camada de Atuadores"
ALARM["<b>Alarme Sonoro</b><br/>Sirene/>GPIO 4"]
COIFA["<b>Coifa/Exaustor</b><br/>Motor 220V<br/>GPIO 17"]
AC["<b>A/C</b><br/>Unidade comercial<br/>GPIO 16"]
end
subgraph "Rede MQTT"
WiFi["<b>WiFi</b><br/>Wokwi-GUEST<br/>2.4GHz"]
MQTT["<b>Broker MQTT</b><br/>77.37.69.84:1883<br/>Mosquitto"]
end
subgraph "Cloud - Ubidots"
UBIDOTS["<b>Plataforma Ubidots</b><br/>Dashboard<br/>Histórico de Dados<br/>Alertas"]
NODERED["<b>NodeRed</b><br/>Dashboard Fallback<br/>77.37.69.84:1880/dashboard/page1"]
end
DHT22 --> ESP32
MQ2 --> ESP32
RTC --> ESP32
ESP32 --> LOGIC
LOGIC --> ALARM
LOGIC --> COIFA
LOGIC --> AC
ESP32 -->|Publica| WiFi
WiFi --> MQTT
MQTT -->|Subscreve| ESP32
MQTT -->|Forward| UBIDOTS
MQTT --> NODERED
```
## Fluxo de Dados Simplificado
1. **Leitura**: Sensores capturam dados continuamente (a cada 2s)
2. **Processamento**: ESP32 verifica limiares e detecta alarmes
3. **Publicação**: Dados publicados em tópico MQTT `cozinha`
4. **Roteamento**: Broker em 77.37.69.84 recebe e roteia para múltiplos dashboards
5. **Integração Cloud**: Ubidots consome via bridge MQTT automático
6. **Integração Fallback**: NodeRed consome dados em tempo real
7. **Visualização**: Dashboards exibem em tempo real com widgets
# Descrição dos Componentes
## Componentes Principais
| Componente | Especificação | Função |
|-----------|---------------|--------|-----------------|
| Microcontrolador | ESP32 DevKit V4 | Processamento central |
| Sensor Temp/Umidade | DHT22 | Leitura ambiental |
| Sensor de Gás | MQ2 | Detecção de gás/ar |
| RTC | DS1307 | Timer para verificação de incêndio |
| Módulos Relé | 3x Relés 5V | Acionamento periféricos |
| Alarme Sonoro | Sirene | Alerta de emergência |
| Coifa | Motor AC 220V | Exaustão de gases |
| Ar-Condicionado | Unidade Comercial | Controle de temperatura |
## Mapeamento de Pinos GPIO
| GPIO | Periférico | Tipo | Função |
|------|-----------|------|--------|
| 4 | Alarme Sonoro | Saída Digital | Ativação de Sirene |
| 16 | Ar-Condicionado | Saída Digital | Controle de relé A/C |
| 17 | Coifa/Exaustor | Saída Digital | Controle de relé coifa |
| 21 | RTC SDA | I2C | Comunicação com DS1307 |
| 22 | RTC SCL | I2C | Comunicação com DS1307 |
| 32 | DHT22 | Entrada Digital | Leitura temperatura/umidade |
| 33 | MQ2 | Entrada Analógica | Leitura ADC (10 bits, 0-1023) |
# Variáveis de Telemetria MQTT
## Publicação: ESP32 → Broker (Tópico: `cozinha`)
**Intervalo**: 2 segundos
**Tipo**: JSON
**Exemplo**:
```json
{
"tmp": 28.50,
"umi": 65.20,
"gas": 850,
"alarme": 0
}
```
### Descrição das Variáveis Publicadas
| Campo | Tipo | Faixa | Unidade | Descrição | Precisão |
|-------|------|-------|---------|-----------|----------|
| `tmp` | Float | -40 a +80 | °C | Temperatura ambiente (DHT22) | ±0.5°C |
| `umi` | Float | 0 a 100 | % | Umidade relativa do ar (DHT22) | ±2% |
| `gas` | Integer | 0 a 1023 | ADC | Concentração de gás (ADC 10bits, MQ2) | 1 LSB |
| `alarme` | Integer | 0 a 4 | Enum | Estado do sistema | - |
**Mapeamento de Estados (campo `alarme`)**:
- `0` = NOMINAL (sem alarme)
- `1` = GAS (gás acima do limiar)
- `2` = TEMP_ALTA (temperatura acima do limiar)
- `3` = UMIDADE_ALTA (umidade acima do limiar)
- `4` = INCENDIO (padrão de incêndio detectado)
## Subscrição: Broker → ESP32
O sistema recebe comandos de configuração remota em tempo real, provenientes de dois dashboards:
| Origem | Tópico | Tipo | Faixa | Padrão | Descrição |
|--------|--------|------|-------|--------|-----------|
| NodeRed / CLI | `cozinha/max_tmp` | Float | 20 a 60 | 30.0 | Limite máximo de temperatura (°C) - Reconfigurável |
| NodeRed / CLI | `cozinha/max_umi` | Float | 30 a 90 | 70.0 | Limite máximo de umidade (%) - Reconfigurável |
A configuração pode ser feita através dos sliders do **NodeRed Dashboard** (http://77.37.69.84:1880/dashboard/page1), como visto na apresentação passada.
**Exemplo de comandos para configuração:**
```bash
# Alterar limite de temperatura para 32°C
mosquitto_pub -h 77.37.69.84 -t "cozinha/max_tmp" -m "32.0"
# Alterar limite de umidade para 75%
mosquitto_pub -h 77.37.69.84 -t "cozinha/max_umi" -m "75.0"
# Verificar tópico em tempo real
mosquitto_sub -h 77.37.69.84 -t "cozinha"
```
# Configuração do Sistema
## Limiares de Alarme Padrão
| Parâmetro | Ativação | Desativação | Histerese | Tipo |
|-----------|----------|-------------|-----------|------|
| Temperatura | 30.0 °C | 28.0 °C | 2.0 °C | Upper limit com histerese |
| Umidade | 70.0 % | 65.0 % | 5.0 % | Upper limit com histerese |
| Gás (MQ2) | 940 ppm | 916 ppm | 24 ppm | Upper limit com histerese |
**Histerese (Debounce)**: Implementa margem de segurança para evitar oscilações frequentes entre estados ativo/inativo.
## Estados do Sistema
O sistema opera em **5 estados distintos**, com prioridades hierárquicas:
| Estado | Código | Alarme | Coifa | A/C | Descrição | Ação |
|-------------|--------|--------|-------|-----|-----------|-----------|------|
| NOMINAL | 0 | - | - | - | Operação normal, sem alarmes | Todos desativados |
| GAS | 1 | X | X | - | Gás detectado acima do limiar | Alarme + Exaustão |
| TEMP_ALTA | 2 | - | - | X | Temperatura acima do limiar | Resfriamento |
| UMIDADE_ALTA | 3 | - | X | - | Umidade acima do limiar | Exaustão |
| INCENDIO | 4 | X | X | X | Padrão de incêndio detectado | Máxima proteção |
## Detecção Avançada de Incêndio
O sistema implementa detecção inteligente de incêndio baseada em **padrão temporal**, não apenas em limiar único de temperatura:
**Parâmetros de Monitoramento**:
- **Janela de tempo**: 30 segundos
- **Aumento mínimo de temperatura**: $\Delta T > 5°C$
- **Queda mínima de umidade**: $\Delta UR < -10\%$
**Condição de Alerta de Incêndio**:
$$\text{Incêndio} = (\Delta T > 5°C) \land (\Delta UR < -10\%) \text{ em 30 segundos}$$
**Vantagem**: Reduz falsos positivos comparado à detecção por limiar único. Um aumento isolado de temperatura ou queda de umidade não ativa alarme.
# Fluxo de Dados e Integração
## Sequência de Operação Completa
```{mermaid}
%%| fig-width: 7.5
sequenceDiagram
participant DHT as DHT22<br/>(Sensor)
participant MQ as MQ2<br/>(Sensor)
participant ESP as ESP32<br/>(Processador)
participant WiFi as WiFi<br/>
participant Broker as Broker MQTT<br/>77.37.69.84
participant Ubidots as Ubidots<br/>(Cloud)
participant Dashboard as Dashboard<br/>(Visualização)
loop A cada 2 segundos
DHT->>ESP: Temperatura, Umidade
MQ->>ESP: ADC (Gás)
ESP->>ESP: Detecta Alarmes
ESP->>WiFi: JSON payload
WiFi->>Broker: PUBLISH cozinha
Broker->>Ubidots: Forward automático
Ubidots->>Dashboard: Atualiza widgets
end
```
## Integração MQTT com Ubidots
**Tipo de Integração**: Bridge MQTT com Forward Automático
**Configuração do Forward**:
- **Source Broker**: 77.37.69.84:1883
- **Tópico Source**: `cozinha`
- **Destination**: Ubidots Cloud
- **Autenticação**: Token do device Ubidots
**Processamento no Ubidots**:
- Cada variável JSON é extraída e armazenada
- Histórico de 30+ dias
- Alertas configuráveis por variável
- Dashboard com widgets em tempo real
**Processamento no NodeRed**:
- Consumo direto de mensagens MQTT em tempo real
- Acesso em: **http://77.37.69.84:1880/dashboard/page1**
- Controle remoto de limiares via sliders
- Visualização gráfica em tempo real
# Ciclo de Operação
## Fluxograma do Loop Principal
### Parte 1: Leitura e Processamento
```{mermaid}
%%| fig-width: 6.5
flowchart LR
A["INÍCIO<br/>Loop"] --> B{"MQTT<br/>OK?"}
B -->|Não| C["Reconecta"]
C --> D["Lê Sensores"]
B -->|Sim| D
D --> E["DHT22"]
D --> F["MQ2"]
D --> G["RTC"]
E --> H["Detecta<br/>Alarmes"]
F --> H
G --> H
H --> I["Publica"]
```
### Parte 2: Decisão de Alarmes
```{mermaid}
%%| fig-width: 6.5
flowchart LR
H["Detecta<br/>Alarmes"] --> G{"Incêndio?"}
G -->|Sim| S4["Estado = 4"]
G -->|Não| G2{"Gás<br>>940?"}
G2 -->|Sim| S1["Estado = 1"]
G2 -->|Não| T{"Temp<br/>Alta?"}
T -->|Sim| S2["Estado = 2"]
T -->|Não| U{"Umidade<br/>Alta?"}
U -->|Sim| S3["Estado = 3"]
U -->|Não| S0["Estado = 0"]
S4 --> M["Publica<br/>MQTT"]
S1 --> M
S2 --> M
S3 --> M
S0 --> M
M --> A["Ativa<br/>Atuadores"]
A --> B["Aguarda 2s"]
B --> H
```
# Validação e Testes
## Protocolo de Validação
A validação do sistema foi realizada através dos seguintes testes:
### Teste 1: Conectividade WiFi
- Conexão com SSID Wokwi-GUEST
- Obtenção de IP via DHCP
### Teste 2: Conectividade MQTT
- Conexão com broker 77.37.69.84:1883
- Publicação de mensagens a cada 2s
- Recebimento de comandos de configuração
### Teste 3: Leitura de Sensores
- DHT22: Temperatura entre 15-35°C (simulado)
- DHT22: Umidade entre 40-80% (simulado)
- MQ2: ADC entre 0-1023 (simulado)
### Teste 4: Detecção de Alarmes
- Temperatura > 30°C ativa ar-condicionado
- Umidade > 70% ativa coifa
- Gás > 940 ativa alarme + coifa
- Padrão de incêndio (ΔT>5°C + ΔUR<-10%) ativa todos
### Teste 5: Integração Ubidots
- Dados recebidos no dashboard
- Histórico armazenado
- Widgets atualizados em tempo real
## Monitoramento de Dados
**Captura do Monitor Serial** (origin dos dados):
```
[SETUP] Iniciando sistema...
[DHT] Inicializado
[WiFi] Conectando.....
[WiFi] Conectado!
[MQTT] Conectando...
[MQTT] Conectado!
[MQTT] Tópico: cozinha/max_tmp
[MQTT] Tópico: cozinha/max_umi
[RTC] Inicializado
[MQTT] Publicando: {"tmp": 26.10, "umi": 63.50, "gas": 906, "alarme": 0}
[SETUP] Pronto!
[MQTT] Publicando: {"tmp": 26.10, "umi": 63.50, "gas": 906, "alarme": 0}
[MQTT] Publicando: {"tmp": 26.10, "umi": 63.50, "gas": 906, "alarme": 0}
[MQTT] Publicando: {"tmp": 26.10, "umi": 63.50, "gas": 906, "alarme": 0}
[MQTT] Publicando: {"tmp": 26.10, "umi": 63.50, "gas": 983, "alarme": 0}
[ALARME] GÁS DETECTADO!
```
## Comparação: Origem vs. Ubidots
| Métrica | Monitor Serial | Ubidots | Status |
|---------|---|---|---|
| Temperatura | 28.50°C | 28.50°C | Correspondência |
| Umidade | 65.20% | 65.20% | Correspondência |
| Gás | 850 ADC | 850 ADC | Correspondência |
| Alarme | 0 (NOMINAL) | 0 | Correspondência |
**Conclusão**: Dados transmitidos corretamente sem perda ou corrupção.
# Dashboard Ubidots
### Configuração do Device
**Nome do Device**: `cozinha`
**Tipo**: Sensor IoT
**Variáveis Configuradas**:
| Variável | Tipo | Unidade | Limites | Status |
|----------|------|---------|---------|--------|
| temperatura | Float | °C | -40 a +80 | Ativa |
| umidade | Float | % | 0 a 100 | Ativa |
| gas | Integer | ppm | 0 a 1023 | Ativa |
| alarme | Integer | Enum | 0 a 4 | Ativa |
## Widgets no Dashboard
### Widget 1: Gauge Temperatura
- **Variável**: temperatura
- **Mín**: 15°C | **Máx**: 40°C
- **Alerta**: > 30°C (vermelho)
### Widget 2: Gauge Umidade
- **Variável**: umidade
- **Mín**: 30% | **Máx**: 90%
- **Alerta**: > 70% (laranja)
### Widget 3: Gauge Gás
- **Variável**: gas
- **Mín**: 500 ppm | **Máx**: 1000 ppm
- **Alerta**: > 940 ppm (vermelho)
### Widget 4: Indicador de Estado
- **Variável**: alarme
- **Estados**:
- 0 = Verde (NOMINAL)
- 1 = Vermelho (GAS)
- 2 = Laranja (TEMP_ALTA)
- 3 = Amarelo (UMIDADE_ALTA)
- 4 = Vermelho (INCENDIO)
### Widget 5: Gráfico de Histórico (Última 24h)
- **Variáveis**: temperatura, umidade, gas
- **Tipo**: Linha com pontos
- **Intervalo**: Últimas 24 horas
## Alertas Configurados
| Evento | Condição | Ação |
|--------|----------|------|
| Temperatura Alta | tmp > 30°C | Email |
| Umidade Alta | umi > 70% | Nada |
| Gás Detectado | gas > 940 | SMS |
| Incêndio | alarme == 4 | SMS + Email |
## Screenshot do Dashboard
![Dashboard no Ubidots](ubidots.png)
**Conteúdo Esperado**:
- 4 widgets numéricos com valores em tempo real
- Gráfico histórico de 24 horas
- Status indicador colorido
- Últimas leituras: data/hora
## Dashboard NodeRed - Monitoramento Fallback
### Acesso
O sistema também disponibiliza um dashboard NodeRed para monitoramento alternativo ao Ubidots:
**URL**: http://77.37.69.84:1880/dashboard/page1
![Dashboard no NodeRed](nodered.png)
### Recursos Disponíveis
| Recurso | Descrição | Funcionalidade |
|---------|-----------|-----------------|
| Indicadores Numéricos | Temperatura, Umidade, Gás | Valores em tempo real |
| Status do Alarme | Código 0-4 | Indicador colorido (verde/vermelho/amarelo) |
| Gráfico em Tempo Real | Histórico 2 minutos | Visualização de tendências |
| Slider Temperatura | Input de controle | Alterar limite máximo de temp |
| Slider Umidade | Input de controle | Alterar limite máximo de umidade |
### Configuração de Sliders
Os sliders do dashboard NodeRed publicam diretamente nos tópicos MQTT:
```
Slider Temperatura → cozinha/max_tmp
Slider Umidade → cozinha/max_umi
```
**Operação**:
1. Mover slider de temperatura
2. NodeRed publica valor em `cozinha/max_tmp`
3. ESP32 subscreve e recebe o comando
4. Limite é atualizado imediatamente
# Conclusões
## Funções Implementadas
1. **Detecção de Incêndio em 2D**: Combinação de ΔT e ΔUR em janela temporal
2. **Configuração Remota**: Limiares ajustáveis via MQTT em tempo real
3. **Histerese Dinâmica**: Evita oscilações entre estados
4. **Priorização de Alarmes**: Sistema hierárquico de estados
5. **Integração Cloud**: Dashboard profissional com Ubidots
## Aplicações Práticas
Este sistema pode ser estendido para:
- Outras ambientes: restaurantes, indústrias alimentícias
- Múltiplos sensores: CO2, fumaça, luminosidade
- Integração com sistemas SCADA
- Análise preditiva com machine learning
- Mobile app de alertas
## Recomendações Futuras
- [ ] Adicionar certificado SSL/TLS para segurança
- [ ] Implementar backup local em SD card
- [ ] Criar relatórios mensais de histórico
- [ ] Adicionar IA para detecção de padrões anormais
---
**Última Atualização**: 26 de janeiro de 2026
**Versão**: 1.0
**Status**: Finalizado

BIN
Relatorio/ubidots.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

551
diagram.json Normal file
View File

@@ -0,0 +1,551 @@
{
"version": 1,
"author": "Amaro Lopes",
"editor": "wokwi",
"parts": [
{
"type": "board-esp32-devkit-c-v4",
"id": "esp",
"top": -19.2,
"left": -119.96,
"attrs": {}
},
{
"type": "wokwi-gas-sensor",
"id": "gas1",
"top": 98.7,
"left": -559.4,
"attrs": {}
},
{
"type": "wokwi-relay-module",
"id": "relay1",
"top": 86.6,
"left": 96,
"attrs": {}
},
{
"type": "wokwi-dht22",
"id": "dht1",
"top": -86.1,
"left": -331.8,
"attrs": {
"temperature": "61"
}
},
{
"type": "wokwi-gnd",
"id": "gnd2",
"top": 268.8,
"left": -298.2,
"attrs": {}
},
{
"type": "wokwi-led",
"id": "led1",
"top": 82.8,
"left": 330.6,
"attrs": {
"color": "red",
"flip": "1"
}
},
{
"type": "wokwi-gnd",
"id": "gnd3",
"top": 268.8,
"left": 345,
"attrs": {}
},
{
"type": "wokwi-resistor",
"id": "r1",
"top": 119.15,
"left": 259.2,
"attrs": {
"value": "220"
}
},
{
"type": "wokwi-vcc",
"id": "vcc3",
"top": -181.64,
"left": -412.8,
"attrs": {}
},
{
"type": "wokwi-vcc",
"id": "vcc4",
"top": -181.64,
"left": 403.2,
"attrs": {}
},
{
"type": "wokwi-relay-module",
"id": "relay2",
"top": 0.2,
"left": 124.8,
"attrs": {}
},
{
"type": "wokwi-led",
"id": "led2",
"top": -3.6,
"left": 359.4,
"attrs": {
"color": "green",
"flip": "1"
}
},
{
"type": "wokwi-resistor",
"id": "r2",
"top": 32.75,
"left": 288,
"attrs": {
"value": "220"
}
},
{
"type": "wokwi-relay-module",
"id": "relay3",
"top": 86.6,
"left": 96,
"attrs": {}
},
{
"type": "wokwi-led",
"id": "led3",
"top": 82.8,
"left": 330.6,
"attrs": {
"color": "red",
"flip": "1"
}
},
{
"type": "wokwi-resistor",
"id": "r3",
"top": 119.15,
"left": 259.2,
"attrs": {
"value": "220"
}
},
{
"type": "wokwi-relay-module",
"id": "relay4",
"top": -86.2,
"left": 134.4,
"attrs": {}
},
{
"type": "wokwi-led",
"id": "led4",
"top": -90,
"left": 369,
"attrs": {
"color": "blue",
"flip": "1"
}
},
{
"type": "wokwi-resistor",
"id": "r4",
"top": -53.65,
"left": 297.6,
"attrs": {
"value": "220"
}
},
{
"type": "wokwi-text",
"id": "text1",
"top": -76.8,
"left": 422.4,
"attrs": {
"text": "COIFA"
}
},
{
"type": "wokwi-text",
"id": "text2",
"top": 9.6,
"left": 422.4,
"attrs": {
"text": "AR CONDICIONADO"
}
},
{
"type": "wokwi-text",
"id": "text3",
"top": 96,
"left": 422.4,
"attrs": {
"text": "ALARME SONORO"
}
},
{
"type": "wokwi-ds1307",
"id": "rtc1",
"top": 253.8,
"left": 48.1,
"attrs": {}
}
],
"connections": [
[
"esp:TX",
"$serialMonitor:RX",
"",
[]
],
[
"esp:RX",
"$serialMonitor:TX",
"",
[]
],
[
"dht1:GND",
"gnd2:GND",
"black",
[
"v0"
]
],
[
"dht1:SDA",
"esp:32",
"green",
[
"v0"
]
],
[
"gas1:GND",
"gnd2:GND",
"black",
[
"h0"
]
],
[
"led1:A",
"r1:2",
"green",
[
"v0"
]
],
[
"led1:C",
"gnd3:GND",
"black",
[
"v0"
]
],
[
"relay1:NO",
"r1:1",
"green",
[
"h0"
]
],
[
"vcc3:VCC",
"vcc4:VCC",
"red",
[
"v19.2",
"h0",
"v0",
"h57.6"
]
],
[
"esp:3V3",
"vcc3:VCC",
"red",
[
"h-38.25",
"v-144",
"h0",
"v0",
"h-249.6"
]
],
[
"dht1:VCC",
"vcc3:VCC",
"red",
[
"v9.6",
"h-38.4"
]
],
[
"gnd2:GND",
"gnd3:GND",
"black",
[
"v-28.8",
"h0",
"v0",
"h595.2"
]
],
[
"esp:GND.2",
"gnd3:GND",
"black",
[
"v0",
"h57.6",
"v230.4",
"h268.8"
]
],
[
"vcc4:VCC",
"relay1:COM",
"red",
[
"v19.2",
"h-134.4",
"v249.6",
"h-19.2"
]
],
[
"gas1:VCC",
"vcc3:VCC",
"red",
[
"h0"
]
],
[
"relay1:VCC",
"vcc4:VCC",
"red",
[
"h-28.8",
"v-240",
"h345.6"
]
],
[
"relay1:GND",
"gnd2:GND",
"black",
[
"h-19.2",
"v124.4",
"h-364.8"
]
],
[
"relay1:IN",
"esp:4",
"green",
[
"h0"
]
],
[
"led2:A",
"r2:2",
"green",
[
"v0"
]
],
[
"relay2:NO",
"r2:1",
"green",
[
"h0"
]
],
[
"led2:C",
"gnd3:GND",
"black",
[
"v201.6",
"h-29.2"
]
],
[
"relay2:VCC",
"vcc4:VCC",
"red",
[
"h-57.6",
"v-153.6",
"h345.6"
]
],
[
"relay2:GND",
"gnd2:GND",
"black",
[
"h-48",
"v210.8",
"h-240"
]
],
[
"relay2:IN",
"esp:16",
"green",
[
"h-67.2",
"v76.6"
]
],
[
"led3:A",
"r3:2",
"green",
[
"v0"
]
],
[
"relay3:NO",
"r3:1",
"green",
[
"h0"
]
],
[
"led4:A",
"r4:2",
"green",
[
"v0"
]
],
[
"relay4:NO",
"r4:1",
"green",
[
"h0"
]
],
[
"led4:C",
"gnd3:GND",
"black",
[
"v288",
"h-38.8"
]
],
[
"relay4:VCC",
"vcc4:VCC",
"red",
[
"h-67.2",
"v-67.2",
"h345.6"
]
],
[
"relay4:GND",
"gnd2:GND",
"black",
[
"h-57.6",
"v297.2",
"h-268.8",
"v0",
"h-96"
]
],
[
"relay4:IN",
"esp:17",
"green",
[
"h-86.4",
"v-0.2"
]
],
[
"relay2:COM",
"vcc4:VCC",
"red",
[
"h39.6",
"v-164.6",
"h134.4"
]
],
[
"relay4:COM",
"vcc4:VCC",
"red",
[
"h30",
"v-78.2",
"h134.4"
]
],
[
"gas1:AOUT",
"esp:33",
"green",
[
"h230.4",
"v-9.6"
]
],
[
"rtc1:GND",
"esp:CMD",
"black",
[
"h-192",
"v-96"
]
],
[
"esp:5V",
"rtc1:5V",
"red",
[
"h-9.45",
"v96"
]
],
[
"rtc1:SCL",
"esp:22",
"green",
[
"h-38.4",
"v-269"
]
],
[
"rtc1:SDA",
"esp:21",
"green",
[
"h-48",
"v-230.7"
]
]
],
"dependencies": {}
}

15
platformio.ini Normal file
View File

@@ -0,0 +1,15 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32]
platform = espressif32
framework = arduino
board = esp32dev
lib_deps = hwspeedy/DHT-Sensor, knolleary/PubSubClient, adafruit/RTClib

429
src/esp32-ntp-clock.ino Normal file
View File

@@ -0,0 +1,429 @@
// ============================================================================
// Sistema de monitoramento de cozinha com ESP32
// Monitora temperatura, umidade e gás com detecção avançada de incêndio
// Envia dados via MQTT e controla coifa e ar-condicionado
// ============================================================================
// INCLUDES
#include <DHT.h> // Sensor DHT22
#include <PubSubClient.h> // MQTT
#include <RTClib.h> // RTC DS1307
#include <WiFi.h> // WiFi
// ============================================================================
// DEFINES - Pinos GPIO
// ============================================================================
#define DHTPIN 32 // Sensor DHT22 (temperatura/umidade)
#define DHTTYPE DHT22 // Tipo sensor DHT
#define MQ2PIN 33 // Sensor MQ2 (qualidade do ar/gás)
#define ALARME_SONORO 4 // Alarme sonoro
#define COIFA 17 // Coifa (exaustor)
#define AR_CONDICIONADO 16 // Ar-condicionado
// ============================================================================
// ENUMS
// ============================================================================
enum statusCozinha {
NOMINAL, // Sem alerta
GAS, // Detecção de gás
TEMP_ALTA, // Temperatura acima do limite
UMIDADE_ALTA, // Umidade acima do limite
INCENDIO // Possível incêndio (padrão: ΔT > 5°C + Δumidade < -10% em 30s)
};
// ============================================================================
// CONSTANTES - Limiares de alarme
// ============================================================================
const int GAS_ON = 940; // Ativação gás (~1000ppm)
const int GAS_OFF = 916; // Desativação gás (~500ppm)
const float TEMP_ON_DEFAULT = 30.0; // Temperatura máxima padrão (°C)
const float TEMP_HISTERESE = 2.0; // Histerese temperatura
const float UMIDADE_ON_DEFAULT = 70.0; // Umidade máxima padrão (%)
const float UMIDADE_HISTERESE = 5.0; // Histerese umidade
// Detecção de incêndio
const uint32_t JANELA_TEMPO_INCENDIO = 30; // Janela: 30 segundos
const float DELTA_TEMP_INCENDIO = 5.0; // Aumento mínimo: 5°C
const float DELTA_UMIDADE_INCENDIO = -10.0; // Queda mínima: -10%
// ============================================================================
// CONSTANTES - WiFi e MQTT
// ============================================================================
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
const char* MQTT_BROKER = "77.37.69.84";
const int MQTT_PORT = 1883;
const char* MQTT_SUB_TOPICS[] = { "cozinha/max_tmp", "cozinha/max_umi" };
const char* MQTT_PUB_TOPIC = "cozinha";
const int MQTT_SUB_COUNT = sizeof(MQTT_SUB_TOPICS) / sizeof(MQTT_SUB_TOPICS[0]);
// ============================================================================
// VARIÁVEIS GLOBAIS - Objetos
// ============================================================================
DHT dht(DHTPIN, DHTTYPE);
RTC_DS1307 RTC;
WiFiClient espClient;
PubSubClient mqtt(espClient);
DateTime now;
// ============================================================================
// VARIÁVEIS GLOBAIS - Estado do sistema
// ============================================================================
int alarmStatus = NOMINAL;
float umidade = 0.0;
float temperatura = 0.0;
int iqAR = 0;
// Variáveis de configuração (atualizáveis via MQTT)
float TEMP_ON = TEMP_ON_DEFAULT;
float TEMP_OFF = TEMP_ON - TEMP_HISTERESE;
float UMIDADE_ON = UMIDADE_ON_DEFAULT;
float UMIDADE_OFF = UMIDADE_ON - UMIDADE_HISTERESE;
// Variáveis para detecção de incêndio
float temp_inicio = 0.0;
float umidade_inicio = 0.0;
uint32_t timestamp_inicio = 0;
// Buffer MQTT
char msg[256];
char MQTT_CLIENTID[32];
// ============================================================================
// FUNÇÕES AUXILIARES - Callbacks MQTT
// ============================================================================
void mqttCallback(char* topic, byte* payload, unsigned int length)
{
Serial.print("[MQTT] Mensagem em [");
Serial.print(topic);
Serial.println("]");
// Copia payload para buffer null-terminated
char buf[64];
int n = (length < (int)sizeof(buf) - 1) ? length : (int)sizeof(buf) - 1;
memcpy(buf, payload, n);
buf[n] = '\0';
Serial.print("[MQTT] payload: ");
Serial.println(buf);
// Processa tópicos
if (strcmp(topic, "cozinha/max_tmp") == 0) {
float v = atof(buf);
if (!isnan(v)) {
TEMP_ON = v;
TEMP_OFF = TEMP_ON - TEMP_HISTERESE;
Serial.print("[MQTT] TEMP_ON = ");
Serial.println(TEMP_ON);
}
} else if (strcmp(topic, "cozinha/max_umi") == 0) {
float v = atof(buf);
if (!isnan(v)) {
UMIDADE_ON = v;
UMIDADE_OFF = UMIDADE_ON - UMIDADE_HISTERESE;
Serial.print("[MQTT] UMIDADE_ON = ");
Serial.println(UMIDADE_ON);
}
}
}
// ============================================================================
// FUNÇÕES AUXILIARES - Conectividade
// ============================================================================
// Conecta o ESP32 à rede WiFi
void connectWiFi()
{
Serial.print("[WiFi] Conectando...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
Serial.println("\r\n[WiFi] Conectado!");
}
// Inscreve em tópicos MQTT para receber atualizações de limiares
void mqttSubscribe()
{
Serial.println("[MQTT] Conectado!");
for (int i = 0; i < MQTT_SUB_COUNT; ++i) {
if (mqtt.subscribe(MQTT_SUB_TOPICS[i])) {
Serial.print("[MQTT] Tópico: ");
Serial.println(MQTT_SUB_TOPICS[i]);
}
}
}
// Conecta ao broker MQTT
void connectMQTT()
{
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
mqtt.setCallback(mqttCallback);
while (!mqtt.connected()) {
Serial.println("[MQTT] Conectando...");
if (mqtt.connect(MQTT_CLIENTID)) {
mqttSubscribe();
break;
} else {
Serial.print("[MQTT] Falha rc=");
Serial.println(mqtt.state());
delay(2000);
}
}
}
// ============================================================================
// FUNÇÕES AUXILIARES - Sensores
// ============================================================================
// Lê valores de temperatura e umidade do sensor DHT22
void read_DHT()
{
umidade = dht.readHumidity();
temperatura = dht.readTemperature();
if (isnan(umidade) || isnan(temperatura)) {
Serial.println("[DHT] Erro na leitura!");
umidade = 0.0;
temperatura = 0.0;
}
}
void read_MQ2()
{
iqAR = analogRead(MQ2PIN);
}
// ============================================================================
// FUNÇÕES - Detecção de alarmes
// ============================================================================
// Detecta padrão de possível incêndio: aumento de temperatura > 5°C + queda de umidade > 10% em 30 segundos
void detect_fire()
{
// Inicializa janela de monitoramento na primeira chamada
if (temp_inicio == 0.0) {
temp_inicio = temperatura;
umidade_inicio = umidade;
timestamp_inicio = now.secondstime();
return;
}
// Calcula tempo decorrido
uint32_t tempo_decorrido = now.secondstime() - timestamp_inicio;
// Verifica padrão de incêndio a cada 30 segundos
if (tempo_decorrido >= JANELA_TEMPO_INCENDIO) {
float delta_temp = temperatura - temp_inicio;
float delta_umidade = umidade - umidade_inicio;
// Debug: exibe deltas
Serial.print("[INCENDIO] ΔT: ");
Serial.print(delta_temp);
Serial.print("°C | ΔUmidade: ");
Serial.print(delta_umidade);
Serial.println("%");
// Verifica condições críticas
if (delta_temp > DELTA_TEMP_INCENDIO && delta_umidade < DELTA_UMIDADE_INCENDIO) {
Serial.println("[ALERTA] INCÊNDIO DETECTADO!");
alarmStatus = INCENDIO;
}
// Reseta janela
temp_inicio = temperatura;
umidade_inicio = umidade;
timestamp_inicio = now.secondstime();
}
}
// Verifica qual alarme deve ser ativado baseado nos limites
void check_alarm()
{
// Atualiza RTC
now = RTC.now();
// Detecção de incêndio (máxima prioridade)
detect_fire();
if (alarmStatus == INCENDIO) {
return;
}
// Verifica gás
if (alarmStatus == GAS) {
if (iqAR <= GAS_OFF) {
alarmStatus = NOMINAL;
}
} else if (iqAR > GAS_ON) {
alarmStatus = GAS;
return;
}
// Verifica umidade
if (alarmStatus == UMIDADE_ALTA) {
if (umidade <= UMIDADE_OFF) {
alarmStatus = NOMINAL;
}
} else if (umidade > UMIDADE_ON) {
alarmStatus = UMIDADE_ALTA;
return;
}
// Verifica temperatura
if (alarmStatus == TEMP_ALTA) {
if (temperatura <= TEMP_OFF) {
alarmStatus = NOMINAL;
}
} else if (temperatura > TEMP_ON) {
alarmStatus = TEMP_ALTA;
return;
}
// Nenhum alarme ativo
if (alarmStatus != GAS && alarmStatus != UMIDADE_ALTA
&& alarmStatus != TEMP_ALTA) {
alarmStatus = NOMINAL;
}
}
// ============================================================================
// FUNÇÕES - Leitura e publicação
// ============================================================================
// Realiza leitura de todos os sensores e publica dados via MQTT
void leitura_sensores()
{
read_DHT();
read_MQ2();
// Formata e publica via MQTT
snprintf(msg, sizeof(msg),
"{\"tmp\": %.2f, \"umi\": %.2f, \"gas\": %d, \"alarme\": %d}",
temperatura, umidade, iqAR, alarmStatus);
Serial.print("[MQTT] Publicando: ");
Serial.println(msg);
mqtt.publish(MQTT_PUB_TOPIC, msg);
// Verifica alarmes
check_alarm();
}
// Processa ativação de periféricos baseado no alarme
void processa_alarme()
{
// Desativa todos antes de processar
digitalWrite(ALARME_SONORO, LOW);
digitalWrite(COIFA, LOW);
digitalWrite(AR_CONDICIONADO, LOW);
if (alarmStatus == NOMINAL) {
return;
}
// Ativa periféricos conforme alarme
switch (alarmStatus) {
case INCENDIO:
digitalWrite(ALARME_SONORO, HIGH);
digitalWrite(COIFA, HIGH);
digitalWrite(AR_CONDICIONADO, HIGH);
Serial.println("[ALARME] INCÊNDIO!");
break;
case GAS:
digitalWrite(ALARME_SONORO, HIGH);
digitalWrite(COIFA, HIGH);
Serial.println("[ALARME] GÁS DETECTADO!");
break;
case UMIDADE_ALTA:
digitalWrite(COIFA, HIGH);
Serial.println("[ALARME] UMIDADE ALTA!");
break;
case TEMP_ALTA:
digitalWrite(AR_CONDICIONADO, HIGH);
Serial.println("[ALARME] TEMPERATURA ALTA!");
break;
default:
break;
}
}
// ============================================================================
// FUNÇÕES - Inicialização
// ============================================================================
void rtc_setup()
{
if (!RTC.begin()) {
Serial.println("[RTC] Erro: não encontrado!");
while (1)
delay(1000);
}
Serial.println("[RTC] Inicializado");
}
// Inicializa o ESP32 e configura os periféricos
void setup()
{
Serial.begin(115200);
delay(100);
Serial.println("\n[SETUP] Iniciando sistema...");
// Inicializa DHT
dht.begin();
Serial.println("[DHT] Inicializado");
// Gera ID único MQTT
snprintf(MQTT_CLIENTID, sizeof(MQTT_CLIENTID), "esp32_%08X",
(uint32_t)(ESP.getEfuseMac() & 0xFFFFFFFF));
// Configura ADC
analogReadResolution(10);
// Configura pinos
pinMode(ALARME_SONORO, OUTPUT);
pinMode(COIFA, OUTPUT);
pinMode(AR_CONDICIONADO, OUTPUT);
pinMode(MQ2PIN, INPUT);
pinMode(DHTPIN, INPUT);
// Desativa todos os atuadores
digitalWrite(ALARME_SONORO, LOW);
digitalWrite(COIFA, LOW);
digitalWrite(AR_CONDICIONADO, LOW);
// Conecta
connectWiFi();
connectMQTT();
rtc_setup();
// Primeira leitura
now = RTC.now();
leitura_sensores();
Serial.println("[SETUP] Pronto!\n");
}
// ============================================================================
// LOOP PRINCIPAL
// ============================================================================
// Loop principal - executa continuamente
void loop()
{
// Verifica MQTT
if (!mqtt.connected()) {
connectMQTT();
}
mqtt.loop();
// Lê sensores
leitura_sensores();
// Processa alarme
processa_alarme();
// Aguarda 2 segundos
unsigned long start = millis();
while (millis() - start < 2000UL) {
mqtt.loop();
delay(10);
}
}

4
wokwi.toml Normal file
View File

@@ -0,0 +1,4 @@
[wokwi]
version = 1
elf = ".pio/build/esp32/firmware.elf"
firmware = ".pio/build/esp32/firmware.bin"