A $20-energy meter for your home


Standard library is available from GitHub. I recommend to use Soft(ware) Serial and spare Hardware serial for debugging.

Here is the code for SoftSerial setup

// Basic use for PZEM-004T with Arduino/ESP chip


/* Use software serial for the PZEM
    * Pin D6 (Connects to the RX pin on the PZEM)
    * Pin D5 (Connects to the TX pin on the PZEM)
PZEM004Tv30 pzem(D6, D5);

void setup() {

void loop() {
    // Here are basic info
    float voltage = pzem.voltage();
    float current = pzem.current();
    float energy = pzem.energy();
    // We can print these numbers out
    Serial.printf("V: %.0f\tA: %.1f\tkWh: %.1f\n", voltage, current, energy);

We can add more features such as WiFi, MQTT tranmission and Json format so messages easier to read. To do so include several libraries:

#include <Wire.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ArduinoJson.h>
#include <PZEM004Tv30.h>

Define your WiFi, MQTT users and passwords

/*_____________________WIFI and MQTT_______________________*/
#define wifi_ssid "MyWiFi"            // change this
#define wifi_password "WiFiPass"      // change this
#define mqtt_server ""    // change this
#define mqtt_user "mqtt_user"         // change this
#define mqtt_password "mqtt_pass"     // change this
#define mqtt_port 1883
#define publish_topic "sensors/power"

And create a function to read energy chip with interval:

void readPZEM(DynamicJsonDocument &doc){
    float voltage = pzem.voltage();
    float current = pzem.current();
    float power = pzem.power();
    float energy = pzem.energy();
    float frequency = pzem.frequency();
    float pf = pzem.pf();
    doc["V"] = voltage;
    doc["A"] = current;
    doc["W"] = power;
    doc["kWh"] = energy;
    doc["Hz"] = frequency;
    doc["PF"] = pf;

and here is a loop that run forever:

void loop(){
    // main program here
    uint32_t now_ = millis()/1000;
    if ((now_ -lastPush)>=INVL){
      DynamicJsonDocument doc(1024);
      doc["sensor"] = SENSORNAME;
      doc["type"] = "json"; //format of message
      doc["uptime"] = now_;
      lastPush = now_;
      JsonObject obj = doc.to<JsonObject>();
      if (now_ > 86400L){

This sketch included a OTA update. It comes handy with system that is hard to access or turning off the breaker so often is unwelcome. The full script is on GitHub.