Skip to content

Instantly share code, notes, and snippets.

@bfritscher
Last active May 30, 2020 20:29
Show Gist options
  • Save bfritscher/da9dac542d4d70604b607670c867d555 to your computer and use it in GitHub Desktop.
Save bfritscher/da9dac542d4d70604b607670c867d555 to your computer and use it in GitHub Desktop.
PZEM-004T Energy monitor with nodemcu esp8266 arduino
/* FOR COMMENTS PLEASE USE https://github.com/bfritscher/esp8266_PZEM-004T */
/* NOTIFICATION ARE NOT SENT BY EMAIL FOR GISTS :-( */
// VERSION 2 with WiFIManager integration for device_name
#include <FS.h> //this needs to be first, or it all crashes and burns...
#include <ESP8266WiFi.h> // ESP8266 Core WiFi Library (you most likely already have this in your sketch)
#include <Ticker.h> // for LED status
#include <DNSServer.h> // Local DNS Server used for redirecting all requests to the configuration portal
#include <ESP8266WebServer.h> // Local WebServer used to serve the configuration portal
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
#include <ESP8266HTTPClient.h>
#include <SoftwareSerial.h>
#include <PZEM004T.h> // https://github.com/olehs/PZEM004T Power Meter
// select which pin will trigger the configuration portal when set to LOW
#define TRIGGER_PIN 0
#define MAX_ATTEMPTS 10
PZEM004T pzem(4,5); // RX,TX (D2, D1) on NodeMCU
IPAddress ip(192,168,1,1); // required by pzem but not used
HTTPClient http;
Ticker ticker;
boolean led = false;
char device_name[40];
WiFiManagerParameter custom_device_name("name", "device name", device_name, 40, " required");
void tick() {
//toggle state
led = !led;
pinMode(BUILTIN_LED, OUTPUT);
digitalWrite(BUILTIN_LED, led); // set pin to the opposite state
}
//gets called when WiFiManager enters configuration mode
void configModeCallback (WiFiManager *myWiFiManager) {
//entered config mode, make led toggle faster
ticker.attach(0.2, tick);
}
//callback notifying us of the need to save config
void saveConfigCallback () {
Serial.println("saving config");
strcpy(device_name, custom_device_name.getValue());
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["device_name"] = device_name;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
json.printTo(Serial);
json.printTo(configFile);
configFile.close();
}
boolean readConfig() {
// clean FS, for testing
// SPIFFS.format();
//read configuration from FS json
Serial.println("mounting FS...");
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success()) {
Serial.println("\nparsed json");
strcpy(device_name, json["device_name"]);
return true;
} else {
Serial.println("failed to load json config");
return false;
}
}
}
} else {
Serial.println("failed to mount FS");
return false;
}
//end read
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Starting setup");
// set led pin as output
pinMode(BUILTIN_LED, OUTPUT);
// start ticker with 0.6 because we start in AP mode and try to connect
ticker.attach(0.6, tick);
// WiFiManager
// Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wifiManager;
// wifiManager.setDebugOutput(false);
// reset settings - for testing
// wifiManager.resetSettings();
// sets timeout until configuration portal gets turned off
// useful to make it all retry or go to sleep in seconds
wifiManager.setTimeout(180);
//set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
// set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
wifiManager.setAPCallback(configModeCallback);
//add all your parameters here
wifiManager.addParameter(&custom_device_name);
// wifiManager.setCustomHeadElement("<style>html{filter: invert(100%); -webkit-filter: invert(100%);}</style>");
if(!readConfig()) {
wifiManager.resetSettings();
}
// fetches ssid and pass and tries to connect
// if it does not connect it starts an access point with auto generated name from 'ESP' and the esp's Chip ID use
// and goes into a blocking loop awaiting configuration
String ssid = "POWER_MONITOR_" + String(ESP.getChipId());
if(!wifiManager.autoConnect(ssid.c_str(), NULL)) {
Serial.println("failed to connect and hit timeout");
//reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(5000);
}
// if you get here you have connected to the WiFi
ticker.detach();
// keep LED on
digitalWrite(BUILTIN_LED, LOW);
pzem.setAddress(ip);
delay(5000);
// keep LED off
digitalWrite(BUILTIN_LED, HIGH);
}
// could make generic and pass a pointer to the right function...
float getVoltage() {
int i = 0;
float r = -1.0;
do {
r = pzem.voltage(ip);
wdt_reset();
i++;
} while ( i < MAX_ATTEMPTS && r < 0.0);
return r;
}
float getCurrent() {
int i = 0;
float r = -1.0;
do {
r = pzem.current(ip);
wdt_reset();
i++;
} while ( i < MAX_ATTEMPTS && r < 0.0);
return r;
}
float getPower() {
int i = 0;
float r = -1.0;
do {
r = pzem.power(ip);
wdt_reset();
i++;
} while ( i < MAX_ATTEMPTS && r < 0.0);
return r;
}
float getEnergy() {
int i = 0;
float r = -1.0;
do {
r = pzem.energy(ip);
wdt_reset();
i++;
} while ( i < MAX_ATTEMPTS && r < 0.0);
return r;
}
void sendMeasures() {
float v = getVoltage();
Serial.print(v);Serial.print("V; ");
float i = getCurrent();
Serial.print(i);Serial.print("A; ");
float p = getPower();
Serial.print(p);Serial.print("W; ");
float e = getEnergy();
Serial.print(e);Serial.print("Wh; ");
String payload = "";
if(v >= 0.0) payload += "voltage,device_name=" + String(device_name) + " value=" + String(v, 2) + "\n";
if(i >= 0.0) payload += "current,device_name=" + String(device_name) + " value=" + String(i, 2) + "\n";
if(p >= 0.0) payload += "power,device_name=" + String(device_name) + " value=" + String(p, 2) + "\n";
if(e >= 0.0) payload += "energy,device_name=" + String(device_name) + " value=" + String(e, 2) + "\n";
Serial.print(payload);
if (payload=="") {
Serial.println();
return;
}
http.begin("[HOST]/influxdb/write?db=power");
http.addHeader("cache-control", "no-cache");
http.setAuthorization("[HASH]");
int httpCode = http.POST(payload);
Serial.print(httpCode);
if (httpCode < 0) {
Serial.println();
Serial.printf("[HTTP]... failed, error: %s\n", http.errorToString(httpCode).c_str());
Serial.println();
Serial.print(payload);
}
http.end();
Serial.println();
}
void loop() {
// put your main code here, to run repeatedly:
sendMeasures();
delay(5000);
// is configuration portal requested?
if ( digitalRead(TRIGGER_PIN) == LOW ) {
digitalWrite(BUILTIN_LED, LOW);
WiFiManager wifiManager;
wifiManager.resetSettings();
SPIFFS.format();
ESP.reset();
delay(5000);
}
}
@crosofg
Copy link

crosofg commented Apr 25, 2017

How many Pzem can be connected by software serial? Can I add four pzem modules?

@sama1993
Copy link

Hi, So im able to connect my Pzem and get all the readings, but ive noticed that sometimes the voltage comes 0 while the circuit is connected and sometimes the current comes 0 and the energy shows invalid values, all this happens randomly. Do you how this problem can be solved ?

@bfritscher
Copy link
Author

I have a lot of bad readings -1, but polling until it gets a value works for me.

@vicfergar
Copy link

Hi, I have this same hardware working with TASMOTA firm. Did you connect it with 3.3v power supply? I modified R17 to work at 3.3v and arduino boards can read it, but I have problems with ESP8266 boards and 3.3v power supply. Thanks in advance!!

@amazigh007
Copy link

It looks like there is an error in the code. In line 166: while ( r < MAX_ATTEMPTS && r < 0.0);. I guess the r should be i instead as you want to quit the loop after the number of attempts, when nothing is read. Same issue in line 177, 188 and 199.
I used a stripped version of the code as I don't implement a wifi communication. Working well. Thanks for the good work

@bfritscher
Copy link
Author

@amazigh007 Thanks!
@vicfergar I use 5V of the nodemcu and now from PSU see

https://github.com/bfritscher/esp8266_PZEM-004T

NOTIFICATION ARE NOT SENT BY EMAIL FOR GISTS :-(

@Zadkiel2
Copy link

Zadkiel2 commented Feb 7, 2020

Hey man, I wonder if you could show some code like this for ArduinoJson v 6. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment