Este repositório contém a infraestrutura e os códigos necessários para implantar um sistema completo de monitoramento de energia em tempo real. O projeto utiliza um microcontrolador ESP8266 com o sensor PZEM-004T, enviando dados via MQTT para uma stack local baseada em Docker composta por Mosquitto, Telegraf, InfluxDB e Grafana (Stack TIG).
- Hardware: ESP8266 (NodeMCU) + Sensor de Energia PZEM-004T v3.0.
- Mensageria (Broker): Eclipse Mosquitto (MQTT).
- Coletor de Dados (ETL): Telegraf.
- Banco de Dados de Séries Temporais: InfluxDB 2.7.
- Visualização (Dashboard): Grafana.
Para que os volumes do Docker funcionem corretamente e armazenem seus dados de forma persistente, crie a estrutura de diretórios na sua máquina host (Linux, macOS ou Windows via PowerShell):
# Criação das pastas de configuração e persistência de dados
mkdir ~/mosquitto/config
mkdir ~/mosquitto/data
mkdir ~/mosquitto/log
mkdir ~/influxdb_data
mkdir ~/grafana_data
mkdir ~/telegraf_configAloque os arquivos de configuração nos seus respectivos diretórios:
- Coloque o arquivo
docker-compose.ymlna raiz do seu projeto. - Coloque o arquivo
mosquitto.confem~/mosquitto/config/. - Coloque o arquivo
telegraf.confem~/telegraf_config/.
Para facilitar os testes iniciais e evitar erros de Não Autorizado (rc=5), configure o Mosquitto para aceitar conexões anônimas de qualquer IP:
listener 1883 0.0.0.0
allow_anonymous true
Abra o terminal no diretório onde está o seu docker-compose.yml e execute:
docker compose up -dEste comando fará o download das imagens e iniciará os serviços em segundo plano.
Como estamos usando o InfluxDB v2, a autenticação é baseada em Tokens, o que requer uma configuração pós-instalação.
- Acesse o InfluxDB no navegador:
http://localhost:8086. - Siga o assistente de configuração inicial:
- Username / Password: Defina suas credenciais.
- Organization:
EngEletrica(Deve ser idêntico aotelegraf.conf). - Bucket:
sensor_pzem(Deve ser idêntico aotelegraf.conf).
- Vá no menu lateral: Load Data -> API Tokens -> Generate API Token -> All Access Token.
- Copie o Token gerado e cole no seu arquivo
~/telegraf_config/telegraf.conf:[[outputs.influxdb_v2]] urls = ["[http://127.0.0.1:8086](http://127.0.0.1:8086)"] token = "COLE_SEU_TOKEN_AQUI" organization = "EngEletrica" bucket = "sensor_pzem"
- Reinicie o Telegraf para aplicar o novo token:
docker restart telegraf
Certifique-se de que a variável MQTT_SERVIDOR no código do seu ESP8266 aponta para o IP da máquina onde o Docker está rodando (ex: IP local da sua rede Wi-Fi, descubra usando ipconfig no Windows ou ip a no Linux).
// Exemplo de configuração no código Arduino/C++
const char* MQTT_SERVIDOR = "192.168.1.15"; // IP do Servidor/Notebook
const int MQTT_PORTA = 1883;
const char* MQTT_TOPICO = "tcc/pzem/data";Nota: Como o Mosquitto está configurado com allow_anonymous true neste setup inicial, o ESP8266 se conectará mesmo sem credenciais. Para ambientes de produção, crie um arquivo de senhas (passwd) no Mosquitto.
- Acesse o Grafana no navegador:
http://localhost:3000(Usuário/Senha padrão:admin/admin). - Vá em Connections -> Data Sources e adicione o InfluxDB.
- Na configuração do Data Source:
- Query Language: Mude para
Flux. - URL:
http://localhost:8086(ou o IP local da máquina host). - Auth: Desative a opção "Basic Auth".
- InfluxDB Details: Preencha a
Organization, oDefault Buckete oTokengerado no passo 3.
- Query Language: Mude para
No Grafana, adicione novos painéis utilizando a linguagem Flux. Use os códigos abaixo na área de query.
A. Painel tipo "Stat" (Cards de Valor Atual) Mostra a leitura em tempo real. Exemplo para Tensão (V):
from(bucket: "sensor_pzem")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "pzem_data")
|> filter(fn: (r) => r["_field"] == "tensao")
|> last()
(Para outros parâmetros, altere "tensao" para "corrente", "potencia", "frequencia", ou "fator_potencia", mapeados conforme o JSON enviado pelo ESP8266).
B. Painel tipo "Time Series" (Gráfico de Linhas) Mostra a flutuação ao longo do tempo:
from(bucket: "sensor_pzem")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "pzem_data")
|> filter(fn: (r) => r["_field"] == "tensao")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> yield(name: "mean")
C. Painel "Consumo Acumulado" (Wh) Calcula a diferença da energia gasta apenas no dia atual:
from(bucket: "sensor_pzem")
|> range(start: today())
|> filter(fn: (r) => r["_measurement"] == "pzem_data")
|> filter(fn: (r) => r["_field"] == "energia")
|> spread()