Medindo Energia com PZEM no ESPHome - Monofásico, Bifásico ou Trifásico

image image

Este tutorial traz a opção de utilizar o módulo PZEM-004T para realizar a medição de energia no Home Assistant.

É uma variante da opção de utilizar Tasmota.

Do que você irá precisar?

  • Módulo PZEM004T - 100A
  • NodeMcu v3
  • Jumpers
  • Fonte de alimentação para o nodemcu.
  • Add-on ESPHOME instalado no home assistant (ESPHome no seu Home Assistant).

Este tutorial não tem como objetivo mostrar como efetuar a ligação dos módulos PZEM à sua rede elétrica, para isso é altamente recomendado que você procure um profissional capacitado para intervir na sua rede elétrica e siga as instruções do manual.

Alterando os endereços dos módulos PZEM

Por padrão os módulos PZEM vem de fabrica com o endereço 1, para utilizar mais de um PZEM no mesmo nodemcu é necessário alterar esse endereço de forma a não repetir o endereço de nenhum PZEM.

Caso você for utilizar mais de um módulo no seu nodemcu, deverá realizar esta alteração. Caso contrário, pode prosseguir sem alterar.

Para alterar o endereço do módulo PZEM veja as alternativas:
Onde X é o endereço desejado para o módulo PZEM.

Código para o ESPHome:

O código a seguir contém a estrutura necessária para a instalação de três módulos PZEM em um único nodemcu de forma a monitorar um quadro ou carga trifásica.

Crie um novo dispositivo no add-on ESPHome e substitua todo o código pelo código a seguir.

É necessário alterar apenas as primeiras linhas de configuração (você pode utilizar o arquivo secrets.yaml do ESPHome como sugerido ou colocar as informações no prório arquivo do código.

O código abaixo foi elaborado para que você necessite de poucas alterações, basta realizar as alterações a seguir.

  • Alteração da Plataforma e Tipo da Placa se você não tiver usando um nodemcu baseado em esp8266.
  • hostname: Nome do host na rede (será usado para o mDNS - não utilzar o caracter _ devido causar erro na resolução de nomes do mDNS).
  • RedeWifi: Nome da rede wifi que o nodemcu irá se conectar.
  • SenhaWifi: Senha da rede wifi que o nodemcu irá se conectar.
  • SenhaWifiReconfig: Definição de uma senha para o wifi do nodemcu em caso de falha na conexão da rede configurada. Será usado para reconfiguração do mesmo via rede.
  • EndConfig: Endereço que será feito upload do arquivo do esphome (o endereço IP atual do nodemcu na rede).
  • SenhaAPI: Senha que será solicitada no Home Assistant para configurar a integração ESPHome
  • PinoTX: Pino do nodemcu que será usado como TX para conexão dos módulos PZEM.
  • PinoRX: Pino do nodemcu que será usado como RX para conexão dos módulos PZEM.
  • NumFases: Número de fases (número de PZEM), de 1 a 3 que está sendo conectado ao nodemcu.
  • TempoAtualizacao: Tempo de atualização das medições no Home Assistant.

Código:

substitutions:
  #Configurações:
  Plataforma: ESP8266
  TipoPlaca: d1_mini
  hostname: 'casaenergia' #Hostname do dispositivo na rede
  PrefixoNome: "Med.Energia - "
  RedeWifi: !secret RedeWifi #Nome da rede wifi que o dispositivo irá se conectar
  SenhaWifi: !secret  SenhaWifi #Senha da rede wifi que o dispositivo irá se conectar
  SenhaWifiReconfig: !secret SenhaWifiReconfig #Senha do AP Wifi para reconfiguração do wifi do dispositivo
  EndConfig: ${hostname}.local #192.168.1.50 #Endereço para configuração (IP que o esp está acessível atualmente na rede), especialmente usado quando quer alterar o hostname via OTA
  WifiOculto: 'False' #Se a rede wifi está oculta defina como 'True'
  WifiFastConnect: 'False' #Se o esp realizará a conexão à rede wifi sem escanear as redes disponíveis defina como 'True'

  SenhaAPI: !secret SenhaAPI #Senha para adicionar o esp no HA
  SenhaOTA: !secret SenhaOTA #Senha para atualizar o firmware do esp via OTA
  
  #Configuração da rede dos PZEMs
  PinoTX: "GPIO5" #Pino TX que o PZEM está conectado no esp
  PinoRX: "GPIO4" #Pino RX que o PZEM está conectado no esp

  #Endereço do PZEM de cada fase
  EndPZEMFase1: "1" #Endereço do módulo PZEM (padão de fábrica é 1)
  EndPZEMFase2: "2" #Endereço do módulo PZEM (padão de fábrica é 1, neste caso alteramos para 3)
  EndPZEMFase3: "3" #Endereço do módulo PZEM (padão de fábrica é 1, neste caso alteramos para 3)
  
  #Configuração do sistema de medição:
  NumFases: "3" #Número de fases presentes na medição (número de PZEM na rede)
  TempoAtualizacao: "1s" #Tempo de atualização das medições no home assistant
  
esphome:
  name: $hostname
  platform: ${Plataforma}
  board: ${TipoPlaca}
  
wifi:
  networks:
  - ssid: ${RedeWifi}
    password: ${SenhaWifi}
    hidden: ${WifiOculto}
  fast_connect: ${WifiFastConnect}
  use_address: ${EndConfig}
  
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${hostname}_WIFI
    password: ${SenhaWifiReconfig}

#Habilita um AP Wifi para reconfigurar em caso de perda de conexão com a rede configurada
captive_portal:

#Habilita a api para comunicar com o Home Assistant
api:
  password: ${SenhaAPI}

#Habilita a atualização de firmware por OTA
ota:
  password: ${SenhaOTA}

logger:
  baud_rate: 0

#Configuração pinos PZEM
uart:
  tx_pin: ${PinoTX}
  rx_pin: ${PinoRX}
  baud_rate: 9600
  stop_bits: 1
  parity: NONE
  
switch:
  #Comando reinicilizar esp remotamente
  - platform: restart
    id: ${hostname}_restart
    name: ${PrefixoNome} Reiniciar
    icon: mdi:restart

text_sensor:
  #Informações da conexão wifi
  - platform: wifi_info
    #Endereço IP
    ip_address:
      id: ${hostname}_IP
      name: ${PrefixoNome} Endereço IP
      icon: mdi:ip-network
    #Nome da Rede
    ssid:
      id: ${hostname}_SSID
      name: ${PrefixoNome} Rede Wifi
      icon: mdi:wifi
  #Informação da versão da compilação
  - platform: version
    id: ${hostname}_versao
    name: ${PrefixoNome} Versão
    icon: mdi:information

binary_sensor:
  #Status (conectado ou desconectado)
  - platform: status
    id: ${hostname}_status
    name: ${PrefixoNome} Status
    device_class: connectivity

sensor:
  #Sensor Intensidade Sinal Wifi
  - platform: wifi_signal
    id: ${hostname}_wifi_sinal
    name: ${PrefixoNome} Intensidade Wifi
    icon: mdi:signal
    update_interval: 10s
    
  #Sensores de energia da Fase 1
  - platform: pzemac
    current:
      id: ${hostname}_I1
      name: "${PrefixoNome} Fase 1 - Corrente"
      icon: mdi:current-ac
      accuracy_decimals: 3
    voltage:
      id: ${hostname}_V1
      name: "${PrefixoNome} Fase 1 - Tensão"
      icon: mdi:sine-wave
      accuracy_decimals: 1
    power:
      id: ${hostname}_P1
      name: "${PrefixoNome} Fase 1 - Potência"
      icon: mdi:flash
      accuracy_decimals: 2
    frequency:
      id: ${hostname}_F1
      name: "${PrefixoNome} Fase 1 - Frequência"
      icon: mdi:sine-wave
      accuracy_decimals: 1
    power_factor:
      id: ${hostname}_FP1
      name: "${PrefixoNome} Fase 1 - Fator de Potência"
      icon: mdi:angle-acute
      accuracy_decimals: 2
    energy:
      id: ${hostname}_E1
      name: "${PrefixoNome} Fase 1 - Energia"
      icon: mdi:counter
      accuracy_decimals: 2
    update_interval: ${TempoAtualizacao}
    address: ${EndPZEMFase1}
    
  #Sensores de energia da Fase 2
  - platform: pzemac
    current:
      id: ${hostname}_I2
      name: "${PrefixoNome} Fase 2 - Corrente"
      icon: mdi:current-ac
      accuracy_decimals: 3
    voltage:
      id: ${hostname}_V2
      name: "${PrefixoNome} Fase 2 - Tensão"
      icon: mdi:sine-wave
      accuracy_decimals: 1
    power:
      id: ${hostname}_P2
      name: "${PrefixoNome} Fase 2 - Potência"
      icon: mdi:flash
      accuracy_decimals: 1
    frequency:
      id: ${hostname}_F2
      name: "${PrefixoNome} Fase 2 - Frequência"
      icon: mdi:sine-wave
      accuracy_decimals: 1
    power_factor:
      id: ${hostname}_FP2
      name: "${PrefixoNome} Fase 2 - Fator de Potência"
      icon: mdi:angle-acute
      accuracy_decimals: 2
    energy:
      id: ${hostname}_E2
      name: "${PrefixoNome} Fase 2 - Energia"
      icon: mdi:counter
      accuracy_decimals: 2
      unit_of_measurement: kWh
      filters:
          - multiply: 0.001
    update_interval:  ${TempoAtualizacao}
    address:  ${EndPZEMFase2}

  #Sensores de energia da Fase 3
  - platform: pzemac
    current:
      id: ${hostname}_I3
      name: "${PrefixoNome} Fase 3 - Corrente"
      icon: mdi:current-ac
      accuracy_decimals: 3
    voltage:
      id: ${hostname}_V3
      name: "${PrefixoNome} Fase 3 - Tensão"
      icon: mdi:sine-wave
      accuracy_decimals: 1
    power:
      id: ${hostname}_P3
      name: "${PrefixoNome} Fase 3 - Potência"
      icon: mdi:flash
      accuracy_decimals: 1
    frequency:
      id: ${hostname}_F3
      name: "${PrefixoNome} Fase 3 - Frequência"
      icon: mdi:sine-wave
      accuracy_decimals: 1
    power_factor:
      id: ${hostname}_FP3
      name: "${PrefixoNome} Fase 3 - Fator de Potência"
      icon: mdi:angle-acute
      accuracy_decimals: 2
    energy:
      id: ${hostname}_E3
      name: "${PrefixoNome} Fase 3 - Energia"
      icon: mdi:counter
      accuracy_decimals: 2
      unit_of_measurement: kWh
      filters:
          - multiply: 0.001
    update_interval:  ${TempoAtualizacao}
    address:  ${EndPZEMFase3}

  #Potência Aparente Fase 1
  - platform: template
    id: ${hostname}_S1
    name: "${PrefixoNome} Fase 1 - Potência Aparente"
    icon: mdi:alpha-s
    accuracy_decimals: 1
    lambda: |-
        return id(${hostname}_I1).state * id(${hostname}_V1).state;
    unit_of_measurement: "VA"
    update_interval:  ${TempoAtualizacao}
  #Potência Reativa Fase 1
  - platform: template
    id: ${hostname}_Q1
    name: "${PrefixoNome} Fase 1 - Potência Reativa"
    icon: mdi:alpha-q
    accuracy_decimals: 1
    lambda: |-
        return id(${hostname}_S1).state * sin(acos(id(${hostname}_FP1).state));
    unit_of_measurement: "var"
    update_interval:  ${TempoAtualizacao}
  #Potência Aparente Fase 2
  - platform: template
    id: ${hostname}_S2
    name: "${PrefixoNome} Fase 2 - Potência Aparente"
    icon: mdi:alpha-s
    accuracy_decimals: 1
    lambda: |-
        return id(${hostname}_I2).state * id(${hostname}_V2).state;
    unit_of_measurement: "VA"
    update_interval:  ${TempoAtualizacao}
  #Potência Reativa Fase 2
  - platform: template
    id: ${hostname}_Q2
    name: "${PrefixoNome} Fase 2 - Potência Reativa"
    icon: mdi:alpha-q
    accuracy_decimals: 1
    lambda: |-
        return id(${hostname}_S2).state * sin(acos(id(${hostname}_FP2).state));
    unit_of_measurement: "var"
    update_interval:  ${TempoAtualizacao}
  #Potência Aparente Fase 3
  - platform: template
    id: ${hostname}_S3
    name: "${PrefixoNome} Fase 3 - Potência Aparente"
    icon: mdi:alpha-s
    accuracy_decimals: 1
    lambda: |-
        return id(${hostname}_I3).state * id(${hostname}_V3).state;
    unit_of_measurement: "VA"
    update_interval:  ${TempoAtualizacao}
  #Potência Reativa Fase 3
  - platform: template
    id: ${hostname}_Q3
    name: "${PrefixoNome} Fase 3 - Potência Reativa"
    icon: mdi:alpha-q
    accuracy_decimals: 1
    lambda: |-
        return id(${hostname}_S3).state * sin(acos(id(${hostname}_FP3).state));
    unit_of_measurement: "var"
    update_interval:  ${TempoAtualizacao}
  #Potência Ativa Total
  - platform: template
    id: ${hostname}_PT
    name: "${PrefixoNome} Potência Ativa Total"
    icon: mdi:flash
    accuracy_decimals: 1
    lambda: |-
        if (${NumFases}==1) {
          return id(${hostname}_P1).state;
        }
        if (${NumFases}==2) {
          return id(${hostname}_P1).state + id(${hostname}_P2).state;
        }
        if (${NumFases}==3) {
          return id(${hostname}_P1).state + id(${hostname}_P2).state + id(${hostname}_P3).state;
        }
    unit_of_measurement: "W"
    update_interval:  ${TempoAtualizacao}
  #Potência Aparente Total
  - platform: template
    id: ${hostname}_ST
    name: "${PrefixoNome} Potência Aparente Total"
    icon: mdi:alpha-s
    accuracy_decimals: 1
    lambda: |-
        if (${NumFases}==1) {
          return id(${hostname}_S1).state;
        }
        if (${NumFases}==2) {
          return id(${hostname}_S1).state + id(${hostname}_S2).state;
        }
        if (${NumFases}==3) {
          return id(${hostname}_S1).state + id(${hostname}_S2).state + id(${hostname}_S3).state;
        }
    unit_of_measurement: "VA"
    update_interval:  ${TempoAtualizacao}
  #Potência Reativa Total
  - platform: template
    id: ${hostname}_QT
    name: "${PrefixoNome} Potência Reativa Total"
    icon: mdi:alpha-q
    accuracy_decimals: 1
    lambda: |-
        if (${NumFases}==1) {
          return id(${hostname}_Q1).state;
        }
        if (${NumFases}==2) {
          return id(${hostname}_Q1).state + id(${hostname}_Q2).state;
        }
        if (${NumFases}==3) {
          return id(${hostname}_Q1).state + id(${hostname}_Q2).state + id(${hostname}_Q3).state;
        }
    unit_of_measurement: "var"
    update_interval:  ${TempoAtualizacao}
  #Fator de Potência Total
  - platform: template
    id: ${hostname}_FPT
    name: "${PrefixoNome} Fator de Potência Total"
    icon: mdi:angle-acute
    accuracy_decimals: 2
    lambda: |-
        return id(${hostname}_PT).state / id(${hostname}_ST).state;
    unit_of_measurement: ""
    update_interval:  ${TempoAtualizacao}
  #Energia Total
  - platform: template
    id: ${hostname}_ET
    name: "${PrefixoNome} Energia Total"
    icon: 'mdi:counter'
    accuracy_decimals: 3
    lambda: |-
        if (${NumFases}==1) {
          return (id(${hostname}_E1).state);
        }
        if (${NumFases}==2) {
            return (id(${hostname}_E1).state + id(${hostname}_E2).state);
        }
        if (${NumFases}==3) {
          return (id(${hostname}_E1).state + id(${hostname}_E2).state + id(${hostname}_E3).state);
        }
    unit_of_measurement: "kWh"
    update_interval:  ${TempoAtualizacao}

Todas essas informações importantes (senhas OTA e API, configuração da rede wifi, etc) estão armazenadas no arquivo secrets.yaml (ver aqui e aqui para mais detalhes de configuração e uso).

Enviando o código para o nodemcu:

O primeiro envio do código para o nodemcu deve ser realizado via USB, para isso clique em INSTALL no dispositivo no add-on ESPHome:
image

E clique em Manual download:
image

O código será compilado e ao final será exibido o botão download (usar um navegador para realizar o download, não funciona no aplicativo de smartphone/computador).
image

Após fazer upload do código para o nodemcu, as próximas atualizações podem ser realizadas via rede (OTA), bastando clicar na opção Wirelessly ao instalar:
image

Adicionado o dispositivo no Home Assistant.

Navegue para Configurações > Integração > Adicionar Integração e pesquise a integração ESPHome na lista:
image

Informe o endereço IP ou nome do host do dispositivo e clique em Enviar:
image

Informe a senha da API configurada no código do ESPHome e clique em enviar:
image

O dispositivo adicionado será exibido.
image

Configurando a entidade Energia Total para ser usada no painel de energia do Home Asssistant:

Incluir o include do arquivo customize.yaml no aquivo configuration.yaml conforme abaixo:

homeassistant:
  customize: !include customize.yaml

Incluir a customização da entidade de energia total no arquivo customize.yaml:

sensor.med_energia_energia_total:
  device_class: energy
  state_class: total_increasing

Recarregar as configurações ou reiniciar o Home Assistant.

Adicionando a entidade Energia Total no painel de energia do Home Assistant:

Navegue para Configurações > Energia e adicione a entidade Energia Total ao Grid consumption:
image

Após um período de algumas horas será possível visualizar o gráfico de consumo no painel de energia:
image

Estatísticas utility meter:

As entidades utility meter podem ser criadas normalmente a partir da informação de energia totoal e vão servir para estatística para o usuário, podendo ser usadas em automação.

O código para criar as entidades utility meter é:

  #Totalizado com reset diário
  energia_diaria:
    source: sensor.med_energia_energia_total
    cycle: daily
    tariffs:
      - normal
  #Totalizado com reset semanal
  energia_semanal:
    source: sensor.med_energia_energia_total
    cycle: weekly
    tariffs:
      - normal
  #Totalizado com reset mensal
  energia_mensal:
    source: sensor.med_energia_energia_total
    cycle: monthly
    tariffs:
      - normal
  #Totalizado com reset anual
  energia_anual:
    source: sensor.med_energia_energia_total
    cycle: yearly
    tariffs:
      - normal
  #Totalizado sem reset periódico
  energia_total:
    source: sensor.med_energia_energia_total
    tariffs:
      - normal  
6 Likes

Muito Bom @douglasit ! Parabéns pelo excelente trabalho!

1 Like

Muito bom! Parabéns!

Sabe se conseguimos medir energia / potencia negativa assim ?
Estou tentando medir o consumo da casa e a energia injetada na rede.

Cheguei a comentar agora em um outro post mas esse seu trabalho está mais completo para a medição trifásica…

1 Like

Acredito que não vai conseguir efetuar a medição negativa com o PZEM, mas talvez você possa calcular a potência injetada e consumida da rede medindo a energia de saída do inversor e a de entrada na residência. Porém você precisaria de dois conjuntos de PZEM.

Para alterar o endereço de um módulo PZEM veja o post:

Conseguir realizar as medidas com 2 Pzem, mas não consigo obter energia total e os outros dados referente a potencia total, mostra somente como desconhecido. Tenho que editar alguma coisa no código ??

Você usa apenas dois PZEMs, ou seja sua instalação é bifásica?

Você deve configurar o parâmetro NumFases conforme o post:

Qual valor está usando?

@douglasit
Esse era o erro, como esse código funciona para circuitos trifásicos não mudei o NumFases.
Somete outra duvida, esse sensor.med_energia_fase_1_energia e fase_2, não deveria ser resetado a cada dia ??
Pois utilizava o tasmota e alimentava o Utility Meter com consumo diário e a cada dia ele zerava esse valor de energia. Fiquei um pouco confuso nessa parte. Mesmo assim Obrigado pela ajuda !!!

A informação do sensor de energia por fase e de energia total (soma da energia total de casa fase) são provenientes dos PZEMs e são cumulativo até que seja resetado no PZEM (por comando ou porque chegou ao valor máximo). Mas mesmo assim ele já é suficiente para usar no gerenciamento de energia do HA. A vantagem de usar esses sensores no painel de energia ao invés dos sensores utility meter é que os PZEMs continuam a acumular a leitura mesmo se perder a comunicação com o HA, sendo que a diferença da última medição e a medição de quando retornar a comunicação com o HA contabilizada na hora do retorno da comunicação.

As entidades utility meter podem ser criadas normalmente a partir dessa informação de energia total e vão servir para estatística para o usuário, podendo ser usadas em automação.

O código para criar as entidades utility meter é:

  #Totalizado com reset diário
  energia_diaria:
    source: sensor.med_energia_energia_total
    cycle: daily
    tariffs:
      - normal
  #Totalizado com reset semanal
  energia_semanal:
    source: sensor.med_energia_energia_total
    cycle: weekly
    tariffs:
      - normal
  #Totalizado com reset mensal
  energia_mensal:
    source: sensor.med_energia_energia_total
    cycle: monthly
    tariffs:
      - normal
  #Totalizado com reset anual
  energia_anual:
    source: sensor.med_energia_energia_total
    cycle: yearly
    tariffs:
      - normal
  #Totalizado sem reset periódico
  energia_total:
    source: sensor.med_energia_energia_total
    tariffs:
      - normal  

Novamente muito obrigado, @douglasit. Desta forma como estou em fase de teste para meu TCC, tenho somente que resetar a energia acumulativa dos pzem. Na ide arduino com a biblioteca do pzem-004t v3, existe a possibilidade de esse reset, no esphome teria essa possibilidade ? Criando um botão para esse reset ?

Desconheço que a biblioteca do PZEMAC no ESPHome possua essa função implementada, mas acredito que você possa conseguir sim. Utilize o UART SWITC com o campo data de acordo com o manual do PZEM para executar o reset.

Basta configurar como o manual especifica:
image

Com não uso e não configurei não tenho como te ajudar mais. Se conseguir e quiser compartilhar conosco ficaremos grato.