Pular para conteúdo

Integração MQTT

Broker

HiveMQ Cloud (Free Tier), protocolo MQTTS (MQTT sobre TLS), porta 8883.

A conexão usa autenticação por usuário e senha. As credenciais ficam nas variáveis de ambiente MQTT_USERNAME e MQTT_PASSWORD.


Subscriber Django

O subscriber é um management command Django (telemetry/management/commands/run_mqtt_subscriber.py) que usa paho-mqtt. Deve rodar como processo separado (ex: python manage.py run_mqtt_subscriber).

Comportamento obrigatório: - Reconexão automática em caso de queda de conexão. - Mensagens malformadas são descartadas com log; o processo não cai.

Leituras atrasadas (backfill): o firmware (v2/v3) tem um buffer local que reenvia leituras com o ts original ao reconectar após uma queda (ver docs/hardware.md §7) — elas são inseridas no banco com measured_at no passado. Se a queda for mais longa que 3 dias, esse backfill pode cair fora da janela de auto-refresh dos agregados contínuos do TimescaleDB; nesse caso, reprocesse manualmente com python manage.py reprocess_readings (ver docs/DEPLOY.md §9.1).


Tópicos

Telemetria

Tópico: casa/{mqtt_id}/telemetria

Direção: ESP32 → Django

Payload (JSON):

{
  "vrms": 220.5,
  "irms": 4.2,
  "watts": 910.1,
  "fp": 0.98,
  "kwh": 12.34
}

Campo Unidade Model destino
vrms V TelemetryReading.voltage_rms
irms A TelemetryReading.current_rms
watts W TelemetryReading.active_power
fp TelemetryReading.power_factor
kwh kWh TelemetryReading.energy_kwh

Status

Tópico: casa/{mqtt_id}/status

Direção: ESP32 → Django

Payload offline (LWT — Last Will Testament):

{ "estado": "offline" }

Payload online (boot):

{
  "estado": "online",
  "uptime_segundos": 42,
  "motivo_reset": "power_on"
}

Ao receber este tópico a service layer de telemetry: 1. Atualiza Meter.is_online e Meter.last_seen_at. 2. Cria um DeviceStatusEvent. 3. Infere disconnect_cause: - uptime_segundos baixo logo após retorno → 'power_outage' - uptime_segundos alto contínuo → 'network_failure'


Variáveis de ambiente necessárias

MQTT_HOST=<host.hivemq.cloud>
MQTT_PORT=8883
MQTT_USERNAME=<usuario>
MQTT_PASSWORD=<senha>

5. Validação do fluxo hardware → API (sem hardware físico)

Para validar o fluxo completo ESP32 → broker → subscriber → banco → dashboard sem precisar de um medidor físico em mãos, o repositório traz simulator.py (raiz do projeto): um cliente MQTT que publica payloads de telemetria e status exatamente nos mesmos tópicos e formato que o firmware real usa (casa/{mqtt_id}/telemetria, casa/{mqtt_id}/status), lendo as mesmas credenciais do .env (MQTT_HOST/MQTT_PORT/MQTT_USERNAME/MQTT_PASSWORD).

Uso

# Cadastre antes um medidor no sistema com o mesmo mqtt_id (ex.: medidor-01)
python simulator.py --mqtt-id medidor-01

# Intervalo customizado entre leituras (padrão: 5s, igual ao firmware)
python simulator.py --mqtt-id medidor-01 --interval 10

# Tensão e potência base customizadas (útil para simular 127V vs. 220V, ou cargas maiores)
python simulator.py --mqtt-id medidor-01 --interval 2 --voltage 127.0 --power 3000

O script publica estado: online (retido) ao conectar, uma leitura de telemetria a cada --interval segundos (com variação aleatória realista de tensão/potência/fator de potência, e kwh acumulando como o firmware faz), e estado: offline (LWT, retido) ao ser encerrado com Ctrl+C — replicando o ciclo de vida completo de um medidor real, incluindo o cenário de desconexão.

Resultado esperado (validação clara)

Com o backend (runserver + run_mqtt_subscriber) e o simulador rodando:

  1. O terminal do simulador mostra cada mensagem publicada ([#N] Telemetria → casa/medidor-01/telemetria {...})
  2. O terminal do run_mqtt_subscriber confirma o recebimento e a persistência
  3. O medidor aparece online no dashboard em poucos segundos, com os valores de tensão/potência atualizando a cada ciclo
  4. Ao interromper o simulador (Ctrl+C), o medidor deve aparecer offline no dashboard logo em seguida

Esse é o mesmo fluxo de validação usado no Guia de Instalação como alternativa a ter o hardware físico já montado.

Cobertura automatizada

O parsing e a persistência dos payloads (o que o simulator.py exercita de ponta a ponta manualmente) já têm testes unitários dedicados em telemetry/tests.py (ParseTelemetryTest, StoreReadingTest, ProcessStatusPayloadTest) — cobrindo payload válido, campos ausentes, JSON inválido e payload malformado sem derrubar o processo.