How to use PZEM004T Energy Monitor with esphome

This is my electricity meter in the UK. They call them Smart Meters so they probably use Zigbee to communicate with not so smart display at home to display real usage

My own…


2 Likes

Would you mind sharing your full .bin file for the esp? :slight_smile:

Below my .yaml config. Just add new device to EspHome paste this config and generate .bin

substitutions:
  # Modify variables based on your settings
  hostname: "esp32_power_monitor"
  # this is just for ingormation where this device is located
  location: "Consumer Box"
  #amend platford and board type
  platform: ESP32
  board: nodemcu-32s

esphome:
  name: $hostname
  platform: $platform
  board: $board

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: on  
# Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "AP $hostname"
    password: !secret ap_fallback_password

captive_portal:

logger:

web_server:

api:
  password: !secret ha_api_password
  reboot_timeout: 5min 
ota:
  password: !secret ota_password

text_sensor:
  - platform: version
    name: version
  - platform: wifi_info
    ip_address:
      name: ip
    ssid:
      name: ssid
    bssid:
      name: bssid

binary_sensor:   
  - platform: status
    name: "$hostname Status"


time:
  - platform: homeassistant
    id: esptime 
    

status_led:
  pin: GPIO2

uart:
  tx_pin: GPIO17
  rx_pin: GPIO16
  baud_rate: 9600
  stop_bits: 1

sensor:

  - platform: wifi_signal
    name: "$hostname WiFi"
    update_interval: 60s 
    id: wifi_stat
  - platform: uptime
    name: $hostname Uptime

  
  - platform: pzemac
    current:
      name: "Home Current"
      id: cur
    voltage:
      name: "Home Voltage"
      id: vol
    power:
      name: "Home Power"
      id: power
      state_class: measurement

    frequency:
      name: "Home Frequency"
      id: freq
    power_factor:
      name: "Home Power Factor"
      id: pow_factor
    update_interval: 5s

  - platform: total_daily_energy
    name: "Total Daily Energy"
    unit_of_measurement: kWh
    filters:
      - multiply: 0.001
    power_id: power
    

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
display:
  - platform: lcd_pcf8574
    dimensions: 20x4
    address: 0x27
    lambda: |-
      it.printf(0, 0, "* Home Power Meter *");
      it.printf(0, 1, "Pow:%.0fW", id(power).state);
      it.printf(10, 1, "Amp:%.1fA", id(cur).state);
      it.printf(0, 2, "%.1fV", id(vol).state);
      it.printf(14, 2, "%.1fHz", id(freq).state);
      it.strftime(0, 3, "Time:%H:%M", id(esptime).now());
      it.printf(10, 3, " WiFi%.0fdb", id(wifi_stat).state);
      
      // first number shows position in line then comma and line number. 0 is first line

2 Likes

I have 2 pzem on the same modbus network
1 pzem = IP(1)
2 pzem = IP(2)

Part of the code used:

PinoTX:“GPIO12”
PinoRX:“GPIO14”

uart:
tx_pin: ${PinoTX}
rx_pin: ${PinoRX}
baud_rate: 9600
stop_bits: 1

switch:

  • platform: uart
    name: “Phase 1 - Reset Energy”
    date = [0x01, 0x42, 0x80, 0x11]

  • platform: uart
    name: “Phase 2 - Reset Energy”
    date = [?, 0x42, ?, ?]

What HEX numbers should I put to reset the power of the second pzem(IP=2)???

Here’s the reset commands for the first 15 addresses. I’ve tested up to 0x03.

Address Command
0x01 0x01, 0x42, 0x80, 0x11
0x02 0x02, 0x42, 0x80, 0xE1
0x03 0x03, 0x42, 0x81, 0x71
0x04 0x04, 0x42, 0x83, 0x41
0x05 0x05, 0x42, 0xAE, 0x1F
0x06 0x06, 0x42, 0x82, 0x21
0x07 0x07, 0x42, 0x83, 0xB1
0x08 0x08, 0x42, 0x86, 0x41
0x09 0x09, 0x42, 0x87, 0xD1
0x0A 0x0A, 0x42, 0x87, 0x21
0x0B 0x0B, 0x42, 0x86, 0xB1
0x0C 0x0C, 0x42, 0x84, 0x81
0x0D 0x0D, 0x42, 0x85, 0x11
0x0E 0x0E, 0x42, 0x85, 0xE1
0x0F 0x0F, 0x42, 0x84, 0x71

Edit: There were issues with 04 through 0F. Should be resolved.

4 Likes

I thought it might be worth commenting on my experiences (thanks to this thread).
I have three PZEM v4, powered at 3v3 from a single Wemos D1 mini, using the diode multiplexing suggestion in this thread. It all works as it should - no components removed or mods to the PZEM units themselves required. I used a hybrid ESPHome config taken from this thread to program the Wemos. Many thanks to those who took the time to comment, I doubt I would have got there without!

2 Likes

I answer myself…
the circuit diagram is correct ant this code works.
only for V3!!!

esphome:
  name: powermeter

esp8266:
  board: nodemcuv2

# Enable logging
logger:
  level: debug
  baud_rate: 0

# Enable Home Assistant API
api:

ota:
  password: !secret ota_password

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Powermeter Fallback Hotspot"
    password: !secret wifi_password_fallback

captive_portal:

web_server:
  port: 80
  
uart:
  - id: ubus1
    rx_pin: GPIO3
    tx_pin: GPIO1
    baud_rate: 9600
    stop_bits: 1
#modbus:
#  id: mbus
#  uart_id: ubus

sensor:
  - platform: pzemac
#    modbus_id: mbus
    address: 1
    current:
      name: "ID1 Current"
    voltage:
      name: "ID1 Voltage"
    energy:
      name: "ID1 Energy"
    power:
      name: "ID1 Power"
    frequency:
      name: "ID1 Frequency"
    power_factor:
      name: "ID1 Power Factor"
    update_interval: 10s
  - platform: pzemac
#    modbus_id: mbus
    address: 2
    current:
      name: "ID2 Current"
    voltage:
      name: "ID2 Voltage"
    energy:
      name: "ID2 Energy"
    power:
      name: "ID2 Power"
    frequency:
      name: "ID2 Frequency"
    power_factor:
      name: "ID2 Power Factor"
    update_interval: 10s
  - platform: pzemac
#    modbus_id: mbus
    address: 3
    current:
      name: "ID3 Current"
    voltage:
      name: "ID3 Voltage"
    energy:
      name: "ID3 Energy"
    power:
      name: "ID3 Power"
    frequency:
      name: "ID3 Frequency"
    power_factor:
      name: "ID3 Power Factor"
    update_interval: 10s
    
button:
  - platform: restart
    name: Restart Powermeter

but the most important thing is to change the PZEM ID
to do it:

  • ESP flash with TASMOTA
  • connect PZEM
  • use this template
  • {"NAME":"HW-655 PZEM","GPIO":[0,62,0,98,6,5,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
  • via console use the command “ModuleAddress X” where X is the number of the ID to be associated with the PZEM (like this ModuleAddress 1, ModuleAddress 2, ModuleAddress 3, ModuleAddress x…)

done this build the circuit and everything works perfectly.

2 Likes

Hi bro, can you send me the circuit diagram for the circuit because I have a graduating project in the same subject, I will add indicators like buzzer and led when the energy reaches a specific limit, and also I will add the bill cost.

1 Like

Hi,
I have copied my code below, and for what you are looking for I think could be done with ESPHome Automations, use spare GPIO as switch and use the values as sensors.

Also YES! All automations you define in ESPHome are executed on the ESP itself and will continue to work even if the WiFi network is down or the MQTT server is not reachable.

esphome:
  name: wemos_d1_pzem016_display
  platform: ESP8266
  board: d1_mini
# esp8266_restore_from_flash: true


wifi:
  ssid: 
  password: 
  power_save_mode: none
  
  manual_ip:  
   static_ip: 192.168.0.103
   gateway: 192.168.0.2
   subnet: 255.255.255.0
   dns1: 192.168.0.2
   
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Wemos D1 pZEM016"
    password: "RSixraswZ4Vk"

captive_portal:

# Enable logging
logger:
  level: DEBUG
  baud_rate: 0 

# Enable Home Assistant API
api:

ota:

# Enable Web server.
web_server:
  port: 80
  
text_sensor:
  - platform: version
    name: "ESPHome Version PZEM 016"
    
# Example configuration entry

  - platform: wifi_info
    ip_address:
      name: ESP IP Address PZEM-016
    ssid:
      name: ESP Connected PZEM-016
    bssid:
      name: ESP Connected PZEM-016
      
      
#time:
#  - platform: homeassistant
#    id: homeassistant_time

time:
  - platform: sntp
    id: my_time
    timezone: GB
   
uart:
  rx_pin: GPIO3
  tx_pin: GPIO1
  baud_rate: 9600
  stop_bits: 1
  
i2c:
  sda: D2
  scl: D1  

sensor:

################################################################################
#           Fast sensors for Energy calculation & local display                #
################################################################################

  - platform: pzemac
    current:
      name: "PZEM-016 Current"
      id: pzem_016_current
      internal: true
    voltage:
      name: "PZEM-016 Voltage"
      id: pzem_016_voltage
      internal: true
    power:
      name: "PZEM-016 Power"
      id: pzem_016_power
      internal: true
    frequency:
      name: "PZEM-016 Frequency"
      id: pzem_016_frequency
      internal: true
    power_factor:
      name: "PZEM-016 Power Factor"
      id: pf
      internal: true
    update_interval: 2s

  - platform: total_daily_energy
    name: "PZEM-016 Daily Energy"
    power_id: pzem_016_power
    id: daily_energy_total
    filters:
        # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kWh
    icon: mdi:counter
    accuracy_decimals: 1   


  - platform: integration
    name: "PZEM-016 Energy Meter" 
    sensor: pzem_016_power
    time_unit: h
    filters:
     # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
    unit_of_measurement: kWh
    icon: mdi:counter
    
    
  # Sensors with general information.

  # Uptime sensor.
  - platform: uptime
    name: PZEM-016 Uptime
    id: "pzem_016_uptime"

  # WiFi Signal sensor.
  - platform: wifi_signal
    name: PZEM-016 WiFi Signal
    id: "pzem_016_wifi_signal"
    update_interval: 60s
    
    
################################################################################
#                         Slow sensors for Home assistant                      #
################################################################################

  - platform: template #########################
    name: "PZEM-016 Current Slow"
    lambda: |-
      if (id(pzem_016_current).state) {
        return (id(pzem_016_current).state);
      } else {
        return 0;
      }
    unit_of_measurement: A
    icon: "mdi:alpha-a-circle"
    update_interval: 10s
  
  - platform: template #########################
    name: "PZEM-016 Voltage Slow"
    lambda: |-
      if (id(pzem_016_voltage).state) {
        return (id(pzem_016_voltage).state);
      } else {
        return 0;
      }
    unit_of_measurement: V
    icon: "mdi:alpha-v-circle"
    update_interval: 10s
  
  - platform: template #########################
    name: "PZEM-016 Power Slow"
    lambda: |-
      if (id(pzem_016_power).state) {
        return (id(pzem_016_power).state);
      } else {
        return 0;
      }
    unit_of_measurement: W 
    icon: "mdi:alpha-w-circle"
    update_interval: 10s
  
  - platform: template #########################
    name: "PZEM-016 Frequency Slow"
    lambda: |-
      if (id(pzem_016_frequency).state) {
        return (id(pzem_016_frequency).state);
      } else {
        return 0;
      }
    unit_of_measurement: Hz
    icon: "mdi:alpha-f-circle"
    update_interval: 10s

  - platform: template #########################
    name: "PZEM-016 Power Factor Slow"
    lambda: |-
      if (id(pf).state) {
        return (id(pf).state);
      } else {
        return 0;
      }
    unit_of_measurement: PF
    icon: "mdi:alpha-p-circle"
    update_interval: 10s

    
display:
  - platform: lcd_pcf8574
    id: mydisplay
    dimensions: 16x2
    address: 0x27
    lambda: |-
      it.printf(0, 0, "%.1fV", id(pzem_016_voltage).state);
      it.printf(7, 0, "%.1f AMPS", id(pzem_016_current).state);
      it.printf(0, 1, "%.1f HZ", id(pzem_016_frequency).state);
      it.printf(8, 1, "P:%.1f W", id(pzem_016_power).state);


binary_sensor:
  - platform: status
    name: "PZEM-016 Status" 
   

status_led:
  pin:
     number: D4
     inverted: false     

Regards,

2 Likes

can i add more than one pzem address in the same code that you have right?

what is the windows software you mention to change the pzem address?

better just use esphome :wink:

The (great) esphome docs tell you exactly how:

Just wondering if the comment above "

Applies to configurations where there is only one PZEM004T?

I have mine setup but am not getting any power readings - I’ve checked all the connections so not sure what to troubleshoot next?

No, you only need to change the pzem004t addresses if you have more than one connected to one uart interface.

That’s very unfortunate but I can assure you nobody will be able to help you properly if you don’t provide logs.

Yep, understood! Now that we have the first part out of the way. :grinning:

EspHome YAML is:

esphome:
  name: esphome-web-emontx

esp8266:
  board: huzzah

# Enable logging
logger:
  level: VERY_VERBOSE
  baud_rate: 0

# Enable Home Assistant API
api:

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-48A0De"
    password: "0jDkLK3czIY0"

captive_portal:

uart:
  tx_pin: TX
  rx_pin: RX
  baud_rate: 9600
  stop_bits: 1
  debug:
modbus:

sensor:
  - platform: pzemac
    address: 1  
    current:
      name: "PZEM-004T V3 Current"
    voltage:
      name: "PZEM-004T V3 Voltage"
    energy:
      name: "PZEM-004T V3 Energy"
    power:
      name: "PZEM-004T V3 Power"
    frequency:
      name: "PZEM-004T V3 Frequency"
    power_factor:
      name: "PZEM-004T V3 Power Factor"
    update_interval: 60s

web_server:
  port: 80

With logging turned on:

[20:35:16][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=145431 (now=155431)
[20:35:17][VV][api.service:355]: on_ping_request: PingRequest {}
[20:35:17][VV][api.service:043]: send_ping_response: PingResponse {}
[20:35:20][VV][api.service:355]: on_ping_request: PingRequest {}
[20:35:20][VV][api.service:043]: send_ping_response: PingResponse {}
[20:35:24][VV][scheduler:185]: Running interval '' with interval=60000 last_execution=103609 (now=163609)
[20:35:26][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=155431 (now=165438)
[20:35:31][VV][scheduler:185]: Running interval 'update' with interval=60000 last_execution=110569 (now=170576)
[20:35:31][VV][uart.arduino_esp8266:180]:     Flushing...
[20:35:31][V][modbus:184]: Modbus write: 01.04.00.00.00.0A.70.0D (8)
[20:35:31][D][uart_debug:114]: >>> 01:04:00:00:00:0A:70:0D
[20:35:32][VV][api.service:355]: on_ping_request: PingRequest {}
[20:35:32][VV][api.service:043]: send_ping_response: PingResponse {}
[20:35:35][VV][api.service:355]: on_ping_request: PingRequest {}
[20:35:35][VV][api.service:043]: send_ping_response: PingResponse {}
[20:35:36][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=165431 (now=175436)
[20:35:46][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=175431 (now=185435)
[20:35:47][VV][api.service:355]: on_ping_request: PingRequest {}
[20:35:47][VV][api.service:043]: send_ping_response: PingResponse {}
[20:35:50][VV][api.service:355]: on_ping_request: PingRequest {}
[20:35:50][VV][api.service:043]: send_ping_response: PingResponse {}
[20:35:56][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=185431 (now=195431)
[20:36:02][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:02][VV][api.service:043]: send_ping_response: PingResponse {}
[20:36:05][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:05][VV][api.service:043]: send_ping_response: PingResponse {}
[20:36:06][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=195431 (now=205436)
[20:36:16][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=205431 (now=215431)
[20:36:17][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:17][VV][api.service:043]: send_ping_response: PingResponse {}
[20:36:20][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:20][VV][api.service:043]: send_ping_response: PingResponse {}
[20:36:24][VV][scheduler:185]: Running interval '' with interval=60000 last_execution=163609 (now=223609)
[20:36:26][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=215431 (now=225435)
[20:36:31][VV][scheduler:185]: Running interval 'update' with interval=60000 last_execution=170569 (now=230572)
[20:36:31][VV][uart.arduino_esp8266:180]:     Flushing...
[20:36:31][V][modbus:184]: Modbus write: 01.04.00.00.00.0A.70.0D (8)
[20:36:31][D][uart_debug:114]: >>> 01:04:00:00:00:0A:70:0D
[20:36:32][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:32][VV][api.service:043]: send_ping_response: PingResponse {}
[20:36:35][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:35][VV][api.service:043]: send_ping_response: PingResponse {}
[20:36:36][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=225431 (now=235431)
[20:36:46][VV][scheduler:185]: Running interval '' with interval=10000 last_execution=235431 (now=245431)
[20:36:47][VV][api.service:355]: on_ping_request: PingRequest {}
[20:36:47][VV][api.service:043]: send_ping_response: PingResponse {}

I don’t see much related to the pzem004t in the logs, could you set the mode logging to debug (actually that’s less than very_verbose which is set now) and also include the very beginning of the log to see if the component is set up successfully?

Just one thing comes to my mind, the pzem004t v1 and v2 (not sold anymore) need the pzem004t component in esphome. Only the pzem004t v3 (sold nowadays) is using the pzemac component.

This is everything related to serial/uart/pzemac

[22:47:45][C][logger:233]: Logger:
[22:47:45][C][logger:234]:   Level: VERY_VERBOSE
[22:47:45][C][logger:235]:   Log Baud Rate: 0
[22:47:45][C][logger:236]:   Hardware UART: UART0
[22:47:45][C][uart.arduino_esp8266:102]: UART Bus:
[22:47:45][C][uart.arduino_esp8266:103]:   TX Pin: GPIO1
[22:47:45][C][uart.arduino_esp8266:104]:   RX Pin: GPIO3
[22:47:45][C][uart.arduino_esp8266:106]:   RX Buffer Size: 256
[22:47:45][C][uart.arduino_esp8266:108]:   Baud Rate: 9600 baud
[22:47:45][C][uart.arduino_esp8266:109]:   Data Bits: 8
[22:47:45][C][uart.arduino_esp8266:110]:   Parity: NONE
[22:47:45][C][uart.arduino_esp8266:111]:   Stop bits: 1
[22:47:45][C][uart.arduino_esp8266:113]:   Using hardware serial interface.
[22:47:45][C][modbus:129]: Modbus:
[22:47:45][C][modbus:131]:   Send Wait Time: 250 ms
[22:47:45][C][pzemac:067]: PZEMAC:
[22:47:45][C][pzemac:068]:   Address: 0x01
[22:47:45][C][pzemac:069]: Voltage 'PZEM-004T V3 Voltage'
[22:47:45][C][pzemac:069]:   Device Class: 'voltage'
[22:47:45][C][pzemac:069]:   State Class: 'measurement'
[22:47:45][C][pzemac:069]:   Unit of Measurement: 'V'
[22:47:45][C][pzemac:069]:   Accuracy Decimals: 1
[22:47:45][C][pzemac:070]: Current 'PZEM-004T V3 Current'
[22:47:45][C][pzemac:070]:   Device Class: 'current'
[22:47:45][C][pzemac:070]:   State Class: 'measurement'
[22:47:45][C][pzemac:070]:   Unit of Measurement: 'A'
[22:47:45][C][pzemac:070]:   Accuracy Decimals: 3
[22:47:45][C][pzemac:071]: Power 'PZEM-004T V3 Power'
[22:47:45][C][pzemac:071]:   Device Class: 'power'
[22:47:45][C][pzemac:071]:   State Class: 'measurement'
[22:47:45][C][pzemac:071]:   Unit of Measurement: 'W'
[22:47:45][C][pzemac:071]:   Accuracy Decimals: 2
[22:47:45][C][pzemac:072]: Energy 'PZEM-004T V3 Energy'
[22:47:45][C][pzemac:072]:   Device Class: 'energy'
[22:47:45][C][pzemac:072]:   State Class: 'total_increasing'
[22:47:45][C][pzemac:072]:   Unit of Measurement: 'Wh'
[22:47:45][C][pzemac:072]:   Accuracy Decimals: 0
[22:47:45][C][pzemac:073]: Frequency 'PZEM-004T V3 Frequency'
[22:47:45][C][pzemac:073]:   State Class: 'measurement'
[22:47:45][C][pzemac:073]:   Unit of Measurement: 'Hz'
[22:47:45][C][pzemac:073]:   Accuracy Decimals: 1
[22:47:45][C][pzemac:073]:   Icon: 'mdi:current-ac'
[22:47:45][C][pzemac:074]: Power Factor 'PZEM-004T V3 Power Factor'
[22:47:45][C][pzemac:074]:   Device Class: 'power_factor'
[22:47:45][C][pzemac:074]:   State Class: 'measurement'
[22:47:45][C][pzemac:074]:   Unit of Measurement: ''
[22:47:45][C][pzemac:074]:   Accuracy Decimals: 2

That looks good so far. And what happens in the logs every 60 seconds when it should take measurements? Any errors?

Could anyone help me with this address change code? I have several questions regarding to this code.

  • What address does this code change the PZEM into?
  • I understand that the unchanged default address from the factory is 1. So does this change the address to 2?
  • Do I just copy and paste this exact code into the ESPHome YAML and it will change the address to 2?
  • What if I wanted to use 3 PZEMs and I need to change the address to 3?

I assume it has something to do with this?