Sketch ( ESP32 ):
#include <Arduino.h>
#include <WiFi.h>
#include <ModbusClientTCPasync.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <HTTPClient.h>
#include <AsyncTCP.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>
#include <Preferences.h>
Preferences preferences;
#include "webpages.h" //file Upload helper
const String default_httpuser = "admin";
const String default_httppassword = "admin";
// configuration structure
struct Config {
String httpuser; // username to access web admin
String httppassword; // password to access web admin
};
Config config; // configuration
String listFiles(bool ishtml = false);
//Div. globale Variablen
#define FIRMWARE_VERSION "v1.0.0"
int debuglevel = 0;
bool L1_error; //Stromklemme Error
bool L2_error; //Stromklemme Error
bool L3_error; //Stromklemme Error
bool N_error; //Stromklemme Error
bool S_error; //Phasen sequenz Error
float N_A; //Neutalleiter Strom
bool N_A_error; //Neutralleiter Nichtübereinstimmung
bool N_O_error; //Neutrallleiter Überstrom Error
float Total_A; //Strom Ingesamt
float Total_W; //Leistung Ingesamt
float Total_sch; //Scheinleistung Insgesamt
float L1_V; //Spannung der Phase
float L1_A; //Strom der Phase
float L1_W; //Leistung der Phase
float L1_SCH; //Scheinleistung der Phase
float L1_PF; //Powerfactor der Phase
bool L1_O_W_error; //Überleistung Error
bool L1_O_V_error; //Überspannung Error
bool L1_O_A_error; //Überstrom Error
float L1_Hz; //Freuqenz der Phase
float L2_V; //Spannung der Phase
float L2_A; //Strom der Phase
float L2_W; //Leistung der Phase
float L2_SCH; //Scheinleistung der Phase
float L2_PF; //Powerfactor der Phase
bool L2_O_W_error; //Überleistung Error
bool L2_O_V_error; //Überspannung Error
bool L2_O_A_error; //Überstrom Error
float L2_Hz; //Freuqenz der Phase
float L3_V; //Spannung der Phase
float L3_A; //Strom der Phase
float L3_W; //Leistung der Phase
float L3_SCH; //Scheinleistung der Phase
float L3_PF; //Powerfactor der Phase
bool L3_O_W_error; //Überleistung Error
bool L3_O_V_error; //Überspannung Error
bool L3_O_A_error; //Überstrom Error
float L3_Hz; //Freuqenz der Phase
static unsigned long push_lastMillis = 0;
int pushservice = 0;
String pushservice_url = "";
long push_interval = 900000;
String modbus_ip; //nur zu ausgabe für get json
int sip_ipa;
int sip_ipb;
int sip_ipc;
int sip_ipd;
int gip_ipa;
int gip_ipb;
int gip_ipc;
int gip_ipd;
int sub_ipa;
int sub_ipb;
int sub_ipc;
int sub_ipd;
int pdns_ipa;
int pdns_ipb;
int pdns_ipc;
int pdns_ipd;
int sdns_ipa;
int sdns_ipb;
int sdns_ipc;
int sdns_ipd;
int modbus_ipa;
int modbus_ipb;
int modbus_ipc;
int modbus_ipd;
//Modbus
int modbus_interval = 100;
static unsigned long Modbus_lastMillis = 0;
int modbus_timeout = 1500; //Dauert die Antwort länger, werden keine Daten zum Soyo geschickt ( Power = 0 )
int modbus_fehler_count = 0;
//RS485 control
#define RS485_RXPin 32 // Serial Receive pin ( Pin CFG)
#define RS485_TXPin 33 // Serial Transmit pin ( Pin RS485_EN)
#define RS485_TX_PIN_VALUE HIGH
#define RS485_RX_PIN_VALUE LOW
HardwareSerial RS485Serial(1);
//Webserver instanz
AsyncWebServer server(80);
//Wifi zugangsdaten
String ssid = "MeinWlanName";
String password = "MeinWlanPasswort";
//Ethernet
#ifndef ETH_PHY_TYPE
#define ETH_PHY_TYPE ETH_PHY_LAN8720
#define ETH_PHY_ADDR 0
#define ETH_PHY_MDC 23
#define ETH_PHY_MDIO 18
#define ETH_PHY_POWER -1
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
#endif
#include <ETH.h>
int eth_static_ip = 0; //Ob static ip verwendet wird oder nicht ( Wird überschrieben von Preference weiter unten )
//Soyo Timer / Variablen
int soyo_power_data[8] = {0x24, 0x56, 0x00, 0x21, 0x00, 0x00, 0x80, 0x08}; // 0 Watt
float power_total;
int anz_soyo = 1;
unsigned long Soyo_previousMillis = 0;
int Soyo_interval = 50;
long max_einspeisung = 1200;
int long einspeisung_reduzieren = 7;
static bool eth_connected = false;
// WARNING: onEvent is called from a separate FreeRTOS task (thread)!
void onEvent(arduino_event_id_t event) {
switch (event) {
case ARDUINO_EVENT_ETH_START:
Serial.println("ETH Started");
// The hostname must be set after the interface is started, but needs
// to be set before DHCP, so set it from the event handler thread.
ETH.setHostname("Pro3M-TCP2RTU");
break;
case ARDUINO_EVENT_ETH_CONNECTED: Serial.println("ETH Connected"); break;
case ARDUINO_EVENT_ETH_GOT_IP:
Serial.println("ETH Got IP");
Serial.println(ETH);
eth_connected = true;
break;
case ARDUINO_EVENT_ETH_LOST_IP:
Serial.println("ETH Lost IP");
eth_connected = false;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
eth_connected = false;
break;
case ARDUINO_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
eth_connected = false;
break;
default: break;
}
}
IPAddress ip = {192, 168, 178, 1}; // IP address of modbus server wird später durch eeprom überschrieben
uint16_t port = 502; // port of modbus server
// Create a ModbusTCP client instance
ModbusClientTCPasync MB(ip, port);
void setup() {
//preferences.clear();
Serial.begin(115200);
preferences.begin("speicher", false);
anz_soyo = preferences.getInt("anz_soyo", 3);
pushservice = preferences.getInt("pushser", 0);
pushservice_url = preferences.getString("pushserurl", "");
push_interval = preferences.getInt("pushinv", 900000);
ssid = preferences.getString("ssid", "");
password = preferences.getString("wpass", "");
config.httpuser = preferences.getString("uploadu", "admin");
config.httppassword = preferences.getString("uploadp", "admin");
max_einspeisung = preferences.getInt("max_eins", 3000);
einspeisung_reduzieren = preferences.getInt("einsp_red", 0);
Soyo_interval = preferences.getInt("soyo_intv", 100);
modbus_interval = preferences.getInt("mod_intv", 100);
modbus_timeout = preferences.getInt("mod_timeo", 3000);
modbus_ipa = preferences.getInt("mod_ipa", 192);
modbus_ipb = preferences.getInt("mod_ipb", 168);
modbus_ipc = preferences.getInt("mod_ipc", 178);
modbus_ipd = preferences.getInt("mod_ipd", 71);
IPAddress ip = {modbus_ipa, modbus_ipb, modbus_ipc, modbus_ipd};
modbus_ip = String(modbus_ipa)+"."+String(modbus_ipb)+"."+String(modbus_ipc)+"."+String(modbus_ipd);
Serial.println("Booting");
Serial.print("Firmware: "); Serial.println(FIRMWARE_VERSION);
WiFi.mode(WIFI_STA);
RS485Serial.begin(4800, SERIAL_8N1, RS485_RXPin, RS485_TXPin);
//Pin für DHCP erzwingen
pinMode(2, INPUT_PULLUP);
//Ethernet
Network.onEvent(onEvent);
ETH.begin();
eth_static_ip = preferences.getInt("ip-static", 0);
sip_ipa = preferences.getInt("sip_ipa", 192);
sip_ipb = preferences.getInt("sip_ipb", 168);
sip_ipc = preferences.getInt("sip_ipc", 178);
sip_ipd = preferences.getInt("sip_ipd", 182);
IPAddress local_IP(sip_ipa, sip_ipb, sip_ipc, sip_ipd); // Static IP Address
gip_ipa = preferences.getInt("gip_ipa", 192);
gip_ipb = preferences.getInt("gip_ipb", 168);
gip_ipc = preferences.getInt("gip_ipc", 178);
gip_ipd = preferences.getInt("gip_ipd", 1);
IPAddress gateway(gip_ipa, gip_ipb, gip_ipc, gip_ipd); // Gateway
sub_ipa = preferences.getInt("sub_ipa", 255);
sub_ipb = preferences.getInt("sub_ipb", 255);
sub_ipc = preferences.getInt("sub_ipc", 255);
sub_ipd = preferences.getInt("sub_ipd", 0);
IPAddress subnet(sub_ipa, sub_ipb, sub_ipc, sub_ipd); // Subnet Mask
pdns_ipa = preferences.getInt("pdns_ipa", 8);
pdns_ipb = preferences.getInt("pdns_ipb", 8);
pdns_ipc = preferences.getInt("pdns_ipc", 8);
pdns_ipd = preferences.getInt("pdns_ipd", 8);
IPAddress primaryDNS(pdns_ipa, pdns_ipb, pdns_ipc, pdns_ipd); // Primary DNS
sdns_ipa = preferences.getInt("sdns_ipa", 8);
sdns_ipb = preferences.getInt("sdns_ipb", 8);
sdns_ipc = preferences.getInt("sdns_ipc", 4);
sdns_ipd = preferences.getInt("sdns_ipd", 4);
IPAddress secondaryDNS(sdns_ipa, sdns_ipb, sdns_ipc, sdns_ipd); // Secondary DNS
if(eth_static_ip == 1 && digitalRead(2) == HIGH){
Serial.println("Setze feste IP Adresse");
ETH.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
delay(500);
}
// Connect to WiFi
WiFi.begin(ssid.c_str(), password.c_str());
delay(200);
Serial.println("Baue WiFi verbindung auf...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(". ");
delay(1000);
}
IPAddress wIP = WiFi.localIP();
Serial.printf("WIFi IP address: %u.%u.%u.%u\n", wIP[0], wIP[1], wIP[2], wIP[3]);
// Set up ModbusTCP client.
// - provide onData handler function
MB.onDataHandler(&handleData);
// - provide onError handler function
MB.onErrorHandler(&handleError);
// Set message timeout to 2000ms and interval between requests to the same host to 200ms
MB.setTimeout(2000);
// Start ModbusTCP background task
MB.setIdleTimeout(60000);
MB.disconnect();
MB.connect(ip, 502);
//filesystem
Serial.println("Mounting SPIFFS ...");
if (!SPIFFS.begin(true)) {
// if you have not used SPIFFS before on a ESP32, it will show this error.
// after a reboot SPIFFS will be configured and will happily work.
Serial.println("ERROR: Cannot mount SPIFFS, Rebooting");
ESP.restart();
}
Serial.print("SPIFFS Free: "); Serial.println(humanReadableSize((SPIFFS.totalBytes() - SPIFFS.usedBytes())));
Serial.print("SPIFFS Used: "); Serial.println(humanReadableSize(SPIFFS.usedBytes()));
Serial.print("SPIFFS Total: "); Serial.println(humanReadableSize(SPIFFS.totalBytes()));
//Server Antworten
server.onNotFound(notFound);
server.onFileUpload(handleUpload);
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
server.on("/einstellungen", HTTP_GET, [](AsyncWebServerRequest *request)
{
if (checkUserWebAuth(request)) {
if (request->hasParam("wert")){
String wert = request->getParam("wert")->value();
if(wert == "anz_soyo" && request->hasParam("value")){
anz_soyo = request->getParam("value")->value().toInt();
preferences.putInt("anz_soyo", anz_soyo);
String json;
json += "{";
json += "\"meldung\":\"Anzahl Inverter eingestellt.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "pushservice" && request->hasParam("value")){
pushservice = request->getParam("value")->value().toInt();
preferences.putInt("pushser", pushservice);
String json;
json += "{";
if(pushservice == 1){
json += "\"meldung\":\"Pushservice Eingeschaltet.\"";
}else{
json += "\"meldung\":\"Pushservice Ausgeschaltet.\"";
}
json += "}";
request->send(200, "application/json", json);
}
if(wert == "pushservice_url" && request->hasParam("value")){
pushservice_url = request->getParam("value")->value();
preferences.putString("pushserurl", pushservice_url);
delay(1000); //Gib pref ne Sekunde
String json;
json += "{";
json += "\"meldung\":\"Pushservice URL geändert\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "push_interval" && request->hasParam("value")){
push_interval = request->getParam("value")->value().toInt();
preferences.putLong("pushinv", push_interval);
String json;
json += "{";
json += "\"meldung\":\"Pushservice Intervall eingestellt.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "httpuser" && request->hasParam("value")){
config.httpuser = request->getParam("value")->value();
preferences.putString("uploadu", config.httpuser);
String json;
json += "{";
json += "\"meldung\":\"Benutzer geändert\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "httppassword" && request->hasParam("value")){
config.httppassword = request->getParam("value")->value();
preferences.putString("uploadp", config.httppassword);
String json;
json += "{";
json += "\"meldung\":\"Passwort geändert\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "wifipassword" && request->hasParam("value")){
password = request->getParam("value")->value();
preferences.putString("wpass", password);
String json;
json += "{";
json += "\"meldung\":\"Wifi Passwort geändert\"";
json += "}";
request->send(200, "application/json", json);
WiFi.begin(ssid.c_str(), password.c_str());
}
if(wert == "wifissid" && request->hasParam("value")){
ssid = request->getParam("value")->value();
preferences.putString("ssid", ssid);
String json;
json += "{";
json += "\"meldung\":\"Wifi SSID geändert\"";
json += "}";
request->send(200, "application/json", json);
WiFi.begin(ssid.c_str(), password.c_str());
}
if(wert == "max_einspeisung" && request->hasParam("value")){
max_einspeisung = request->getParam("value")->value().toInt();
preferences.putLong("max_eins", max_einspeisung);
String json;
json += "{";
json += "\"meldung\":\"Maximale Inverter Power gesetzt.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "einspeisung_reduzieren" && request->hasParam("value")){
einspeisung_reduzieren = request->getParam("value")->value().toInt();
preferences.putLong("einsp_red", einspeisung_reduzieren);
String json;
json += "{";
json += "\"meldung\":\"Leistungsdrossel gesetzt.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "Soyo_interval" && request->hasParam("value")){
Soyo_interval = request->getParam("value")->value().toInt();
preferences.putInt("soyo_intv", Soyo_interval);
String json;
json += "{";
json += "\"meldung\":\"Inverter RTU Intervall geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "modbus_interval" && request->hasParam("value")){
modbus_interval = request->getParam("value")->value().toInt();
preferences.putInt("mod_intv", modbus_interval);
String json;
json += "{";
json += "\"meldung\":\"Pro3EM TCP Intervall geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "modbus_timeout" && request->hasParam("value")){
modbus_timeout = request->getParam("value")->value().toInt();
preferences.putInt("mod_timeo", modbus_timeout);
String json;
json += "{";
json += "\"meldung\":\"Pro3EM TCP Timeout geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "modbus_ipa" && request->hasParam("modbus_ipa") && request->hasParam("modbus_ipb") && request->hasParam("modbus_ipc") && request->hasParam("modbus_ipd")){
modbus_ipa = request->getParam("modbus_ipa")->value().toInt();
modbus_ipb = request->getParam("modbus_ipb")->value().toInt();
modbus_ipc = request->getParam("modbus_ipc")->value().toInt();
modbus_ipd = request->getParam("modbus_ipd")->value().toInt();
IPAddress ip = {modbus_ipa, modbus_ipb, modbus_ipc, modbus_ipd};
MB.disconnect();
MB.connect(ip, 502);
preferences.putInt("mod_ipa", modbus_ipa);
preferences.putInt("mod_ipb", modbus_ipb);
preferences.putInt("mod_ipc", modbus_ipc);
preferences.putInt("mod_ipd", modbus_ipd);
String json;
json += "{";
json += "\"meldung\":\"Pro3EM TCP IP geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "eth_static_ip" && request->hasParam("value")){
eth_static_ip = request->getParam("value")->value().toInt();
preferences.putInt("ip-static", eth_static_ip);
String json;
json += "{";
if(eth_static_ip == 1){
json += "\"meldung\":\"Static IP Eingeschaltet.\"";
}else{
json += "\"meldung\":\"Static IP Ausgeschaltet.\"";
}
json += "}";
request->send(200, "application/json", json);
}
if(wert == "sip_ipa" && request->hasParam("sip_ipa") && request->hasParam("sip_ipb") && request->hasParam("sip_ipc") && request->hasParam("sip_ipd")){
sip_ipa = request->getParam("sip_ipa")->value().toInt();
sip_ipb = request->getParam("sip_ipb")->value().toInt();
sip_ipc = request->getParam("sip_ipc")->value().toInt();
sip_ipd = request->getParam("sip_ipd")->value().toInt();
IPAddress local_IP(sip_ipa, sip_ipb, sip_ipc, sip_ipd);
preferences.putInt("sip_ipa", sip_ipa);
preferences.putInt("sip_ipb", sip_ipb);
preferences.putInt("sip_ipc", sip_ipc);
preferences.putInt("sip_ipd", sip_ipd);
String json;
json += "{";
json += "\"meldung\":\"Static IP geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "gip_ipa" && request->hasParam("gip_ipa") && request->hasParam("gip_ipb") && request->hasParam("gip_ipc") && request->hasParam("gip_ipd")){
gip_ipa = request->getParam("gip_ipa")->value().toInt();
gip_ipb = request->getParam("gip_ipb")->value().toInt();
gip_ipc = request->getParam("gip_ipc")->value().toInt();
gip_ipd = request->getParam("gip_ipd")->value().toInt();
IPAddress gateway(gip_ipa, gip_ipb, gip_ipc, gip_ipd);
preferences.putInt("gip_ipa", gip_ipa);
preferences.putInt("gip_ipb", gip_ipb);
preferences.putInt("gip_ipc", gip_ipc);
preferences.putInt("gip_ipd", gip_ipd);
String json;
json += "{";
json += "\"meldung\":\"Gateway IP geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "sub_ipa" && request->hasParam("sub_ipa") && request->hasParam("sub_ipb") && request->hasParam("sub_ipc") && request->hasParam("sub_ipd")){
sub_ipa = request->getParam("sub_ipa")->value().toInt();
sub_ipb = request->getParam("sub_ipb")->value().toInt();
sub_ipc = request->getParam("sub_ipc")->value().toInt();
sub_ipd = request->getParam("sub_ipd")->value().toInt();
IPAddress subnet(sub_ipa, sub_ipb, sub_ipc, sub_ipd);
preferences.putInt("sub_ipa", sub_ipa);
preferences.putInt("sub_ipb", sub_ipb);
preferences.putInt("sub_ipc", sub_ipc);
preferences.putInt("sub_ipd", sub_ipd);
String json;
json += "{";
json += "\"meldung\":\"subnet IP geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "pdns_ipa" && request->hasParam("pdns_ipa") && request->hasParam("pdns_ipb") && request->hasParam("pdns_ipc") && request->hasParam("pdns_ipd")){
pdns_ipa = request->getParam("pdns_ipa")->value().toInt();
pdns_ipb = request->getParam("pdns_ipb")->value().toInt();
pdns_ipc = request->getParam("pdns_ipc")->value().toInt();
pdns_ipd = request->getParam("pdns_ipd")->value().toInt();
IPAddress primaryDNS(pdns_ipa, pdns_ipb, pdns_ipc, pdns_ipd);
preferences.putInt("pdns_ipa", pdns_ipa);
preferences.putInt("pdns_ipb", pdns_ipb);
preferences.putInt("pdns_ipc", pdns_ipc);
preferences.putInt("pdns_ipd", pdns_ipd);
String json;
json += "{";
json += "\"meldung\":\"primaryDNS IP geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
if(wert == "sdns_ipa" && request->hasParam("sdns_ipa") && request->hasParam("sdns_ipb") && request->hasParam("sdns_ipc") && request->hasParam("sdns_ipd")){
sdns_ipa = request->getParam("sdns_ipa")->value().toInt();
sdns_ipb = request->getParam("sdns_ipb")->value().toInt();
sdns_ipc = request->getParam("sdns_ipc")->value().toInt();
sdns_ipd = request->getParam("sdns_ipd")->value().toInt();
IPAddress secondaryDNS(sdns_ipa, sdns_ipb, sdns_ipc, sdns_ipd);
preferences.putInt("sdns_ipa", sdns_ipa);
preferences.putInt("sdns_ipb", sdns_ipb);
preferences.putInt("sdns_ipc", sdns_ipc);
preferences.putInt("sdns_ipd", sdns_ipd);
String json;
json += "{";
json += "\"meldung\":\"secondaryDNS IP geändert.\"";
json += "}";
request->send(200, "application/json", json);
}
}
}else{
String json;
json += "{";
json += "\"meldung\":\"Nicht eingeloggt, keine Änderungen vorgenommen.\"";
json += "}";
request->send(200, "application/json", json);
}
});
server.on("/quicklogin", HTTP_GET, [](AsyncWebServerRequest *request)
{
if (checkUserWebAuth(request)) {
request->redirect("/");
}else{
return request->requestAuthentication();
}
});
server.on("/werksreset", HTTP_GET, [](AsyncWebServerRequest *request)
{
preferences.clear();
ESP.restart();
});
/*
server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request)
{
String json;
json += "{";
json += "\"meldung\":\"Wird neugestartet.\"";
json += "}";
request->send(200, "application/json", json);
request->redirect("/");
delay(3000);
ESP.restart();
});
*/
server.on("/json", HTTP_GET, [](AsyncWebServerRequest *request)
{
String json;
json += "{";
json += "\"antwort\":\"json_all\",";
json += "\"pro3em_ip\":\""+modbus_ip+"\",\"L1_error\":\""+String(L1_error)+"\",\"L2_error\":\""+String(L2_error)+"\",\"L3_error\":\""+String(L3_error)+"\",\"N_error\":\""+String(N_error)+"\",\"S_error\":\""+String(S_error)+"\",\"N_A\":\""+String(N_A)+"\",\"N_A_error\":\""+String(N_A_error)+"\",\"N_O_error\":\""+String(N_O_error)+"\",\"Total_A\":\""+String(Total_A)+"\",\"Total_W\":\""+String(Total_W)+"\",\"Total_sch\":\""+String(Total_sch)+"\",\"L1_V\":\""+String(L1_V)+"\",\"L1_A\":\""+String(L1_A)+"\",\"L1_W\":\""+String(L1_W)+"\",\"L1_SCH\":\""+String(L1_SCH)+"\",\"L1_PF\":\""+String(L1_PF)+"\",\"L1_O_W_error\":\""+String(L1_O_W_error)+"\",\"L1_O_V_error\":\""+String(L1_O_V_error)+"\",\"L1_O_A_error\":\""+String(L1_O_A_error)+"\",\"L1_Hz\":\""+String(L1_Hz)+"\",\"L2_V\":\""+String(L2_V)+"\",\"L2_A\":\""+String(L2_A)+"\",\"L2_W\":\""+String(L2_W)+"\",\"L2_SCH\":\""+String(L2_SCH)+"\",\"L2_PF\":\""+String(L2_PF)+"\",\"L2_O_W_error\":\""+String(L2_O_W_error)+"\",\"L2_O_V_error\":\""+String(L2_O_V_error)+"\",\"L2_O_A_error\":\""+String(L2_O_A_error)+"\",\"L2_Hz\":\""+String(L2_Hz)+"\",\"L3_V\":\""+String(L3_V)+"\",\"L3_A\":\""+String(L3_A)+"\",\"L3_W\":\""+String(L3_W)+"\",\"L3_SCH\":\""+String(L3_SCH)+"\",\"L3_PF\":\""+String(L3_PF)+"\",\"L3_O_W_error\":\""+String(L3_O_W_error)+"\",\"L3_O_V_error\":\""+String(L3_O_V_error)+"\",\"L3_O_A_error\":\""+String(L3_O_A_error)+"\",\"L3_Hz\":\""+String(L3_Hz)+"\",\"wifi_signal\":\""+String(WiFi.RSSI())+"\",";
json += "\"anz_soyo\":\""+String(anz_soyo)+"\",\"pushservice\":\""+String(pushservice)+"\",\"pushservice_url\":\""+String(pushservice_url)+"\",";
json += "\"push_interval\":\""+String(push_interval)+"\"";
json += ",\"httpuser\":\""+String(config.httpuser)+"\",\"max_einspeisung\":\""+String(max_einspeisung)+"\",\"einspeisung_reduzieren\":\""+String(einspeisung_reduzieren)+"\",\"Soyo_interval\":\""+String(Soyo_interval)+"\",\"modbus_interval\":\""+String(modbus_interval)+"\",\"modbus_timeout\":\""+String(modbus_timeout)+"\",\"modbus_ipa\":\""+String(modbus_ipa)+"\",\"modbus_ipb\":\""+String(modbus_ipb)+"\",\"modbus_ipc\":\""+String(modbus_ipc)+"\",\"modbus_ipd\":\""+String(modbus_ipd)+"\",\"eth_static_ip\":\""+String(eth_static_ip)+"\",\"sip_ipa\":\""+String(sip_ipa)+"\",\"sip_ipb\":\""+String(sip_ipb)+"\",\"sip_ipc\":\""+String(sip_ipc)+"\",\"sip_ipd\":\""+String(sip_ipd)+"\",\"gip_ipa\":\""+String(gip_ipa)+"\",\"gip_ipb\":\""+String(gip_ipb)+"\",\"gip_ipc\":\""+String(gip_ipc)+"\",\"gip_ipd\":\""+String(gip_ipd)+"\",\"sub_ipa\":\""+String(sub_ipa)+"\",\"sub_ipb\":\""+String(sub_ipb)+"\",\"sub_ipc\":\""+String(sub_ipc)+"\",\"sub_ipd\":\""+String(sub_ipd)+"\",\"pdns_ipa\":\""+String(pdns_ipa)+"\",\"pdns_ipb\":\""+String(pdns_ipb)+"\",\"pdns_ipc\":\""+String(pdns_ipc)+"\",\"pdns_ipd\":\""+String(pdns_ipd)+"\",\"sdns_ipa\":\""+String(sdns_ipa)+"\",\"sdns_ipb\":\""+String(sdns_ipb)+"\",\"sdns_ipc\":\""+String(sdns_ipc)+"\",\"sdns_ipd\":\""+String(sdns_ipd)+"\",\"ssid\":\""+String(ssid)+"\"";
json += "}";
request->send(200, "application/json", json);
});
//File upload sever gedöns
// visiting this page will cause you to be logged out
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest * request) {
request->requestAuthentication();
request->send(401);
});
// presents a "you are now logged out webpage
server.on("/logged-out", HTTP_GET, [](AsyncWebServerRequest * request) {
String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
Serial.println(logmessage);
request->send_P(401, "text/html", logout_html, processor);
});
server.on("/upload", HTTP_GET, [](AsyncWebServerRequest * request) {
String logmessage = "Client:" + request->client()->remoteIP().toString() + + " " + request->url();
if (checkUserWebAuth(request)) {
logmessage += " Auth: Success";
Serial.println(logmessage);
request->send_P(200, "text/html", index_html, processor);
} else {
logmessage += " Auth: Failed";
Serial.println(logmessage);
return request->requestAuthentication();
}
});
server.on("/listfiles", HTTP_GET, [](AsyncWebServerRequest * request)
{
String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
if (checkUserWebAuth(request)) {
logmessage += " Auth: Success";
Serial.println(logmessage);
request->send(200, "text/plain", listFiles(true));
} else {
logmessage += " Auth: Failed";
Serial.println(logmessage);
return request->requestAuthentication();
}
});
server.on("/file", HTTP_GET, [](AsyncWebServerRequest * request) {
String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
if (checkUserWebAuth(request)) {
logmessage += " Auth: Success";
Serial.println(logmessage);
if (request->hasParam("name") && request->hasParam("action")) {
const char *fileName = request->getParam("name")->value().c_str();
const char *fileAction = request->getParam("action")->value().c_str();
logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url() + "?name=" + String(fileName) + "&action=" + String(fileAction);
File root = SPIFFS.open("/");
if (!SPIFFS.exists(String(fileName))) {
Serial.println(logmessage + " ERROR: file does not exist");
request->send(400, "text/plain", "ERROR: file does not exist");
} else {
Serial.println(logmessage + " file exists");
if (strcmp(fileAction, "download") == 0) {
logmessage += " downloaded";
request->send(SPIFFS, fileName, "application/octet-stream");
} else if (strcmp(fileAction, "delete") == 0) {
logmessage += " deleted";
SPIFFS.remove(fileName);
request->send(200, "text/plain", "Deleted File: " + String(fileName));
} else {
logmessage += " ERROR: invalid action param supplied";
request->send(400, "text/plain", "ERROR: invalid action param supplied");
}
Serial.println(logmessage);
}
} else {
request->send(400, "text/plain", "ERROR: name and action params required");
}
} else {
logmessage += " Auth: Failed";
Serial.println(logmessage);
return request->requestAuthentication();
}
});
//ARDUINO OTA ( IN LOOP SCHLIEFE HÄNDLE WIEDER AUSKOMMENTIEREN !!! )#####################################################################
// Port defaults to 3232
// ArduinoOTA.setPort(3232);
// Hostname defaults to esp3232-[MAC]
ArduinoOTA.setHostname("Pro3EM-TCP2RTU");
// No authentication by default
ArduinoOTA.setPassword("ABCDEF");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
//Webserver Starten
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
server.begin();
}
void loop() {
ArduinoOTA.handle();
//reconnect wifi
if (WiFi.status() != WL_CONNECTED && eth_connected != true){WiFi.begin(ssid.c_str(), password.c_str()); delay (5000); Serial.println("WIFI VERBINDUNG NEU AUFBAUEN");}// connect to the network
if (millis() - Modbus_lastMillis > modbus_interval) {
Modbus_lastMillis = millis();
//Serial.printf("sending request ");
//Serial.println("##################################################################");
Error err;
//text[token] = "hallo"; //Modbus_lastMillis
//Modbus Anfage (Beliebiger Token der im Handler zur Anfrage indifizieren hilft, Modbus Geräte Adresse, Register (Input register), Register Start Adresse, Anzahl an register die ausgelesen werden )
err = MB.addRequest(Modbus_lastMillis, 1, 0x04, 1002, 77);
//token = token + 1;
//if(token > 48){token = 0; memset(text, 0, sizeof(text));}
if (err != SUCCESS) {
ModbusError e(err);
Serial.printf("Error creating request: %02X - %s\n", (int)e, (const char *)e);
modbus_fehler_count = modbus_fehler_count + 1;
if(modbus_fehler_count > 5000){ ESP.restart(); }
}
}
if (millis() - push_lastMillis > push_interval) {
if(pushservice == 1 && pushservice_url != ""){ pushservicestart(); }
push_lastMillis = millis();
}
}
void pushservicestart(){
if(WiFi.status()== WL_CONNECTED || eth_connected == true){
HTTPClient http;
String json;
json += "{";
json += "\"antwort\":\"json_all\",";
json += "\"from\":\"Pro3EM_TCP2UDP\",\"L1_error\":\""+String(L1_error)+"\",\"L2_error\":\""+String(L2_error)+"\",\"L3_error\":\""+String(L3_error)+"\",\"N_error\":\""+String(N_error)+"\",\"S_error\":\""+String(S_error)+"\",\"N_A\":\""+String(N_A)+"\",\"N_A_error\":\""+String(N_A_error)+"\",\"N_O_error\":\""+String(N_O_error)+"\",\"Total_A\":\""+String(Total_A)+"\",\"Total_W\":\""+String(Total_W)+"\",\"Total_sch\":\""+String(Total_sch)+"\",\"L1_V\":\""+String(L1_V)+"\",\"L1_A\":\""+String(L1_A)+"\",\"L1_W\":\""+String(L1_W)+"\",\"L1_SCH\":\""+String(L1_SCH)+"\",\"L1_PF\":\""+String(L1_PF)+"\",\"L1_O_W_error\":\""+String(L1_O_W_error)+"\",\"L1_O_V_error\":\""+String(L1_O_V_error)+"\",\"L1_O_A_error\":\""+String(L1_O_A_error)+"\",\"L1_Hz\":\""+String(L1_Hz)+"\",\"L2_V\":\""+String(L2_V)+"\",\"L2_A\":\""+String(L2_A)+"\",\"L2_W\":\""+String(L2_W)+"\",\"L2_SCH\":\""+String(L2_SCH)+"\",\"L2_PF\":\""+String(L2_PF)+"\",\"L2_O_W_error\":\""+String(L2_O_W_error)+"\",\"L2_O_V_error\":\""+String(L2_O_V_error)+"\",\"L2_O_A_error\":\""+String(L2_O_A_error)+"\",\"L2_Hz\":\""+String(L2_Hz)+"\",\"L3_V\":\""+String(L3_V)+"\",\"L3_A\":\""+String(L3_A)+"\",\"L3_W\":\""+String(L3_W)+"\",\"L3_SCH\":\""+String(L3_SCH)+"\",\"L3_PF\":\""+String(L3_PF)+"\",\"L3_O_W_error\":\""+String(L3_O_W_error)+"\",\"L3_O_V_error\":\""+String(L3_O_V_error)+"\",\"L3_O_A_error\":\""+String(L3_O_A_error)+"\",\"L3_Hz\":\""+String(L3_Hz)+"\",\"wifi_signal\":\""+String(WiFi.RSSI())+"\"";
json += "}";
String serverPath = pushservice_url + "?json="+json;
http.begin(serverPath.c_str());
int httpResponseCode = http.GET();
http.end();
}
}
//Soyosource Daten aufbereiten
void setSoyoPowerData(int power){
soyo_power_data[0] = 0x24;
soyo_power_data[1] = 0x56;
soyo_power_data[2] = 0x00;
soyo_power_data[3] = 0x21;
soyo_power_data[4] = power >> 0x08;
soyo_power_data[5] = power & 0xFF;
soyo_power_data[6] = 0x80;
soyo_power_data[7] = calc_checksumme(soyo_power_data[1], soyo_power_data[2], soyo_power_data[3], soyo_power_data[4], soyo_power_data[5], soyo_power_data[6]);
}
int calc_checksumme(int b1, int b2, int b3, int b4, int b5, int b6 ){
int calc = (0xFF - b1 - b2 - b3 - b4 - b5 -b6) % 256;
return calc & 0xFF;
}
//Modbus Daten Handler
// plus a user-supplied token to identify the causing request
void handleData(ModbusMessage response, uint32_t token)
{
//uint16_t aa = response.get(uint16_t index, float& value);
//Serial.print(text[token]); Serial.println(token);
if(debuglevel == 1 || debuglevel == 3){Serial.printf("Response: serverID=%d, FC=%d, Token=%08X, length=%d:\n", response.getServerID(), response.getFunctionCode(), token, response.size());}
int antwortzeit = millis() - token;
if(debuglevel == 1 || debuglevel == 3){
Serial.print("Modbus antwortzeit: ");
Serial.println(antwortzeit);
}
/* //Hex ausgabe
for (auto& byte : response) {
Serial.printf("%02X ", byte);
}
*/
uint16_t offs = 3;
float floatValue = 0.0;
uint16_t boolValue;
offs = response.get(offs, boolValue); //31002
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase A meter error ( Bool ): "); Serial.println(String(boolValue)); }
L1_error = boolValue;
offs = response.get(offs, boolValue); //31003
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase B meter error ( Bool ): "); Serial.println(String(boolValue));}
L2_error = boolValue;
offs = response.get(offs, boolValue); //31004
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase C meter error ( Bool ): "); Serial.println(String(boolValue));}
L3_error = boolValue;
offs = response.get(offs, boolValue); //31005
if(debuglevel == 2 || debuglevel == 3){Serial.print("N meter error ( Bool ): "); Serial.println(String(boolValue));}
N_error = boolValue;
offs = response.get(offs, boolValue); //31006
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase sequence meter error(Bool):"); Serial.println(String(boolValue)); }
S_error = boolValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31007-31008
if(debuglevel == 2 || debuglevel == 3){Serial.print("Neutral current, A ( Float ): "); Serial.println(String(floatValue));}
N_A = floatValue;
offs = response.get(offs, boolValue); //31009
if(debuglevel == 2 || debuglevel == 3){Serial.print("Neutral current mismatch ( Bool ):"); Serial.println(String(boolValue));}
N_A_error = boolValue;
offs = response.get(offs, boolValue); //31010
if(debuglevel == 2 || debuglevel == 3){Serial.print("Neutral overcurrent error ( Bool ):"); Serial.println(String(boolValue)); }
N_O_error = boolValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31011 - 12
if(debuglevel == 1 || debuglevel == 3){Serial.print("Total current, A ( Float ): "); Serial.println(String(floatValue));}
Total_A = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31013 -14
if(debuglevel == 1 || debuglevel == 3){Serial.print("Total active power, W ( Float ): "); Serial.println(String(floatValue));}
Total_W = floatValue;
if (millis() - Soyo_previousMillis >= Soyo_interval){
power_total = floatValue;
int value_power=(int)power_total;
value_power = value_power / anz_soyo;
value_power = value_power - einspeisung_reduzieren;
if(value_power > max_einspeisung){ value_power = max_einspeisung;}
if(value_power < 0){value_power = 0;}
setSoyoPowerData(value_power);
//send data to RS485
if(antwortzeit <= modbus_timeout){
if(debuglevel == 1 || debuglevel == 3){Serial.print("Sende ");Serial.print(value_power);Serial.println("W an Soyo");}
if(debuglevel == 1 || debuglevel == 3){Serial.println("############################################");}
for(int i=0; i<8; i++){
RS485Serial.write(soyo_power_data[i]);
if(debuglevel == 1 || debuglevel == 3){Serial.print(soyo_power_data[i], HEX);}
if(debuglevel == 1 || debuglevel == 3){Serial.print(" ");}
}
if(debuglevel == 1 || debuglevel == 3){Serial.println("");Serial.println("############################################");}
}else{Serial.print("Modbus Timeout !!! Die Modbusantwort hat länger als ");Serial.print(modbus_timeout);Serial.println("ms gedaudert.");}
Soyo_previousMillis = millis();
value_power = 0;
}
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31015 -16
if(debuglevel == 1 || debuglevel == 3){Serial.print("Total apparent power, VA ( Float ):"); Serial.println(String(floatValue));}
Total_sch = floatValue;
//3 reserve registers
offs = response.get(offs, boolValue); //31017
if(debuglevel == 1 || debuglevel == 3){Serial.print("Reserve Register: (bool) "); Serial.println(String(boolValue)); }
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31018 - 19
if(debuglevel == 1 || debuglevel == 3){Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));}
//Phase A
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31020 - 21
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase A voltage, V ( Float ): "); Serial.println(String(floatValue));}
L1_V = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31022 - 23
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase A current, A ( Float ): "); Serial.println(String(floatValue));}
L1_A = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31024 - 25
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase A active power, W ( Float ): "); Serial.println(String(floatValue));}
L1_W = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31026 - 27
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase A apparent power ( Float ): "); Serial.println(String(floatValue));}
L1_SCH = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31028 - 29
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase A power factor ( Float ): "); Serial.println(String(floatValue));}
L1_PF = floatValue;
offs = response.get(offs, boolValue); //31030
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase A overpower error: (bool) "); Serial.println(String(boolValue)); }
L1_O_W_error = boolValue;
offs = response.get(offs, boolValue); //31031
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase A overvoltage error (bool) "); Serial.println(String(boolValue)); }
L1_O_V_error = boolValue;
offs = response.get(offs, boolValue); //31032
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase A overcurrent error: (bool) "); Serial.println(String(boolValue)); }
L1_O_A_error = boolValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31033 - 34
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase A frequency, Hz ( Float ): "); Serial.println(String(floatValue));}
L1_Hz = floatValue;
//5 reserve registers
offs = response.get(offs, boolValue); //31035
//Serial.print("Reserve Register: (bool) "); Serial.println(String(boolValue));
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31036 - 37
//Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31038 - 39
//Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));
//Phase B
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31040 - 41
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase B voltage, V ( Float ): "); Serial.println(String(floatValue));}
L2_V = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31042 - 43
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase B current, A ( Float ): "); Serial.println(String(floatValue));}
L2_A = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31044 - 45
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase B active power, W ( Float ): "); Serial.println(String(floatValue));}
L2_W = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31046 - 47
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase B apparent power ( Float ): "); Serial.println(String(floatValue));}
L2_SCH = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31048 - 49
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase B power factor ( Float ): "); Serial.println(String(floatValue));}
L2_PF = floatValue;
offs = response.get(offs, boolValue); //31050
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase B overpower error: (bool) "); Serial.println(String(boolValue)); }
L2_O_W_error = boolValue;
offs = response.get(offs, boolValue); //31051
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase B overvoltage error (bool) "); Serial.println(String(boolValue)); }
L2_O_V_error = boolValue;
offs = response.get(offs, boolValue); //31052
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase B overcurrent error: (bool) "); Serial.println(String(boolValue)); }
L2_O_A_error = boolValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31053 - 54
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase B frequency, Hz ( Float ): "); Serial.println(String(floatValue));}
L2_Hz = floatValue;
//5 reserve registers
offs = response.get(offs, boolValue); //31055
//Serial.print("Reserve Register: (bool) "); Serial.println(String(boolValue));
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31056 - 57
//Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31058 - 59
//Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));
//Phase C
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31060 - 61
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase C voltage, V ( Float ): "); Serial.println(String(floatValue));}
L3_V = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31062 - 63
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase C current, A ( Float ): "); Serial.println(String(floatValue));}
L3_A = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31064 - 65
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase C active power, W ( Float ): "); Serial.println(String(floatValue));}
L3_W = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31066 - 67
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase C apparent power ( Float ): "); Serial.println(String(floatValue));}
L3_SCH = floatValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31068 - 69
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase C power factor ( Float ): "); Serial.println(String(floatValue));}
L3_PF = floatValue;
offs = response.get(offs, boolValue); //31070
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase C overpower error: (bool) "); Serial.println(String(boolValue)); }
L3_O_W_error = boolValue;
offs = response.get(offs, boolValue); //31071
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase C overvoltage error (bool) "); Serial.println(String(boolValue)); }
L3_O_V_error = boolValue;
offs = response.get(offs, boolValue); //31072
if(debuglevel == 2 || debuglevel == 3){Serial.print("Phase C overcurrent error: (bool) "); Serial.println(String(boolValue)); }
L3_O_A_error = boolValue;
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31073 - 74
if(debuglevel == 1 || debuglevel == 3){Serial.print("Phase C frequency, Hz ( Float ): "); Serial.println(String(floatValue));}
L3_Hz = floatValue;
//5 reserve registers
offs = response.get(offs, boolValue); //31055
//Serial.print("Reserve Register: (bool) "); Serial.println(String(boolValue));
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31056 - 57
//Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));
offs = response.get(offs, floatValue, SWAP_REGISTERS); //31058 - 59
//Serial.print("Reserve Register: ( Float ): "); Serial.println(String(floatValue));
}
//Error Handler für Modbus (Nicht weiter ausgeführt für das Projekt)
// Define an onError handler function to receive error responses
// Arguments are the error code returned and a user-supplied token to identify the causing request
void handleError(Error error, uint32_t token)
{
// ModbusError wraps the error code and provides a readable error message for it
ModbusError me(error);
Serial.printf("Error response: %02X - %s token: %d\n", (int)me, (const char *)me, token);
}
//Filesystem html upload
void notFound(AsyncWebServerRequest *request) {
String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
Serial.println(logmessage);
request->send(404, "text/plain", "Not found");
}
// list all of the files, if ishtml=true, return html rather than simple text
String listFiles(bool ishtml) {
String returnText = "";
Serial.println("Listing files stored on SPIFFS");
File root = SPIFFS.open("/");
File foundfile = root.openNextFile();
if (ishtml) {
returnText += "<table><tr><th align='left'>Name</th><th align='left'>Size</th><th></th><th></th></tr>";
}
while (foundfile) {
if (ishtml) {
returnText += "<tr align='left'><td>" + String(foundfile.name()) + "</td><td>" + humanReadableSize(foundfile.size()) + "</td>";
returnText += "<td><button onclick=\"downloadDeleteButton(\'" + String(foundfile.name()) + "\', \'download\')\">Download</button>";
returnText += "<td><button onclick=\"downloadDeleteButton(\'" + String(foundfile.name()) + "\', \'delete\')\">Delete</button></tr>";
} else {
returnText += "File: " + String(foundfile.name()) + " Size: " + humanReadableSize(foundfile.size()) + "\n";
}
foundfile = root.openNextFile();
}
if (ishtml) {
returnText += "</table>";
}
root.close();
foundfile.close();
return returnText;
}
// Make size of files human readable
// source: https://github.com/CelliesProjects/minimalUploadAuthESP32
String humanReadableSize(const size_t bytes) {
if (bytes < 1024) return String(bytes) + " B";
else if (bytes < (1024 * 1024)) return String(bytes / 1024.0) + " KB";
else if (bytes < (1024 * 1024 * 1024)) return String(bytes / 1024.0 / 1024.0) + " MB";
else return String(bytes / 1024.0 / 1024.0 / 1024.0) + " GB";
}
String processor(const String& var) {
if (var == "FIRMWARE") {
return FIRMWARE_VERSION;
}
if (var == "FREESPIFFS") {
return humanReadableSize((SPIFFS.totalBytes() - SPIFFS.usedBytes()));
}
if (var == "USEDSPIFFS") {
return humanReadableSize(SPIFFS.usedBytes());
}
if (var == "TOTALSPIFFS") {
return humanReadableSize(SPIFFS.totalBytes());
}
}
// used by server.on functions to discern whether a user has the correct httpapitoken OR is authenticated by username and password
bool checkUserWebAuth(AsyncWebServerRequest * request) {
bool isAuthenticated = false;
if (request->authenticate(config.httpuser.c_str(), config.httppassword.c_str())) {
Serial.println("is authenticated via username and password");
isAuthenticated = true;
}
return isAuthenticated;
}
// handles uploads to the filserver
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
// make sure authenticated before allowing upload
if (checkUserWebAuth(request)) {
String logmessage = "UploadClient:" + request->client()->remoteIP().toString() + " " + request->url();
Serial.println(logmessage);
if (!index) {
logmessage = "Upload Start: " + String(filename);
// open the file on first call and store the file handle in the request object
request->_tempFile = SPIFFS.open("/" + filename, "w");
Serial.println(logmessage);
}
if (len) {
// stream the incoming chunk to the opened file
request->_tempFile.write(data, len);
logmessage = "Writing file: " + String(filename) + " index=" + String(index) + " len=" + String(len);
Serial.println(logmessage);
}
if (final) {
logmessage = "Upload Complete: " + String(filename) + ",size: " + String(index + len);
// close the file handle as the upload is now done
request->_tempFile.close();
Serial.println(logmessage);
request->redirect("/");
}
} else {
Serial.println("Auth: Failed");
return request->requestAuthentication();
}
}
webpages.h ( in der Arduino IDE auf + drücken und als webpages.h hinzufügen )
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
</head>
<body>
<p>Main page</p>
<p>Firmware: %FIRMWARE%</p>
<p>Free Storage: <span id="freespiffs">%FREESPIFFS%</span> | Used Storage: <span id="usedspiffs">%USEDSPIFFS%</span> | Total Storage: <span id="totalspiffs">%TOTALSPIFFS%</span></p>
<p>
<button onclick="logoutButton()">Logout</button>
<button onclick="rebootButton()">Reboot</button>
<button onclick="listFilesButton()">List Files</button>
<button onclick="showUploadButtonFancy()">Upload File</button>
</p>
<p id="status"></p>
<p id="detailsheader"></p>
<p id="details"></p>
<script>
function logoutButton() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/logout", true);
xhr.send();
setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
}
function rebootButton() {
document.getElementById("statusdetails").innerHTML = "Invoking Reboot ...";
var xhr = new XMLHttpRequest();
xhr.open("GET", "/reboot", true);
xhr.send();
window.open("/reboot","_self");
}
function listFilesButton() {
xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET", "/listfiles", false);
xmlhttp.send();
document.getElementById("detailsheader").innerHTML = "<h3>Files<h3>";
document.getElementById("details").innerHTML = xmlhttp.responseText;
}
function downloadDeleteButton(filename, action) {
var urltocall = "/file?name=/" + filename + "&action=" + action;
xmlhttp=new XMLHttpRequest();
if (action == "delete") {
xmlhttp.open("GET", urltocall, false);
xmlhttp.send();
document.getElementById("status").innerHTML = xmlhttp.responseText;
xmlhttp.open("GET", "/listfiles", false);
xmlhttp.send();
document.getElementById("details").innerHTML = xmlhttp.responseText;
}
if (action == "download") {
document.getElementById("status").innerHTML = "";
window.open(filename,"_blank");
}
}
function showUploadButtonFancy() {
document.getElementById("detailsheader").innerHTML = "<h3>Upload File<h3>"
document.getElementById("status").innerHTML = "";
var uploadform = "<form method = \"POST\" action = \"/upload/\" enctype=\"multipart/form-data\"><input type=\"file\" name=\"data\"/><input type=\"submit\" name=\"upload\" value=\"Upload\" title = \"Upload File\"></form>"
document.getElementById("details").innerHTML = uploadform;
var uploadform =
"<form id=\"upload_form\" enctype=\"multipart/form-data\" method=\"post\">" +
"<input type=\"file\" name=\"file1\" id=\"file1\" onchange=\"uploadFile()\"><br>" +
"<progress id=\"progressBar\" value=\"0\" max=\"100\" style=\"width:300px;\"></progress>" +
"<h3 id=\"status\"></h3>" +
"<p id=\"loaded_n_total\"></p>" +
"</form>";
document.getElementById("details").innerHTML = uploadform;
}
function _(el) {
return document.getElementById(el);
}
function uploadFile() {
var file = _("file1").files[0];
// alert(file.name+" | "+file.size+" | "+file.type);
var formdata = new FormData();
formdata.append("file1", file);
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", progressHandler, false);
ajax.addEventListener("load", completeHandler, false); // doesnt appear to ever get called even upon success
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", "/");
ajax.send(formdata);
}
function progressHandler(event) {
//_("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total; // event.total doesnt show accurate total file size
_("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes";
var percent = (event.loaded / event.total) * 100;
_("progressBar").value = Math.round(percent);
_("status").innerHTML = Math.round(percent) + "% uploaded... please wait";
if (percent >= 100) {
_("status").innerHTML = "Please wait, writing file to filesystem";
}
}
function completeHandler(event) {
_("status").innerHTML = "Upload Complete";
_("progressBar").value = 0;
xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET", "/listfiles", false);
xmlhttp.send();
document.getElementById("status").innerHTML = "File Uploaded";
document.getElementById("detailsheader").innerHTML = "<h3>Files<h3>";
document.getElementById("details").innerHTML = xmlhttp.responseText;
}
function errorHandler(event) {
_("status").innerHTML = "Upload Failed";
}
function abortHandler(event) {
_("status").innerHTML = "inUpload Aborted";
}
</script>
</body>
</html>
)rawliteral";
const char logout_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
</head>
<body>
<p><a href="/upload/">Log Back In</a></p>
</body>
</html>
)rawliteral";
// reboot.html base upon https://gist.github.com/Joel-James/62d98e8cb3a1b6b05102
const char reboot_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<h3>
Rebooting, returning to main page in <span id="countdown">30</span> seconds
</h3>
<script type="text/javascript">
var seconds = 20;
function countdown() {
seconds = seconds - 1;
if (seconds < 0) {
window.location = "/upload";
} else {
document.getElementById("countdown").innerHTML = seconds;
window.setTimeout("countdown()", 1000);
}
}
countdown();
</script>
</body>
</html>
)rawliteral";