Automação IoT

Monitoramento de Bateria Residencial com MQTT: Estado de Carga, Tensão das Células e Alertas de Bateria Baixa

Publique dados do BMS, como estado de carga, tensão do pack, corrente, diferença de tensão entre células e temperatura, em um broker MQTT e adicione alertas de bateria baixa e desbalanceamento com histerese para saber dos problemas antes que o inversor desligue.

Banco de baterias de lítio na parede com leituras do BMS de estado de carga e tensão das células exibidas em um painel de monitoramento de bateria residencial
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

O monitoramento de bateria residencial mostra quanta energia você tem armazenada, o quanto a bateria está trabalhando e se alguma célula está saindo da linha muito antes de isso virar uma falha. Neste tutorial você vai publicar dados do sistema de gerenciamento de bateria (BMS), como estado de carga, tensão do pack, corrente, tensões das células e temperatura, em um broker MQTT, usando como exemplo prático um sistema Victron com Venus OS, comum em casas e sítios com energia solar. Você também vai adicionar alertas de bateria baixa e de desbalanceamento de células que não inundam o seu celular.

O que monitorar em uma bateria residencial

Um BMS expõe dezenas de valores. Estes são os que importam para a operação do dia a dia e para a detecção precoce de falhas:

Valor Unidade Por que importa Ideia de alerta típica
Estado de carga (SoC) % Energia disponível para a noite e a madrugada Baixo abaixo de 20 %, normaliza acima de 25 %
Tensão do pack V Confere o SoC e detecta falhas de sensor Fora da faixa normal da química
Corrente A Positiva carregando, negativa descarregando Corrente sustentada perto do limite do BMS
Potência W Tensão vezes corrente; fácil de plotar junto com FV e rede Informativo
Tensão mínima / máxima de célula V Célula mais fraca e mais forte Diferença acima de cerca de 50 mV em repouso
Temperatura °C Baterias de lítio não devem carregar abaixo de zero Abaixo de 5 °C ou acima de 45 °C
Alarmes e estado flags Eventos de proteção do BMS Qualquer alarme ativo

Em packs de lítio-ferro-fosfato (LiFePO4), a diferença de tensão entre células costuma ser o sinal de longo prazo mais informativo. O SoC é estimado pelo BMS e pode derivar; as tensões das células são medidas. Uma diferença que cresce mês a mês aponta para um problema de célula ou de balanceamento.

De onde vêm os dados

A forma de obter os dados do BMS depende do sistema:

  1. Sistemas Victron com um dispositivo GX (Venus OS). O dispositivo GX roda um broker MQTT local que expõe todos os valores do D-Bus interno. Ative-o em Settings > Services (chamado Integrations nos firmwares mais novos) com a opção "MQTT on LAN".
  2. Inversores híbridos frequentemente expõem SoC, tensão e corrente da bateria por registradores Modbus, da mesma forma que expõem os dados fotovoltaicos; veja monitoramento de inversor solar para a abordagem com Modbus.
  3. Packs caseiros com placas BMS JK, JBD ou Daly oferecem interfaces Bluetooth ou UART. Projetos de firmware para ESP32 (incluindo componentes ESPHome) as leem e publicam em MQTT.
  4. Baterias de rack com portas CAN ou RS485 normalmente se reportam ao inversor, que passa a ser a sua fonte de dados.

O restante deste guia usa o Venus OS porque sua interface MQTT é bem documentada, mas a lógica de publicação e de alertas funciona para qualquer fonte.

Como funciona o MQTT do Venus OS

O dispositivo GX publica valores em tópicos no formato N/<portal-id>/<service>/<instance>/<path>, com um payload JSON como {"value": 87.5}. Os valores da bateria ficam no serviço battery:

N/<portal-id>/battery/512/Soc
N/<portal-id>/battery/512/Dc/0/Voltage
N/<portal-id>/battery/512/Dc/0/Current
N/<portal-id>/battery/512/Dc/0/Power
N/<portal-id>/battery/512/Dc/0/Temperature
N/<portal-id>/battery/512/System/MinCellVoltage
N/<portal-id>/battery/512/System/MaxCellVoltage

O número da instância (512 aqui) varia conforme a instalação, e os caminhos de tensão das células só ficam disponíveis quando o BMS os informa. Dois detalhes costumam pegar as pessoas de surpresa. Primeiro, o dispositivo GX só continua publicando enquanto um cliente envia regularmente uma requisição de keepalive para R/<portal-id>/keepalive; sem ela, as atualizações param depois de cerca de um minuto. Segundo, o portal ID aparece no tópico N/<portal-id>/system/0/Serial, e é assim que um script consegue descobri-lo.

Fazendo bridge dos dados da bateria para o seu broker

O script abaixo se conecta ao dispositivo GX na LAN, envia keepalives, coleta os valores da bateria e publica um resumo JSON compacto a cada 10 segundos no seu próprio broker. Ele é testado no servidor MQTT público broker.connectmq.com, que não exige conta, mas pode ser lido por qualquer pessoa e serve apenas para testes e protótipos; por isso usa um prefixo aleatório.

pip install "paho-mqtt>=2.0"
import json
import threading
import time
import uuid
import paho.mqtt.client as mqtt

GX_HOST = "192.168.1.70"            # dispositivo GX com Venus OS
PREFIX = f"connectmq/demo/{uuid.uuid4().hex[:8]}/battery/house"
PUBLISH_EVERY = 10

PATHS = {
    "Soc": "soc_pct",
    "Dc/0/Voltage": "voltage_v",
    "Dc/0/Current": "current_a",
    "Dc/0/Power": "power_w",
    "Dc/0/Temperature": "temp_c",
    "System/MinCellVoltage": "cell_min_v",
    "System/MaxCellVoltage": "cell_max_v",
}

values = {}
portal = {"id": None}

dest = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"batt-pub-{uuid.uuid4().hex[:6]}")
dest.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
dest.connect("broker.connectmq.com", 1883, keepalive=60)
dest.loop_start()
dest.publish(f"{PREFIX}/status", "online", qos=1, retain=True)


def gx_connect(client, userdata, flags, reason_code, properties):
    print("Connected to GX:", reason_code)
    client.subscribe("N/+/system/0/Serial")
    client.subscribe("N/+/battery/+/#")


def gx_message(client, userdata, msg):
    parts = msg.topic.split("/")
    if parts[2] == "system" and parts[-1] == "Serial" and portal["id"] is None:
        portal["id"] = parts[1]
        print("Portal ID:", portal["id"])
        client.publish(f"R/{portal['id']}/keepalive", "")
        return
    if parts[2] != "battery":
        return
    path = "/".join(parts[4:])
    if path in PATHS:
        try:
            values[PATHS[path]] = json.loads(msg.payload).get("value")
        except (ValueError, AttributeError):
            pass


def keepalive_loop(client):
    while True:
        if portal["id"]:
            client.publish(f"R/{portal['id']}/keepalive", "")
        time.sleep(30)


gx = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"batt-gx-{uuid.uuid4().hex[:6]}")
gx.on_connect = gx_connect
gx.on_message = gx_message
gx.connect(GX_HOST, 1883, keepalive=60)
gx.loop_start()
threading.Thread(target=keepalive_loop, args=(gx,), daemon=True).start()

print("Publishing to", PREFIX)
while True:
    time.sleep(PUBLISH_EVERY)
    if not values:
        continue
    out = dict(values)
    if out.get("cell_min_v") is not None and out.get("cell_max_v") is not None:
        out["cell_spread_mv"] = round((out["cell_max_v"] - out["cell_min_v"]) * 1000)
    out["ts"] = int(time.time())
    dest.publish(f"{PREFIX}/telemetry", json.dumps(out), qos=1, retain=True)
    print(out)

Acompanhe o resultado:

mosquitto_sub -h broker.connectmq.com -p 1883 -i batt-view-3318 -t "connectmq/demo/+/battery/#" -v
{"soc_pct": 63.4, "voltage_v": 52.71, "current_a": -18.2, "power_w": -959,
 "temp_c": 21.0, "cell_min_v": 3.291, "cell_max_v": 3.304, "cell_spread_mv": 13, "ts": 1760611200}

Corrente e potência negativas significam que a bateria está descarregando. Mantenha essa convenção de sinal consistente entre as fontes para que painéis e automações não precisem de casos especiais.

Alertas de monitoramento de bateria residencial sem ruído

Um alerta de bateria baixa que dispara a cada dez segundos enquanto o SoC oscila em torno do limite ensina as pessoas a ignorá-lo. Use histerese (limites diferentes para disparar e para normalizar) e publique as transições do alerta, não cada leitura:

import json
import paho.mqtt.client as mqtt

TELEMETRY = "connectmq/demo/<your-id>/battery/house/telemetry"
ALERTS = "connectmq/demo/<your-id>/battery/house/alerts"

RULES = {
    # nome: (campo, dispara_se, normaliza_se)
    "low_soc": ("soc_pct", lambda v: v < 20, lambda v: v > 25),
    "cell_imbalance": ("cell_spread_mv", lambda v: v > 50, lambda v: v < 30),
    "cold_battery": ("temp_c", lambda v: v < 5, lambda v: v > 8),
    "hot_battery": ("temp_c", lambda v: v > 45, lambda v: v < 40),
}
active = set()


def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TELEMETRY, qos=1)


def on_message(client, userdata, msg):
    data = json.loads(msg.payload)
    for name, (field, raise_if, clear_if) in RULES.items():
        v = data.get(field)
        if v is None:
            continue
        if name not in active and raise_if(v):
            active.add(name)
            client.publish(ALERTS, json.dumps({"alert": name, "state": "raised", field: v}), qos=1)
        elif name in active and clear_if(v):
            active.discard(name)
            client.publish(ALERTS, json.dumps({"alert": name, "state": "cleared", field: v}), qos=1)


c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="batt-alerts-9902")
c.on_connect = on_connect
c.on_message = on_message
c.connect("broker.connectmq.com", 1883, keepalive=60)
c.loop_forever()

Qualquer coisa capaz de enviar notificações, como o Node-RED ou o Home Assistant, pode assinar o tópico de alertas e encaminhá-lo ao seu celular. Para limites simples de um único valor, o portal da ConnectMQ também oferece regras de alerta por limite com um log de alertas, o que cobre "SoC abaixo de 20" sem rodar um script próprio; a lógica de histerese acima é o que você acrescenta quando precisa dela.

Meça a diferença entre células em repouso ou com carga leve. Durante carga ou descarga intensas, e perto do fim da carga, as células LiFePO4 naturalmente divergem mais, então um alerta de desbalanceamento nesses períodos não indica necessariamente uma falha.

Gráficos do comportamento da bateria ao longo do tempo

Os valores ao vivo respondem "quão cheia está a bateria agora"; o histórico responde "a bateria continua saudável", e os dois juntos formam um gêmeo digital simples do seu banco de baterias. Como o broker só guarda a última mensagem retida, armazene a telemetria com um assinante, como o Telegraf gravando no InfluxDB, o recorder do Home Assistant ou um pequeno script gravando em SQLite. Três gráficos se pagam rapidamente:

  • SoC com potência FV e da rede no mesmo eixo de tempo, que mostra se a bateria está bem dimensionada para o seu consumo noturno.
  • SoC mínimo diário, que revela déficits sazonais e se uma configuração de reserva está alta ou baixa demais.
  • Diferença entre células em repouso ao longo dos meses, o indicador de saúde de longo prazo para packs LiFePO4. Uma tendência lenta de alta é o sinal para revisar as configurações de balanceamento ou contatar o fornecedor.

Checklist de solução de problemas

  1. Nenhuma mensagem do dispositivo GX? Confirme que o MQTT on LAN está ativado e que você assinou os tópicos depois de conectar.
  2. Os valores param depois de um minuto? O keepalive não está chegando a R/<portal-id>/keepalive; verifique o portal ID descoberto.
  3. Tensões das células ausentes? Seu BMS pode não informá-las ao Venus OS; verifique a página do dispositivo da bateria no GX.
  4. O SoC salta depois de uma carga completa? O BMS ressincronizou sua estimativa; isso é normal após períodos sem atingir a carga total.
  5. Os valores parecem desatualizados no painel? Verifique o tópico de status retido; se ele disser offline, a bridge parou.

Migrando para um broker privado

Os dados da bateria mostram quando você está em casa e como o seu sistema está configurado, e o servidor MQTT público é compartilhado; por isso, use um broker privado na instalação definitiva. Um broker MQTT gerenciado como a ConnectMQ permite que a bridge no seu Raspberry Pi faça uma única conexão TLS de saída, sem abrir portas no roteador. Crie um broker no portal da ConnectMQ e altere a conexão de destino:

dest = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
dest.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
dest.tls_set()
dest.connect("broker.connectmq.com", 8883, keepalive=60)

A partir daí, os dados da bateria podem orientar decisões junto com o seu monitor de energia residencial e o carregador de veículo elétrico, como carregar o carro apenas quando a bateria estiver acima de um SoC definido. Para opções de hospedagem do broker, veja o guia de broker MQTT na nuvem e, para a visão geral, o guia de automação IoT.

Monitore sua bateria com a ConnectMQ

A ConnectMQ oferece um broker MQTT gerenciado protegido por TLS, com credenciais geradas, explorador de tópicos ao vivo e regras de alerta por limite para valores como o estado de carga. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e conecte a bridge da sua bateria em minutos.

Perguntas frequentes

Qual é um bom limite de bateria baixa para baterias residenciais?

Muitos usuários de LiFePO4 disparam um alerta por volta de 15 a 20 por cento de SoC e o normalizam alguns pontos acima. Siga a recomendação de SoC mínimo do fabricante da sua bateria para o uso diário.

Por que monitorar as tensões das células e não apenas o estado de carga?

O SoC é uma estimativa que pode derivar, enquanto as tensões das células são medições diretas. Uma diferença crescente entre a célula mais alta e a mais baixa é um sinal precoce de problemas de balanceamento ou de uma célula fraca.

O Venus OS precisa de internet para o MQTT?

Não. O MQTT on LAN funciona de forma totalmente local. A bridge deste guia lê os dados localmente e só faz uma conexão de saída até o broker que você escolher.

Posso monitorar uma bateria caseira sem hardware Victron?

Sim. Placas BMS JK, JBD e Daly podem ser lidas via Bluetooth ou UART por firmware ESP32, como os componentes ESPHome, que publicam diretamente em MQTT usando o mesmo projeto de tópicos e de alertas.

Artigos relacionados

Guia de Configuração do Zigbee2MQTT: do Coordenador ao Painel

Instale o Zigbee2MQTT com Docker, configure o coordenador e a conexão MQTT, pareie e renomeie dispositivos, controle-os via MQTT e veja seus estados em um painel, testando primeiro no servidor MQTT público (gratuito).

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.