Hallo,
da es Nachfragen wegen der von mir erstellten Firmware gab, habe ich sie hier veröffentlicht. Folgendes gibt es zu beachten:
- Ich hafte nicht für Schäden die durch Benutzung meiner Software entstehen könnten! Nutzung auf eigene Gefahr!
- Ich biete nur bedingt support! (Je nach Laune und Zeit )
- Veränderungen am Quellcode können gerne vorgenommen und hier veröffentlicht werden
Kurzanleitung:
Es gibt keine Konfigurationsmöglichkeit. Die Daten für das eigene Netzwerk müssen im Quellcode angegeben werden. Benötigt werden die SSID das Passwort und die IP des MQTT Brokers. Eine neue Firmware kann über OTA geflasht werden, um den Wemos nicht immer wieder ausbauen zu müssen. Über "http://pzem-webupdate.local/update" sollte das gehen.
Angeschlossen werden die RX und TX Anschlüsse der PZEMs an folgende Pins (Wemos mini!):
PZEM 1 D1,D2
PZEM 2 D4,D3
PZEM 3 D5,D6
An D7 kann eine Status LED angeschlossen werden. Die signalisiert den Verbindungsaufbau zum Wlan und bzw Broker.
Nach dem Flashen und Starten gibt es unter IOBroker MQTT den neuen Datenpunkt "ENERGIE" Dort gibt es dann alle Daten zu jeder Phase und die Gesamtwerte. Die einzigen Werte die man ändern kann sind "PZEM-Reset" und "Verbrauchs-Startwert"
PZEM-Reset : Wird dieser auf true gesetzt, wird der Energiezähler aller 3 PZEM-Module auf 0 gesetzt.
Verbrauchs-Startwert: Hier kann der aktuelle Zählerstand des "echten" Hausstromzählers angegeben werden. Damit kann man den Datenpunkt "PZEM-Verbrauch" mit dem Hauszähler synchronisieren.
Viel Spaß!
//####################################################################################
//####################################################################################
//
// PZEM Energiemessgeräterfassung mit WEMOS von HomeZecke v1.1 stand 30.03.2020
// --------------------------------------------------------------------------
// v 1.0 Testphase first release -05.03.2020
// v 1.1 Zählerstartwert kann festgelegt werden -30.03.2020
//
// ToDo: Online Config für Wlan / MQTT usw. hinzuf.
//
//
//
//####################################################################################
//####################################################################################
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <Ticker.h>
#include <PubSubClient.h>
#include <PZEM004Tv30.h>
//Hier die persönlichen Daten eintragen!
//---------------------------------------------
const char* SSID = "WLAN-SSID";
const char* PSK = "PASSWORT";
const char* MQTTserver = "192.168.0.0";
const uint16_t port = 1883;
//---------------------------------------------
const char* version = "Version 1.1";
const char* host = "PZEM-webupdate";
const int STATUS_LED = 13; //D7 (Wemos D1 Mini)
float start_verbrauch = 0;
float diff_verbrauch = 0;
float old_spannung_pzem1;
float old_frequenz_pzem1;
float old_stromstaerke_pzem1;
float old_verbrauch_pzem1;
float old_leistung_pzem1;
float old_pf_pzem1;
float old_spannung_pzem2;
float old_frequenz_pzem2;
float old_stromstaerke_pzem2;
float old_verbrauch_pzem2;
float old_leistung_pzem2;
float old_pf_pzem2;
float old_spannung_pzem3;
float old_frequenz_pzem3;
float old_stromstaerke_pzem3;
float old_verbrauch_pzem3;
float old_leistung_pzem3;
float old_pf_pzem3;
float old_spannung_gesamt;
float old_stromstaerke_gesamt;
float old_leistung_gesamt;
float old_verbrauch_gesamt;
float old_frequenz_gesamt;
float old_pf_gesamt;
float old_PZEM_verbrauch;
//---functions / callbacks
void MQTTcallback(char* topic, byte* payload, unsigned int length);
void pzem_reset();
void pzem_read();
void set_counter(float counterStart);
//-Klassen definieren
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;
WiFiClient MY_NETClient_1;
PubSubClient MQTTClient(MY_NETClient_1);
Ticker myTimer1(pzem_read,5000); //Wie oft sollen die Daten aktualisiert werden? (alle 5 Sek.)
PZEM004Tv30 pzem_1(5, 4); //D1,D2 Wemos D1 Mini L1
PZEM004Tv30 pzem_2(2, 0); //D4,D3 L2
PZEM004Tv30 pzem_3(14, 12); //D5,D6 L3
//=======================================================================================================================
//MQTT CallBack
//=======================================================================================================================
void MQTTcallback(char* topic, byte* payload, unsigned int length)
{
char my_payload[length+1]; // nen string machen, länge eins mehr wegen nullterminierung
float counter_start;
Serial.print(topic);
Serial.print(" : ");
// Topic PZEMreset----------------------------------------------------------------------------------------
if (strcmp(topic,"Energie/PZEM-Reset") == 0 )
{
for (unsigned int i = 0; i < length; i++) // jedes einzelne byte in einen buchstaben umwandeln
{
my_payload[i] = (char)payload[i]; // (char)100 wäre zb ein "d"
};
my_payload[length] = '\0'; // nullterminierung
Serial.println(my_payload);
if (strcmp(my_payload,"true") == 0)
{
pzem_reset(); //Energie an den PZEM's resetten
}
}
// Topic Verbrauchs-Startwert------------------------------------------------------------------------------
if (strcmp(topic,"Energie/Verbrauchs-Startwert") == 0 )
{
for (unsigned int i = 0; i < length; i++) // jedes einzelne byte in einen buchstaben umwandeln
{
my_payload[i] = (char)payload[i]; // (char)100 wäre zb ein "d"
};
my_payload[length] = '\0'; // nullterminierung
Serial.println(my_payload);
counter_start = atof(my_payload); // nen float aus der payload machen
if (counter_start != start_verbrauch)
{
start_verbrauch = counter_start;
set_counter(counter_start); // func set_couter zum setzen aufrufen
}
}
}
//=======================================================================================================================
//WLan -Verbindung aufbauen
//=======================================================================================================================
void initWiFi()
{
Serial.println("");
Serial.print("Wifi connect...");
WiFi.begin(SSID, PSK);
while (WiFi.status() != WL_CONNECTED)
{
digitalWrite(STATUS_LED,!digitalRead(STATUS_LED));
Serial.print(".");
delay(700);
};
Serial.print("Verbunden!");
WiFi.mode(WIFI_STA);
}
//=======================================================================================================================
//MQTT -Verbindung aufbauen
//=======================================================================================================================
void initMQTT(){
MQTTClient.setServer(MQTTserver,port);
MQTTClient.setCallback(MQTTcallback);
Serial.println("");
Serial.print("MQTT verbinden...");
//--Verbindungsloop
while( !MQTTClient.connect("Energie_PZEM") )
{
digitalWrite(STATUS_LED,!digitalRead(STATUS_LED));
Serial.print("*");
delay(100);
};
digitalWrite(STATUS_LED,false);
Serial.print("MQTT ist verbunden!");
//--Topics abbonieren----------------------------------------------------
if (MQTTClient.subscribe("Energie/PZEM-Reset"))
{
Serial.println("MQTT : Energie: Reset aboniert");
};
if (MQTTClient.subscribe("Energie/Verbrauchs-Startwert"))
{
Serial.println("MQTT : Energie/Verbrauchs-Startwert aboniert");
};
}
//=======================================================================================================================
//PZEM Module auslesen und publishen
//=======================================================================================================================
void pzem_read(){
float spannung_gesamt;
float stromstaerke_gesamt;
float leistung_gesamt;
float verbrauch_gesamt;
float frequenz_gesamt;
float pf_gesamt;
float PZEM_verbrauch = 0;
float spannung_pzem1 = pzem_1.voltage();
float stromstaerke_pzem1 = pzem_1.current();
float leistung_pzem1 = pzem_1.power();
float verbrauch_pzem1 = pzem_1.energy();
float frequenz_pzem1 = pzem_1.frequency();
float pf_pzem1 = pzem_1.pf();
float spannung_pzem2 = pzem_2.voltage();
float stromstaerke_pzem2 = pzem_2.current();
float leistung_pzem2 = pzem_2.power();
float verbrauch_pzem2 = pzem_2.energy();
float frequenz_pzem2 = pzem_2.frequency();
float pf_pzem2 = pzem_2.pf();
float spannung_pzem3 = pzem_3.voltage();
float stromstaerke_pzem3 = pzem_3.current();
float leistung_pzem3 = pzem_3.power();
float verbrauch_pzem3 = pzem_3.energy();
float frequenz_pzem3 = pzem_3.frequency();
float pf_pzem3 = pzem_3.pf();
// char* Temp_String = " ";
char Temp_String[12];
if(!isnan(spannung_pzem1) && old_spannung_pzem1 != spannung_pzem1)
{
dtostrf(spannung_pzem1,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L1/SpannungL1", Temp_String);
old_spannung_pzem1 = spannung_pzem1;
}
if(!isnan(spannung_pzem2) && old_spannung_pzem2 != spannung_pzem2)
{
dtostrf(spannung_pzem2,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L2/SpannungL2", Temp_String);
old_spannung_pzem2 = spannung_pzem2;
}
if(!isnan(spannung_pzem3) && old_spannung_pzem3 != spannung_pzem3)
{
dtostrf(spannung_pzem3,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L3/SpannungL3", Temp_String);
old_spannung_pzem3 = spannung_pzem3;
}
if(!isnan(stromstaerke_pzem1) && old_stromstaerke_pzem1 != stromstaerke_pzem1)
{
dtostrf(stromstaerke_pzem1,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L1/StromstaerkeL1", Temp_String);
old_stromstaerke_pzem1 = stromstaerke_pzem1;
}
if(!isnan(stromstaerke_pzem2) && old_stromstaerke_pzem2 != stromstaerke_pzem2)
{
dtostrf(stromstaerke_pzem2,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L2/StromstaerkeL2", Temp_String);
old_stromstaerke_pzem2 = stromstaerke_pzem2;
}
if(!isnan(stromstaerke_pzem3) && old_stromstaerke_pzem3 != stromstaerke_pzem3)
{
dtostrf(stromstaerke_pzem3,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L3/StromstaerkeL3", Temp_String);
old_stromstaerke_pzem3 = stromstaerke_pzem3;
}
if(!isnan(leistung_pzem1) && old_leistung_pzem1 != leistung_pzem1)
{
dtostrf(leistung_pzem1,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L1/LeistungL1", Temp_String);
old_leistung_pzem1 = leistung_pzem1;
}
if(!isnan(leistung_pzem2) && old_leistung_pzem2 != leistung_pzem2)
{
dtostrf(leistung_pzem2,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L2/LeistungL2", Temp_String);
old_leistung_pzem2 = leistung_pzem2;
}
if(!isnan(leistung_pzem3) && old_leistung_pzem3 != leistung_pzem3)
{
dtostrf(leistung_pzem3,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L3/LeistungL3", Temp_String);
old_leistung_pzem3 = leistung_pzem3;
}
if(!isnan(verbrauch_pzem1) && old_verbrauch_pzem1 != verbrauch_pzem1)
{
dtostrf(verbrauch_pzem1,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L1/VerbrauchL1", Temp_String);
old_verbrauch_pzem1 = verbrauch_pzem1;
}
if(!isnan(verbrauch_pzem2) && old_verbrauch_pzem2 != verbrauch_pzem2)
{
dtostrf(verbrauch_pzem2,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L2/VerbrauchL2", Temp_String);
old_verbrauch_pzem2 = verbrauch_pzem2;
}
if(!isnan(verbrauch_pzem3) && old_verbrauch_pzem3 != verbrauch_pzem3)
{
dtostrf(verbrauch_pzem3,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L3/VerbrauchL3", Temp_String);
old_verbrauch_pzem3 = verbrauch_pzem3;
}
if(!isnan(frequenz_pzem1) && old_frequenz_pzem1 != frequenz_pzem1)
{
dtostrf(frequenz_pzem1,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L1/FrequenzL1", Temp_String);
old_frequenz_pzem1 = frequenz_pzem1;
}
if(!isnan(frequenz_pzem2) && old_frequenz_pzem1 != frequenz_pzem2)
{
dtostrf(frequenz_pzem2,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L2/FrequenzL2", Temp_String);
old_frequenz_pzem2 = frequenz_pzem2;
}
if(!isnan(frequenz_pzem3) && old_frequenz_pzem3 != frequenz_pzem3)
{
dtostrf(frequenz_pzem3,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L3/FrequenzL3", Temp_String);
old_frequenz_pzem3 = frequenz_pzem3;
}
if(!isnan(pf_pzem1) && old_pf_pzem1 != pf_pzem1)
{
dtostrf(pf_pzem1,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L1/PowerFactorL1", Temp_String);
old_pf_pzem1 = pf_pzem1;
}
if(!isnan(pf_pzem2) && old_pf_pzem2 != pf_pzem2)
{
dtostrf(pf_pzem2,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L2/PowerFactorL2", Temp_String);
old_pf_pzem2 = pf_pzem2;
}
if(!isnan(pf_pzem3) && old_pf_pzem3 != pf_pzem3)
{
dtostrf(pf_pzem3,5 , 2, Temp_String);
MQTTClient.publish ("Energie/L3/PowerFactorL3", Temp_String);
old_pf_pzem3 = pf_pzem3;
}
if(!isnan(spannung_pzem1) && !isnan(spannung_pzem2) && !isnan(spannung_pzem3))
{
spannung_gesamt = (spannung_pzem1 + spannung_pzem2 + spannung_pzem3) / 3 ;
}
else
{
spannung_gesamt = 0;
}
if(!isnan(stromstaerke_pzem1) && !isnan(stromstaerke_pzem2) && !isnan(stromstaerke_pzem3))
{
stromstaerke_gesamt = stromstaerke_pzem1 + stromstaerke_pzem2 + stromstaerke_pzem3 ;
}
else
{
stromstaerke_gesamt = 0;
}
if(!isnan(leistung_pzem1) && !isnan(leistung_pzem2) && !isnan(leistung_pzem3))
{
leistung_gesamt = leistung_pzem1 + leistung_pzem2 + leistung_pzem3 ;
}
else
{
leistung_gesamt = 0;
}
if(!isnan(verbrauch_pzem1) && !isnan(verbrauch_pzem2) && !isnan(verbrauch_pzem3))
{
verbrauch_gesamt = verbrauch_pzem1 + verbrauch_pzem2 + verbrauch_pzem3 ;
PZEM_verbrauch = verbrauch_gesamt;
verbrauch_gesamt = start_verbrauch + verbrauch_gesamt - diff_verbrauch;
}
else
{
verbrauch_gesamt = 0;
}
if(!isnan(frequenz_pzem1) && !isnan(frequenz_pzem2) && !isnan(frequenz_pzem3))
{
frequenz_gesamt = (frequenz_pzem1 + frequenz_pzem2 + frequenz_pzem3) / 3;
}
else
{
frequenz_gesamt = 0;
}
if(!isnan(pf_pzem1) && !isnan(pf_pzem2) && !isnan(pf_pzem3))
{
pf_gesamt = (pf_pzem1 + pf_pzem2 + pf_pzem3) / 3 ;
}
else
{
pf_gesamt = 0;
}
if (old_spannung_gesamt != spannung_gesamt)
{
dtostrf(spannung_gesamt,5 , 2, Temp_String);
MQTTClient.publish("Energie/Spannung",Temp_String);
old_spannung_gesamt = spannung_gesamt;
}
if (old_stromstaerke_gesamt != stromstaerke_gesamt)
{
dtostrf(stromstaerke_gesamt,5 , 2, Temp_String);
MQTTClient.publish("Energie/Stromstaerke",Temp_String);
old_stromstaerke_gesamt = stromstaerke_gesamt;
}
if (old_leistung_gesamt != leistung_gesamt)
{
dtostrf(leistung_gesamt,5 , 2, Temp_String);
MQTTClient.publish("Energie/Leistung",Temp_String);
old_leistung_gesamt = leistung_gesamt;
}
if (old_verbrauch_gesamt != verbrauch_gesamt)
{
dtostrf(verbrauch_gesamt,5 , 2, Temp_String);
MQTTClient.publish("Energie/Verbrauch",Temp_String);
old_verbrauch_gesamt = verbrauch_gesamt;
}
if (old_frequenz_gesamt != frequenz_gesamt)
{
dtostrf(frequenz_gesamt,5 , 2, Temp_String);
MQTTClient.publish("Energie/Frequenz",Temp_String);
old_frequenz_gesamt = frequenz_gesamt;
}
if (old_pf_gesamt != pf_gesamt)
{
dtostrf(pf_gesamt,5 , 2, Temp_String);
MQTTClient.publish("Energie/PowerFactor",Temp_String);
old_pf_gesamt = pf_gesamt;
}
if (old_PZEM_verbrauch != PZEM_verbrauch)
{
dtostrf(PZEM_verbrauch,5 , 2, Temp_String);
MQTTClient.publish("Energie/PZEM-Verbrauch",Temp_String);
old_PZEM_verbrauch = PZEM_verbrauch;
}
digitalWrite(STATUS_LED,true);
delay(50);
digitalWrite(STATUS_LED,false);
};
//=======================================================================================================================
//Energieverbrauch in pzem Modulen zurücksetzen
//=======================================================================================================================
void pzem_reset()
{
myTimer1.pause();
Serial.println("Energieverbrauch wird zurückgesetzt..!");
pzem_1.resetEnergy();
pzem_2.resetEnergy();
pzem_3.resetEnergy();
delay(1000);
MQTTClient.publish("Energie/PZEM-Reset","false");
myTimer1.resume();
}
//=======================================================================================================================
//Startwert für Verbrauchszähler setzen
//=======================================================================================================================
void set_counter(float counterStart){
myTimer1.pause();
float verbrauch_pzem1 = pzem_1.energy();
float verbrauch_pzem2 = pzem_2.energy();
float verbrauch_pzem3 = pzem_3.energy();
float verbrauch_gesamt = verbrauch_pzem1 + verbrauch_pzem2 + verbrauch_pzem3;
diff_verbrauch = verbrauch_gesamt;
myTimer1.resume();
}
// #####################################################################################################
// #####################################################################################################
// Die SETUP Routine
// #####################################################################################################
// #####################################################################################################
void setup()
{
pinMode(STATUS_LED,OUTPUT);
digitalWrite(STATUS_LED,LOW);
Serial.begin(9600);
delay(100);
Serial.println("");
Serial.println("PZEM 3-Phasen-Reader");
Serial.println(version);
Serial.println("");
initWiFi();
initMQTT();
MQTTClient.publish("Energie/Info",version);
//--OTA Over the Air update einrichten
MDNS.begin(host);
httpUpdater.setup(&httpServer);
httpServer.begin();
MDNS.addService("http", "tcp", 80);
Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
//--Timer für PZEM lesen und senden starten
myTimer1.start();
}
// #####################################################################################################
// #####################################################################################################
// Die Haupt- LOOP Routine
// #####################################################################################################
// #####################################################################################################
void loop(){
//--Wifi verloren? dann neu aufbauen
if (WiFi.status() != WL_CONNECTED)
{
initWiFi();
};
//--MQTT Verbindung verloren? dann neu aufbauen
if (MQTTClient.state() != MQTT_CONNECTED)
{
initMQTT();
};
MQTTClient.loop();
httpServer.handleClient();
MDNS.update();
myTimer1.update();
delay(10);
}