versteh ich ... aber habe den gesamten Code in 20 Tabs in der IDE.
Kann dir gerne mal den Code vom Haupttab geben .. die anderen 19 Tabs lass ich mal ..
in 2.0.17 lief das alles problemlos....
#include "espdef.h" // Definition der ESPs
// -------------------------- weitere Netzwerkdefinitionen --------------------
const IPAddress gateway(192,168,178,1); // Abdresse des Routers
const IPAddress subnet(255,255,255,0); // und der IP SubnetMaske
const IPAddress dns(192,168,178,1); // Router = DNS
const char* otahost = myhostname; // OTA hostname
const String sIPrumpf = "192.168.178"; // wird für die links auf der Haupt_HTML verwendet
const String startIP = ".20";
// ---------------------- die Schnittstellen -----------------
String sdatatransfer; // string für SendData - Messwerte für andere ESPs
String whatsapptext; // text für Whatsapp Messages
String seingabe; // ### Eingabestring vom seriellen Monitor Serial.read(...)
String text; // allgemeiner String für Zwischenspeicher von text bei zB Meldungen oder display.print
int16_t testzaehler; // zählt die Durchläufe im 13 sec Loop für die Laufzeitmeldungen alle 24h
bool starttestmeldung; // stellt sicher, dass die erste testmeldung nur einmal abgesetzt wird
bool testoutputzyklus; // bei jedem 2. Durchlauf L4 on, zum ein/aus Schalten der Ausgaenge
byte testoutputzaehler = 2; // ### für die Aktivierung der Testoutputroutine 4,5,6 bei 2 x ">" Touch
bool testoutputon; // ### generell Testroutine ON/OFF - für Output 4,5,6, on oder off
// alle fix ------- Wifi Connect und Satus ------------------
byte wificonnectversuchemax = 100; // x * 100msec ; wird in der Wifiroutine für anzahl verbindungsversuche verwendet und danach wird abgebrochen
const byte intervallwificonnectversuch = 300; // in sec - wenn Wifibeim Sart nicht verbunden werden konnte, wird alle x sec versucht die Verbindung herzustellen
unsigned long letzterwificonnectversuch; // speichert zeit des letzten Versuchs
bool wifistatusconnect; // bei false - druckt Wifi Fehler auf das Display, wenn die Verbindung nicht gelingt
bool wifinotconnectedgemeldet; // bei Fehler wird der Text nur einmal in der Meldeschnittstelle gemeldet
// ----------------------- Get Data -----------------------
const String sdatenurl = "/data"; // 192.168.178.02x/data ist die url für alle ESP-data Schnittstellen - defintion in der Exceltabelle zum Projekt
const byte maximalfeldergetdata = 45; // zur Definition des espx.idx[] arrays in der getData Routine - maximum aller getData Schnittstellen
const byte getdatafehlertoleranz = 4; // die maximale Anzahl an Fehlversuchen, bevor der espx.swerte String mit -x- überschrieben wird
uint16_t getdatafehlergesamt; // Summe der getdatafehler aus espa und espb
unsigned long getdatastartzeit; // zur Messung der durchschnittlichen Datenempfangszeit in msec
const String sprueftext = "z2601"; // erste Zelle im Send und getData String - zur Prüfung ob der Empfang OK ist
const String swertedatenfehler = "-98"; // wenn der getdata Empfang gestört ist, wird -98 in die swerte[] geschrieben - war früher mal "-x-"
class cGetDataInterface { // die Klasse für die GetDataroutine
public:
cGetDataInterface (String sif, char* ip, byte anzahl) { // constructor definiert das Variablenset für den Start
sinterfacename = sif;
ipsource = ip;
anzahlwerte = anzahl;
};
String sinterfacename; // Name der Schnittstelle
char* ipsource; // const char* oder char[16] funktioniert nicht - es muss ein char* sein
byte anzahlwerte; // Anzahl Werte der Schnittstelle
int16_t getdatazeit; // Zeit in ms bis die Daten empfangen wurden
unsigned long getdatastartzeit; // Startzeit für Zeitmessung getdatazeit
int16_t getdatazeitzulange; // zählt wie oft die getdatazeit bereits zu lange war
int16_t getdatafehler; // zählt die getdatafehler der schnittstelle - wird auf Null gesetz beim nächsten erfolgreichen Datanaustausch
int16_t getdatafehlergemeldet; // verhindert Mehrfachmeldungen bei getdatafehler
String swerte[maximalfeldergetdata]; // liefert die Daten des Empfangsstring in die einzelnen Datenfelder (swerte[x])
};
cGetDataInterface espa (sinterface_a, ip_a, anzahlwerte_a); // Definition der 2 Objekte der Klasse
cGetDataInterface espb (sinterface_b, ip_b, anzahlwerte_b);
byte espbinterfacezaehler; // zählt die Durchläufe für getData bei zB KHz
// -------------------die eigenen Definitionsdateien einbinden -----------------------
#include "htmp.h" // Definition der Parameter und Variablen der DS18B20 Sensoren
#include "htft.h" // Definition der TFT Parameter und Variablen
#include "html.h" // Definition der Strings für die HTML Seite, die fix bleiben
// ------------------ Stoerungsmeldungen -----------------
// max 30-35 Zeichen lang
class cstoerungsmeldung { // Jede Datazeile ist dann ein einzelnes Objekt der Klasse
public:
int16_t idx; // index-Nr der Meldung
String sdatum; // Datum des Meldungseingangs
String sdatumkurz; // Kurzversion des Datums (nur Tag und Monat)
String suhrzeit; // uhrzeit des Meldungseinganges
char farbe; // Farbcode für die Meldung
String smeldung; // der eigentliche Meldungstext
};
// midxmax ist entscheidend für gutes Funktionieren. in Summe sollte Freeheap > 50kB bleiben für WLAN , OTA use
const int16_t midxmax = 130; // Größe des Meldearrays = Zeilen Meldungen (bei 300 bleibt Platz für weitere Messwerte - ca. 30) -
const int16_t midxmax2 = midxmax + 3; // 2 Felder Reserve im Meldearray
cstoerungsmeldung meldung[midxmax2]; // Definition des Arrays (jedes einzelne Feld des Arrays ist eine Meldung bzw ein Objekt der Klasse)
int16_t midx; // = meldeindex , zeiger auf das aktuelle Feld des meldeindex = sammlung aller meldungen
bool meldebedienung; // = 0 wenn die MEldungen automatisch laufen, = 1 wenn in den MEldungen geblättert wird
int16_t letzteangezeigtemeldung; // speichert die Meldungen seit dem letzten Aufruf - zur Anzeige in der Topzeile
int16_t angezeigtemeldungenueberlauf; // zählt die Durchgänge durch das Array
int16_t versaeumtemeldungen; // zählt ide Meldungen , seite letzten Besuch der Meldeseite am TFT oder HTML
int16_t anzeigehtmlmeldunganfang; // Zähler zur Generierung der HML Seite - 1. Meldung auf der Seite
int16_t anzeigehtmlmeldungende; // Zähler zur Generierung der HTML Seite - letzte Meldung der Seite
#if TFTVorhanden == 1 // ******************************************************
// ---------------------- für die Transformation / Querverweise Messwerte standardisiert ------------------
class cdataesp {
public:
String feuchte;
String temp;
String licht;
String dbm;
String laufzeit;
String ESPtemp;
String agetdata; // getdata Zeit in ms der A-Schnittstelle
String bgetdata; // ....der B-Schnittstelle
String versaeumtemeldungen;
};
// Objekte der Klasse cdataesp:
cdataesp pKHz; // p..für "print" - wird gebraucht um die TFT Anzeige und HTML Ausgabe einheitlch zu halten
cdataesp pEG;
cdataesp pDG;
cdataesp pKWr;
cdataesp pKGr;
String pAussenfeuchte; // die Werte die nicht auf jedem ESP vertreten sind, werden als eigene pStrings dargestellt
String pAussentemp;
String pWWoben;
String pWWunten;
String pVL;
String pRL;
String pFernwaerme;
String pVLsolar;
String pRLsolar;
String pDachsolar;
String pSonne;
String pKlima;
String pWasser;
// ------------- Werte für die Diagramme -------------------
class cWerteDiagramm {
public:
cWerteDiagramm (String gruppe, String name, String einheit, int16_t anfang, int16_t ende, uint16_t f) {
sanzeigegruppe = gruppe;
sanzeigename = name;
seinheit = einheit;
anzeigebereichanfang = anfang;
anzeigebereichende = ende;
farbe = f;
};
String sanzeigegruppe;
String sanzeigename;
String seinheit; //
int16_t anzeigewert[idxdmax + 3];
int16_t anzeigebereichanfang; // y-achse Skala im Daigramm - Start
int16_t anzeigebereichende; // y-achse Skala im Diagramm - Ende - Bereich muss gut durch 4 teilbar sein
uint16_t farbe; // muss uint16_t sein : https://rgbcolorpicker.com/565
};
// max 26 DiagrammWerte bei 250 Meldungen max. - Free Heap sollte ca bei <50kB sein (oder 300 Meldungen und 15 Diagrammwerte)
// brauchbare Farben: w white, y yellow, g green, o orange, :: blue und red sind zu dunkel auf schwarzem Hintergrund
// der erste Wert einer Diagrammseite definiert die linke Skala, der letzte Wert einer Seite definiert die rechte Skala
cWerteDiagramm dwert[] = {
{"Aussen: ", "Temp ", "C", -15, 45, orange}, //[0] = 1
{"", "Feuchte", "%", 20, 100, tuerkis},
{"EG: ", "Temp ", "C", 10, 30, orange},
{"", "Feuchte", "%", 20, 100, tuerkis},
{"WWTemp:", "WWO", "C", 30, 70, magenta},
{"", "WWU ", "C", 30, 70, gruen},
{"", "VLSolar", "C", 20, 120, gelb},
{"", "DachSolar", "C", 20, 120, rot},
{"Heiz.Temp.: ", "VL", "C", 20, 80, orange},
{"", "RL", "C", 20, 80, tuerkis},
{"", "Fernwaerme", "C", 20, 80, gelb}, //[10] = 11.Datensatz
{"KG Feuchte: ", "Hz", "%", 20, 100, orange},
{"", "Wr", "%", 20, 100, gelb},
{"", "Gr ", "%", 20, 100, gruen},
{"KG Temp.:", "Hz", "C", 10, 30, orange},
{"", "Wr", "C", 10, 30, gelb},
{"", "Gr", "C", 10, 30, gruen},
{"Licht: ", "EG", "%", 0, 100, tuerkis},
{"", "KHz", "%", 0, 100, orange},
{"", "KWr", "%", 0, 100, gelb},
{"", "KGr", "%", 0, 100, gruen}, //[20]
{"", "DG", "%", 0, 100, magenta},
{"WiFi: ", "EG", "dbm", -90, -10, tuerkis},
{"", "KHz", "dbm", -90, -10, orange},
{"", "KWr", "dbm", -90, -10, gelb},
{"", "KGr", "dbm", -90, -10, gruen},
{"", "DG", "dbm", -90, -10, magenta}, // bei 250 Meldungen sind max. 26 Diagrammwerte möglich beim 4" TFT
{"GetData: ", "EG", "ms", 0, 800, tuerkis},
{"", "KHz", "ms", 0, 800, orange},
{"", "KWr", "ms", 0, 800, gelb},
{"", "KGr", "ms", 0, 800, gruen},
{"", "DG", "ms", 0, 800, magenta} // [31]
};
String duhrzeit[idxdmax + 3]; // speichert uhrzeit und datum zu den gespeicherten Messwerten für das Diagramm
//byte anzeigeintervalldiagramme; // bei Loop5 kann die freuquenz der Anzeigenudates gesteuert werden ; im Normalfall auf 1;
int16_t diagrammseite; // für das Blättern durch die Seiten
int16_t diagrammseitemax; // die maximale Anzahl der Diagrammseiten
#endif // ***************************************
// ---------------- Freigaben und Masterupdate ----------------
const int16_t intervallmasterupdate = 300; // in sec - update der schaltzustände vom Master (ESS KHz an den ESP EG)
unsigned long letztermasterupdate; // Zeit in mills , wann der letzte Masterupdate durchgeführt wurde
unsigned long letztesmasterupdatenachlaufzeit;
bool masterupdate; // wird true, wenn masterupdate läuft
bool masterupdatealt; // speichert den vorherigen Status zum erkennen der fallenden Flanke
byte sendDatazaehler; // zählt die Durchläufe für sendData (Zeitverzögerung im L2)
bool bediensperregetdata; // bei GetData-Empfang werden die anderen Bedienkanäle gesperrt
bool freigabeweb; // Erteilung der Freigabe per HTML (Sperre des Touchkanals)
bool freigabegesamt; // Summe aller Freigaben
bool freigabegesamtalt; // speichert den vorherigen Freigabestatus - zur Erkennung der fallenden Flanke für NAchlaufzeit Freigabe
unsigned long letztefreigabegesamt; // Zeit seit letzter Freigabegesamt für die Nahclaufzeit Freigabe
const int16_t nachlaufzeitfreigabegesamt = 6000; // nachlaufzeit in msec für die Freigabe - damit kann der Empfänger die Steuerbefehle noch in seinem nächsten GetDataDurchlauf übernehmen
unsigned long letztefreigabeweb;
bool triggerfreigabegesamt; // zum Auslösen der freigabegesamt, wenn ein Ausgang geschalten wird
bool anzeigetft;
const byte laufzeit_resetesp = 10; // in sec - bis zum Löschen der ResetESP-Freigabe per HTML
bool resetesp; // für den ESP Reset per HTML
unsigned long letztefreigabe_resetesp; // Zeit seit ResetESP-Anwahl - Start des Zeitfenster für Resetfreigabe
// ----------------------- libraries ---------------------------
#include <Preferences.h> // zumschreiben und lesen im Flash Memory
Preferences preferences; // Namespace preferences .. bezeichnung frei vergebbar
#include <UrlEncode.h>
//#include <WifiClient.h> // wird für whats app messages gebraucht - relativ einfach - infos von randomnerdtutorials.com
#include <HTTPClient.h> // wird gebraucht zum empfangen der Daten von einem Webserver - auch für whats app
#include <AsyncTCP.h> // async Webserver
#include <ESPAsyncWebServer.h> // async Webserver
AsyncWebServer server(80);
#include <ArduinoOTA.h> // OTA - Over the Air Update: Kennwort in IOS PAsswortspeicher
// ======================== Ein- und Ausgänge ===============================
// ----------------------- DHT - Temp und Feuchte -----------------
#include "Adafruit_Sensor.h" // für DHT
#include "DHT.h" // für DHT
//#include "DHTesp.h" // ### Test mit einer anderen DHT-Library - DHT sensor library for ESPx von beegee_tokyo - ging auch , allerding sdie Sensoren von Temu wollten nicht mit der 2. Library
#define HEIZPUMPE 33 // Relais 1, geht nach ESP_EG und wird dort geschalten .. pin nur vorläufig
#define WWPUMPE 17 // Relais 2
#define MISCHER_AUF 16 // Relais 3
#define MISCHER_ZU 2 // Relais 4
#define EPATRONE 32 // Relais 5
#define FREIGABELED 13 // GPIO 1 und 3 geht nicht - der serielle Monitor geht dann nicht;
#define GETDATALED 12 // blaue LED , wenn Daten empfangen, oder bei ESP ohne Empfang gesendet werden
#define KHZLUEFTER_EGBEEPER 21 // EG Beeper , KHz Lüfteransteuerung - Output 7
#define DHTPIN 25 // ist ein Output - reine Input GPIOs gehen nicht, wie zB 34,35,36,39
#define LICHTSENSOR 39 // analog input 0-4095 (12 bit ADC)
#define WASSERSENSOR 36 // analog Input 0-4095
#define SONNENLICHTSENSOR 34 // analog Input // ist im KHz auch Inputreserve
#define DACHSOLARSENSOR 35 // analog Input // wird nur im KHz vernwendet
#if TFTVorhanden == 1 //******************** // nur am KHz und EG - am DG hängt da der DHT AF
#define SOLARPUMPE 26 // Relais 6
#endif // ********************
#define DHTTYPE DHT22 // DHT11 oder DHT22
DHT dht = DHT(DHTPIN, DHTTYPE); // dht (Kleinbuchstaben) ist die veränderbare Bezeichnung - Raumsensor Temp und Feuchte
//DHTesp dht; // ### Test mit einer anderen DHT-Library
#if ESP_DEVICE == 22 // ***********************************************
#define DHTPIN_AF 26 // 2. DHT für AF
DHT dhtAF = DHT(DHTPIN_AF, DHTTYPE);
#endif // *************************************************
// -------------- PWM Channels ---------------------------------
const byte pwmchannelfreigabeled = 0; // PWM kanal 0 , es gibt 16 Kanäle PWM= Pulsweitenmodulation
const byte pwmchannelgetdataled = 1; // PWM Kanal- es gibt 16 Kanäle, 0-15
const int16_t pwmchannelfrequency = 100; // Frequenz des PWM Kanals in Hz
const byte pwmchannelresolution = 8; // Resolution 8 bit => 0-255
int16_t freigabeledhelligkeit; // speichert die Helligkeit der FreigabeLED 0-255
int16_t getdataledhelligkeit; // speichert die Helligkeit der GetDataLED 0-255
const byte pwmchannelbeeper = 2; // ein weiterer PWM Channel - der ist für den Beeper
const int16_t pwmchannelfrequencybeeper = 200; // 200 Hz gibt ertraeglichen Warnton
unsigned long letztesschnellesblinkenfreigabeled; // start/Stop Zeit in mills for das schnelle Blinken der Freigabeled
const byte pwmchannelkhzluefter = 3; // PWM Kanal 3
float khzluefterspeed; // Geschwindigkeit Luefter Khz
byte luefterzaehler; // zaehlt die durchläufe durch L3, alle x mal wird der lüfter upgedatet
const float korrfakttempdht = 0.22; // Korrekturfaktor für Raumtemperaturmessung bei verbauten ESPs
#if TFTVorhanden == 1 // *******************************************
// ========================== Reglereinstlellungen ===================================
const byte solarobergrenze = 61; // max. Speichertemp. bei Betrieb Solarkollektoren
const byte solardiff = 4; // Schaltdifferenz (Schalthysterese) Solar
const byte wwobergrenze = 43; // max. Speichertemp bei Heizung mit Elektro oder WArmwasserpumpe
const byte wwdiff = 5; // Schaltdifferenz (Schalthysterese) Warmwasser
const byte korrwertWWO = 4; // Korrekturwert für Warmwasserfühleroben (### weil er nicht zur Gänze in den Boilerslot passt)
// =====================================================================================
// --------------------- Variablen für Messwerte - hauptsächlich für Regler ---------------------
float WWU; // Temp Warmwasserspeicher oben ; für die Übernahme der Messwerte temp[1] - temp[6] in sprechende Namen bei ESP KHz
float WWO; // Temp Warmwasserspeicher unten
float VL; // Temp Vorlauf Heizung
float RL; // Temp Rücklauf Heizung
float FW; // Temp Fernwärme
float VLS; // Temp Vorlauf Solar
float RLS; // Temp Rücklauf Solar
float Dachsolar; // Temp Fühler in Solarkollektor
float Aussentemp; // Aussentemperatur
float Sonnenstrahlung; // % Sonnenstrahlung am Dach
// ----------------------- Variablen für Schlatzustände und Meldetexte ------------------------------
String sFreigabe; // speichert die Freigabe JA/NEIN - die Schaltzustände der anderen Outputs sind direkt im Output (digitaWririte / digitalRead) bzw in den sanzeige0buttontext[1][x] Stringd
String smasterupdate; // geht auf "MAU", wenn ein Masterupdate durchgeführt wird
bool heizungauto; // Status heizung Automatik oder Handsteuerung
bool warmwasserauto; // Status Warmwasser Automatik ode Handsteuerung
bool steuerunghand; // speichert Zustand HAND - Auto steuerung , true = HAND, false = AUTO
const String smheizungauto = "++++ Heizung AUTO"; // Meldetexte für Schaltbefehle
const String smheizungaus = "----- Heizung AUS";
const String smwarmwasserauto = "++++ Warmwasser AUTO";
const String smwarmwasseraus = "----- Warmwasser AUS";
const String smsteuerunghand = "----- Steuerung HAND";
const String smsteuerungauto = "++++ Steuerung AUTO";
const String smepatroneein = "++++ E-Patrone EIN";
const String smepatroneaus = "----- E-Patrone AUS";
// --------------- Durchschnitt- und Integralberechnung ---------------------
byte samplezeitzaehler; // zählt die 15 sec Durchläufe bis eine ganze Minute voll ist
const int16_t ds_indexmax = 243; // 240 + 3 , Sampleanzahl in Minutenraster für Durchschnitt (240 = 4 Std)
class cDurchschnittsberechnung { // die Klasse für die Durchschnittsberechnung und die dazugehörige Funktion
public:
cDurchschnittsberechnung (String name, String einheit, int16_t anz, int16_t idx) { // constructor definiert das Variablenset für den Start
sname = name;
seinheit = einheit;
anzahl = anz;
index = idx;
};
String sname; // Name des Wertes - Anzeige HTML
String seinheit; // Einheit - Anzeige HTML
float summe; // lfd Summe der Werte
float wert; // der aktuelle Wert
float durchschnitt; // aktueller Durchschnitt
int16_t anzahl; // anzahl der Werte für diese Durchschnittsberechnung - max. zyklus
float wertspeicher[ds_indexmax]; // array in dem die einzelnen werte gespeichert werden
int16_t index;
bool speichervoll;
};
cDurchschnittsberechnung dslichtsonne ("Sonnenlicht", "%", 240, 1); // Definition der Onjekte der Klasse .... 180 min, Index muss 1 sein
cDurchschnittsberechnung dsaussentemp ("Aussentemp", "°C", 30, 1);
// --------------- Integrale ---------------------
const byte integralarraymax = 33; // 31 Tage + 2 Reserve
String swochentag[integralarraymax];
class cIntegralberechnung { // die Klasse für die Integralberechnung und die dazugehörige Funktion
public:
cIntegralberechnung (String name, String einheit, float sta, float end) { // constructor definiert das Variablenset für den Start
sname = name;
seinheit = einheit;
start = sta;
ende = end;
};
String sname; // Name des Wertes - Anzeige HTML
String seinheit; // Eingeit des fertigen Integrals - Anzeige HTML
float summe; // lfd Summe der Werte
float summealt[integralarraymax]; // speichert die alten summen der vorangegangenen Integralperioden
float durchschnitt; // durchschnittswert aus summe / zaehler
float durchschnittalt[integralarraymax];
float wert; // der aktuelle Wert
String sdatum[integralarraymax]; // datum in kurzform
float start; // Startzeit in Dezimalform
float ende; // Endezeit des Integrals in Dezimalform
uint16_t zaehler; // anzahl der integrationen
bool integralfertig;
};
// Start und Endzeit sllte dann gleich sein, wenn sie ale auf einer tabelle angeziegt werden
cIntegralberechnung isolarenergie ("Solar", "k°Cmin", 0, 23.9); // Definition der Objekte der Klasse .... 180 min, Index muss 1 sein
cIntegralberechnung itagesaussentemp ("Aussen", "°C", 0, 23.9);
cIntegralberechnung iheizenergie ("Heizg.", "k°Cmin", 0, 23.9);
cIntegralberechnung iwarmwasserenergie ("WW", "k°Cmin", 0, 23.9);
cIntegralberechnung isonnenstrahlung ("Sonne", "k%min", 0, 23.9);
const int16_t solar_zu_kwh_umrechnung = 280; // umrechnung der gradCelsius * minuten in kwH: Erhebung 2024/05 anhand längerer MEssung
#endif // *****************************************
// ----------------------- Wasseralarm --------------------
unsigned long letzterwasseralarm; // speichert Zeit des letzten Wasseralarms
const byte intervallwasseralarm = 6; // in sec ###
int16_t wasser; // Messwert für Wassermelder
String swasser; // ... und der dazugehörige String
// ------------------------ E- Patrone / Mischer-----------------
const int16_t laufzeit_epatrone = 1800; // Ausschaltverzoegerung E-Patrone in sec (maximal Einschaltzeti)
unsigned long einschaltzeitpunkt_epatrone; // speichert Zeit zu der Epatrone eingschalten wurde
const int16_t laufzeit_mischer = 120;
unsigned long einschaltzeitpunkt_mischer;
// -------------------- Lichtsensoren --------------------------
int16_t lichtwert; // Messwert Lichtsensor 0-100%
int16_t lichtwertalt; // speichert den vorherigen Messwert des Lichtsensors (1 Zyklus alt) wird gebraucht für das lichtabhängige einschalten des TFT
String slicht; String slicht1; String slicht2;
bool lichtzeileanzeige0; // Schaltet in der Anzeige 0 zwischen Anzeige Lichtwerte und Status Ausgänge um
int16_t lichtwertsonne; // Messwert für Eingang Sonnestrahlung 0-100%
// ------------------Variablen für DHT und DHT AF --------- ---------------
float feuchtedht;
String sfeuchtedht; // s steht für String AnzeigeWert ... ist immer parallel zu einem float , in dem der Messwert gespeichert ist
float tempdht;
String stempdht;
byte dhtsensorfehler;
bool tempdhtgemeldet; // wenn feuchteüberschreitung gemeldet wurde, wird das auf true gesetzt und bei geringerem Wert zurück gestezt
bool feuchtedhtgemeldet; // wenn temp ..........
float feuchtedhtAF;
String sfeuchtedhtAF; // s steht für StringAnzeigeWert ... ist immer parallel zu einem float , in dem der Messwert gespeichert ist
float tempdhtAF;
String stempdhtAF;
byte dhtAFsensorfehler;
bool tempdhtAFgemeldet;
bool feuchtedhtAFgemeldet;
// ------------------------ NTP ------------------------------
#include <WiFi.h>
#include "time.h"
#include "sntp.h" // Bsp von der library
const char* ntpServer1 = "fritz.box"; // die eigene Fritz box
const char* ntpServer2 = "pool.ntp.org"; // offizieller NTP Server
const long gmtOffset_sec = 3600; // immter auf 3600
const int daylightOffset_sec = 3600; // im sommer auf 3600 , winter auf 0
const char* time_zone = "CET-1CEST,M3.5.0/02,M10.5.0/03"; // M3.5.0: 3 = 3rd month, 5 =last 0 = sunday, M10.5.0 = last sunday in 19th month ; TimeZone rule for Europe/Rome including daylight adjustment rules (optional)
// --------------------- Loop und Infrastruktur -----------------------
bool imsetup; // ist true, wenn die setuproutine durchlaufen wird
unsigned long letzterloop1; // gespeicherte Zeit des letzten Loop-Durchaufs
unsigned long letzterloop2;
unsigned long letzterloop3;
unsigned long letzterloop4;
unsigned long letzterloop5;
unsigned long messzeitstart; // für die Zeitmessungen der einzelnen Prozesse
unsigned long startzeithauptloop; // zur Messung und Anzeige der loop durchlaufzeiten
unsigned long letztermeldetest; // für Belastungstest mit Testmeldungen
uint32_t laufzeitsec; // ESP laufzeit in sec ...millis()/1000
// Verwendung in funktion sLaufzeit
uint16_t lzwochen;
uint16_t lztage;
uint16_t lzstunden;
uint16_t lzminuten;
uint16_t lzsekunden;
String slzstunden;
String slzminuten;
String slzsekunden;
uint32_t lzsec;
bool ntptimevorhanden; // Zeit und Datum aus NTP
byte ntpsek;
byte ntpmin;
byte ntpstd;
byte ntptag;
byte ntpwochentag;
byte ntpmonat;
byte ntpjahr;
uint16_t ntpjahrkurz;
//int ntpdaylightsavingflag; // ###
String suhrzeit;
String sdatum;
String sdatumkurz;
// ########################################## SETUP #########################################################################
void setup() {
imsetup = true; // steuert die Infos am Startdisplay zu Beginn - für WiFi Status Infos
Serial.begin(115200);
delay(1500); // wird gebraucht, bevor die serielle schnittstelle zur verfügung steht
Serial.println(F(" ------------------ SETUP START --------------------"));
Serial.println(F(" "));
NTPStart();
// NetworkTimePRotocoll - muss vor dem IPStart sein
pinMode(HEIZPUMPE, OUTPUT);
pinMode(WWPUMPE, OUTPUT);
pinMode(EPATRONE, OUTPUT);
ledcSetup(pwmchannelfreigabeled, pwmchannelfrequency, pwmchannelresolution); // Definition des PWM Channels unter Verwendung des ledc-libraries
ledcAttachPin(FREIGABELED, pwmchannelfreigabeled); // Zuweisung #PWM Channel0 zur Freigabeled
ledcSetup(pwmchannelgetdataled, pwmchannelfrequency, pwmchannelresolution); // Definition des PWM Channels
ledcAttachPin(GETDATALED, pwmchannelgetdataled); // Zuweisung #PWM Channel0 zur Freigabeled
pinMode(MISCHER_ZU, OUTPUT);
pinMode(MISCHER_AUF, OUTPUT);
#if ESP_DEVICE == 20 // ******************************
ledcSetup(pwmchannelkhzluefter, pwmchannelfrequency, pwmchannelresolution); // Definition des PWM Channels unter Verwendung des ledc-libraries
ledcAttachPin(KHZLUEFTER_EGBEEPER, pwmchannelkhzluefter);
#endif // ******************************
#if ESP_DEVICE == 21 // ******************************
ledcSetup(pwmchannelbeeper, pwmchannelfrequencybeeper, pwmchannelresolution); // Definition des PWM Channels unter Verwendung des ledc-libraries
ledcAttachPin(KHZLUEFTER_EGBEEPER, pwmchannelbeeper);
#endif // ******************************
pinMode(LICHTSENSOR, INPUT_PULLDOWN);
pinMode(WASSERSENSOR, INPUT_PULLDOWN);
pinMode(SONNENLICHTSENSOR, INPUT_PULLDOWN);
pinMode(DACHSOLARSENSOR, INPUT);
//pinMode(TFTLED, OUTPUT); // muss nicht sein
pinMode(TIRQ_PIN, INPUT_PULLUP); // wird gebraucht, - IRQ is down bzw fallende Flanke (Falling), daher interner PULLUP
#if TFTVorhanden == 1 // *****************************************
pinMode(SOLARPUMPE, OUTPUT);
tftbrightness = 130; // LED des displays - PWM Ansteuerung 0-255 = 0-100% Helligkeit
// String Speicherreservierungen (in 16 Byte Schritten):
//pAussenfeuchte.reserve(6); // ### Beispiel für Größenreservierung bei String (bessere Speicheraufteilung, weniger Fragemntierung im Heap)
//sFreigabe.reserve(5); // angeblich werden String sin 16 Byte Blöcken reserviet - dh. bei 5 Byte automatscih 16 Byte belegt
//sMasterupdate.reserve(5);
#endif // **************************************************
ts.begin(); // start des Touchscreen
ts.setRotation(tftrotation); // Rotation TS 0-3
Serial.println("Start Display");
display.begin(); // ### Fehlermeldung bei 2.8" Display - macht aber nichts im Betrieb
Serial.println("Start set Rotation"); // start des TFT
display.setRotation(tftrotation); // 90 grad ist Querformat , 0 = hochformat, 2 und 3 geht auch noch
if (azdeliverygehaeuse == true) tftbrightness = 255 - tftbrightness;
analogWrite(TFTLED, tftbrightness);
#if TFTVorhanden == 1 //*****************************************
display.fillScreen(BLACK);
display.setTextColor(weiss);
(tftx == 480) ? (display.setFont(&FreeSansBold9pt7b)) : (display.setFont(&FreeSans8pt7b)); // kleinere Schriftgröße bei 2,8" display
display.drawLine(0, 2, tftx, 2, WHITE);
display.setCursor(2, 20); // horizontal, vertikal
display.println(F("ControlCenter by P.Petschauer"));
(tftx == 480) ? (display.setFont(&FreeSans9pt7b)) : (display.setFont(&FreeSans8pt7b)); // kleinere Schriftgröße bei 2,8" display
display.print(F("CC-ESP: "));
display.print(myhostname);
display.print("; "); // mit #if TFTVorhanden
display.println(softwareversion);
display.print("Sketch: " + String(ESP.getSketchSize() / 1024) + " kB, ");
display.println("HW: " + String(ESP.getChipModel()));
byte zeilenanzahlkopf = 3;
display.drawLine(0, (4 + zeilenabstand * zeilenanzahlkopf), tftx, (4 + zeilenabstand * zeilenanzahlkopf), WHITE);
display.setCursor(2, 27 + zeilenabstand * zeilenanzahlkopf); // horizontal, vertikal
#endif //*******************************************
dht.begin(); // DHT Sensor starten
//dht.setup(25, DHTesp::DHT22); // ### Test mit einer anderen DHT-Library
#if ESP_DEVICE == 22 // ***************************** 2.DHT nur am DG ESP
dhtAF.begin();
#endif // *********************************
tempSetup(); // Setup DS18B20 Temperatursensoren
display.print(F("Verbindungsaufbau Wifi: "));
ledcWrite(pwmchannelgetdataled, 255); // schaltet GetDataLEd auf 100% (8 bit 0-255)
Wifistart(); // Teile des Code als Funktion in eigenm Tab ausgelagert - m_wifi.ino -- achtung - die tabs werden alphabetisch aufgerufen
ledcWrite(pwmchannelgetdataled, 0);
delay(1000);
OTAstart(); // Over the Air updates
startAsyncServermitURLs(); // Serverdefinition mit Unterseiten
// ========= Master / Slave Regelung für Start und die Steuerungskommandos ===================
// Holen des Zustands der Ausgänge vom anderen ESP (KG/EG)
#if TFTVorhanden == 1 // ******************************************
if (WiFi.status() == WL_CONNECTED) {
display.print(F("GetData gestartet: Versuche: "));
while (espa.swerte[0] != sprueftext && espa.getdatafehler < 8) { // erstes Datenfeld muss prüftext enthalten, dann gehts weiter - max 8 Fehlversuche
(digitalRead(FREIGABELED) == LOW) ? digitalWrite(FREIGABELED, HIGH): digitalWrite(FREIGABELED, LOW); // ist so wie if (...) ... else ..
sendData(); // wenn beide gleichzeitig starten, warten sie sonst zu lange (zb nach Stromausfall)
espa = ESPInterface(espa); //gibt einen Klasse an die Funktion und bringt eine Klasse zurück
if (espa.getdatafehler > 0 ) display.print("X"); // Fehlversuche werden mit einem X angezeigt
delay(1000);
} // end while
if (espa.getdatafehler == 0 ) display.print(" OK");
display.println("");
if (espa.swerte[0] == sprueftext && (espa.swerte[2] == sbutAUTO || espa.swerte[2] == sbutAUS)) { // Schaltbefehle übernehmen, wenn Daten in Ordnung sind
unsigned long laufzeit = espa.swerte[14].toInt(); // wandelt string in int um
if (smyname == "EG") {
display.setTextColor(gruen);
display.println(F("Update OK, Status Ausgaenge aktualisiert"));
Meldung("Update OK, Ausgaenge uebernommen", 'g');
display.setTextColor(weiss);
}
if (smyname == "KHz") { // Übernahme der Schaltzustände des EG, wenn er ausreichend lange läuft
float f = laufzeit;
String slaufzeit;
if (laufzeit > 3600 * 2) {f = laufzeit / 3600; slaufzeit = String(f, 1) + "h";}
else slaufzeit = String(laufzeit) + "s";
if (laufzeit > millis() / 1000 + 120) { // ESP im EG muss x sec länger laufen als KG-Hz
switchGetDataBefehle();
display.setTextColor(gruen);
display.println(F("Update erfolgt - Ausgaenge geschalten"));
Meldung("Update OK, Ausgaenge geschalten", 'g');
Meldung("lokalerESP: " + String (millis() / 1000) + "s, 2.ESP: " + slaufzeit, 'w');
display.setTextColor(weiss);
} // end if
else {
display.print(F("Datentransfer OK, "));
display.setTextColor(rot);
display.println(F("aber kein Update"));
display.println(F("Laufzeit des anderen ESP zu kurz"));
display.setTextColor(weiss);
Meldung("Update erfolgt, Ausgaenge nicht geschalten", 'w');
Meldung("lokalerESP: " + String (millis() / 1000) + "s, 2.ESP: " + slaufzeit, 'w');
} // end else
} // end if smyname
else switchGetDataBefehle(); // wenn nicht KHz , dann wird geschalten ohne Laufzeitabfrage
} // end if swerte
else { // Wenn der Inhalt der Datenfelder nicht OK ist
display.setTextColor(rot);
display.println(F("Datentransfer fehlerhaft!"));
Meldung("Datentransf.Update Ausgaenge fehlerhaft", 'o');
display.setTextColor(weiss);
}
} // end if wifi.status
display.drawLine(0, tfty - 10, tftx, tfty - 10, WHITE);
delay(1000);
// ================== WHATs APP ========================
String appmessage = ("ESP Neustart: " + smyname + ", IP: " + String(WiFi.localIP()) + ", WLAN: " + String(WiFi.RSSI()) + "dBm, " + String(millis()) + "ms");
Serial.println("WhatsAppMessage:");
Serial.println(appmessage);
//### sendWhatsAppMessage(appmessage);
#endif // *****************************************
// ---------- Freigabe- und GetDataLED werden langsam heller ------------------ (auch WArtezeit zum Lesen des Startscreens)
lichtwert = analogRead(LICHTSENSOR) * 100 / 4095; // misst Raumhelligkeit bei jedem ESP
int zeit = 10;
if (lichtwert < 10 || lichtwert > 90) zeit = 1; // wenn der Lichtsensor verdunkelt ist oder sehr hell ist, geht der Start schneller
for (freigabeledhelligkeit = 0; freigabeledhelligkeit < 130; freigabeledhelligkeit++) {
ledcWrite(pwmchannelfreigabeled, freigabeledhelligkeit);
delay(zeit);
if (TFTVorhanden == 1) delay(zeit * 2);
}
for (getdataledhelligkeit = 0; getdataledhelligkeit < 130; getdataledhelligkeit++) {
ledcWrite(pwmchannelgetdataled, getdataledhelligkeit);
delay(zeit);
if (TFTVorhanden == 1) delay(zeit * 2);
}
getdataledhelligkeit = 0;
freigabeledhelligkeit = 0;
ledcWrite(pwmchannelfreigabeled, freigabeledhelligkeit);
ledcWrite(pwmchannelgetdataled, getdataledhelligkeit);
#if TFTVorhanden == 1 // *******************
display.fillScreen(BLACK);
#endif // **********************************
/* // druckt alle Charzeichen von 0 -130 ASCII Code
char zeichen;
int z = 1;
Serial.println(F("ASCII Zeichencode - TFT Displaytreiber kann nur ASCII-Zeichen: "));
for (int i = 0; i <= 130; i++) {zeichen = char(i); Serial.print(String(i) + " "+ zeichen + ", "); z++; if (z == 10) {Serial.println(); z = 1;}}
Serial.println();
*/
letzteslichtraschheller = millis();
lichtraschheller = true;
imsetup = false;
#if TFTVorhanden == 1 //*********************************************
isolarenergie = Datumerstbefuellung(isolarenergie); // das Wochentagarray in den Objekten der Integralklasse werden befüllt
itagesaussentemp = Datumerstbefuellung(itagesaussentemp);
#endif // **************************************************
Serial.println(F(" ------------------ SETUP ENDE --------------------"));
Serial.println(F(" "));
Meldung("Setuproutine beendet", 'w');
} // Ende SETUP
#if TFTVorhanden == 1 // ************************************************
// --------------------------------------------
cIntegralberechnung Datumerstbefuellung (cIntegralberechnung integralx) { // befüllt zu Beginn die Arrays er Integralklasse mit den Wochentagen
for (int i = 0; i < integralarraymax; i++) {
integralx.sdatum[i] = "----";
}
return integralx;
} // end String sWochentagerstbefuellung
#endif // ***************************************************
// #################### LOOP ###############################################################################
void loop() {
/* Struktur Loop:
1. sehr schnelle Abfragen - OTA, Touch, Freigabegesamt
2. Rücksetzen der Zeitzähler bei Neustart millis nach ca 50 Tagen
3. 1 Sek Loop1 Screensaver, TFT Helligkeitsteuerung, FreigabeLED
4. 4 Sek Loop2 - Display Anzeigen, SendData, GetData und Stringzerlegung, Schalten der Kommandos
5. 7 Sek Loop3 - Temperaturabfrage , Lichtwerte, Löschen DL und % Anzeigen
6. 15 Sek Loop4 - DHT Abfrage, Wifi Verbindungsversuche, Epatrone
7. 6 min Loop5 - für Messwerte für die Diagramme (Zeit bestimmt über diagrammscannintervall in sec)
*/
// =========================== die schnellen Aufgaben ===================================
startzeithauptloop = micros();
// ----------------------- 50 Tage millis() reset ----------
if (millis() > 5 && millis() < 2000) {
Meldung("Millisueberlauf - ms>5&<2000!", 'y');
letzterloop1 = millis();
letzterloop2 = millis();
letzterloop3 = millis();
letzterloop4 = millis();
letzterloop5 = millis();
letztertouchscreensaver = millis();
letzteslichtraschheller = millis();
letzterwificonnectversuch = millis();
letztefreigabe_resetesp = millis();
letztermeldetest = millis();
letztefreigabegesamt = millis();
letztefreigabeweb = millis();
letzterwasseralarm = millis();
letztertouchbutton = millis();
letztermasterupdate = millis();
letztesmasterupdatenachlaufzeit = millis();
einschaltzeitpunkt_epatrone = millis();
einschaltzeitpunkt_mischer = millis();
for (int i = 0; i < felderfusszeile; i++) { // Variablen aus TouchScreen Buttons
freigabetouchunten[i] = false;
letztefreigabetouchunten[i] = 0;
freigabetouchmitte[i] = false;
letztefreigabetouchmitte[i] = 0;
}
touchbuttondazwischennichtgedrueckt = false;
letztetftaenderungoben = millis();
} // end if millis()
ArduinoOTA.handle();
#if TFTVorhanden == 1 // *****************************************
Touchscreen();
// -------- Nachlaufzeit Freigabegesamt bei getData-Freigabe ------------------- muss in der schnellen loop bleiben - loop1 ist zu langsam für erkennung schaltbefehle bzw freigabe bei touch
if (freigabegesamt == true || millis() < letztefreigabegesamt + nachlaufzeitfreigabegesamt) { // nachlaufzeit für Freigabe, damit die steuerbefehle noch übertragen werden
sFreigabe = "JA";
if (freigabegesamt == true) freigabegesamtalt = freigabegesamt;
}
else if (freigabegesamt == false && freigabegesamtalt == true) { // erkennen fallende Flanke bei freigabegesamt
letztefreigabegesamt = millis();
freigabegesamtalt = freigabegesamt;
}
else if (freigabegesamt == false) {
sFreigabe = "NEIN";
freigabeweb = false;
}
// ------------------------- Freigabeweb retour setzen ---------------------------- (wird gebraucht, falls die Freigabe per web erteilt wird, die webseite dann aber vor dem zurücksetzen der Freigabe wieder verlassen wird)
if (millis() > letztefreigabeweb + nachlaufzeitfreigabegesamt && millis() < letztefreigabeweb + nachlaufzeitfreigabegesamt + 3000) {
freigabeweb = false;
}
// ------------------ Nachlaufzeit Masterupdate - wird gleich behandelt wie Freigabe - Verlängerung der Laufzeit ----------------
if (smyname == "KHz") {
if (masterupdate == true || millis() < letztesmasterupdatenachlaufzeit + nachlaufzeitfreigabegesamt) { // nachlaufzeit für Freigabe, damit die steuerbefehle nochübertragen werden
sFreigabe = "JA";
smasterupdate = "MAU";
if (masterupdate == true) masterupdatealt = masterupdate;
}
else if (masterupdate == false && masterupdatealt == true) { // erkennen fallende Flanke bei freigabegesamt
letztesmasterupdatenachlaufzeit = millis();
masterupdatealt = masterupdate;
}
else if (masterupdate == false) {
smasterupdate = "NEIN";
}
} // end if smyname
# endif // *****************************************
// ---------------------- Freigabegesamt -----------------------------------
if (freigabeweb == true || freigabetouchunten[0] == true || freigabetouchunten[1] == true || freigabetouchunten[2] == true || freigabetouchmitte[0] == true || freigabetouchmitte[1] == true || freigabetouchmitte[2] == true) {
freigabegesamt = true;
}
else freigabegesamt = false;
// ------------------- Versaeumte Meldungen berechnen ----------------------------
versaeumtemeldungen = midx + midxmax * angezeigtemeldungenueberlauf - letzteangezeigtemeldung; // berechnet die summe an meldungen - den aus der letztenanzeige
if (versaeumtemeldungen >= midxmax - 1) versaeumtemeldungen = midxmax - 1;
if (versaeumtemeldungen >= 99) versaeumtemeldungen = 99; // damit nicht mehr angezeigt wird als 99, egal wie hoch midxmax ist
if (angezeigtemeldungenueberlauf > 5) angezeigtemeldungenueberlauf = 5; // Begrenzung für Variable
if (zeitmessunghauptloop == true) Serial.println("LOOP - OOOOO schnelle HauptLoopdurchlaufzeit: " + String(micros() - startzeithauptloop) + " microsec.");
// ====================== 1 Sec Loop1 ------- Screensaver, TFT ein bei hell ==================
// wenn 1 sek loop zu zeitaufwändig wird evt Problem in touchroutine nichtTouchProzesseStop()
if (millis() > letzterloop1 + 1000) {
letzterloop1 = millis();
Loop1();
if (zeitmessung == true) Serial.println("LOOP 1 - OOOOO Loopdurchlaufzeit 1: " + String(micros() - startzeithauptloop) + " microsec.");
} // end if loop1
// =================== 4 sec Loop2 --- TFT Refresh (Fusszeile in x sec , Werte in 2x sec)===============================
if (millis() > letzterloop2 + 4000) {
letzterloop2 = millis();
Loop2();
if (zeitmessung == true) Serial.println("LOOP 2 - OOOOO Loopdurchlaufzeit 2: " + String(millis() - letzterloop2) + " ms");
} // end if loop2
// ===================== 7 sec Loop3 ----- Temperatur async, Datenempfang , Stringaufteilung, Lichtwerte ==========================
if (millis() > letzterloop3 + 7000) {
letzterloop3 = millis();
Loop3();
if (zeitmessung == true) Serial.println("LOOP 3 - OOOOO Loopdurchlaufzeit 3: " + String(millis() - letzterloop3) + " ms");
} // end if loop3
// ================== 15 sec loop4 ----- E-Patrone , DHT Abfrage ============
if (millis() > letzterloop4 + 15000) {
letzterloop4 = millis();
Loop4();
if (zeitmessung == true) Serial.println("LOOP 4 - OOOOO Loopdurchlaufzeit 4: " + String(millis() - letzterloop4) + " ms");
} // end if loop 4
#if TFTVorhanden == 1 // *****************************************
// ----------------- schnelles Blinken FreigabeLED bei Bediensperre ------------------------
if ((bediensperregetdata == true || freigabeweb == true) && millis() > letztesschnellesblinkenfreigabeled + 250) {
if (freigabeledhelligkeit == 0) freigabeledhelligkeit = Helligkeitssteuerung();
else freigabeledhelligkeit = 0;
ledcWrite(pwmchannelfreigabeled, freigabeledhelligkeit);
letztesschnellesblinkenfreigabeled = millis();
}
// ================== loop5 für Diagrammdatenbefüllung ============
if (millis() > letzterloop5 + diagrammscanintervall * 1000) {
letzterloop5 = millis();
Loop5(); // ist in Tab nAnz5.ino
if (anzeige == 5) AnzeigeDiagramme();
if (zeitmessung == true) Serial.println("LOOP 5 - OOOOO Loopdurchlaufzeit 5: " + String(millis() - letzterloop5) + " ms");
} // end if loop 5
#endif // *****************************************
if (zeitmessunghauptloop == true) Serial.println("LOOP - OOOOO GESAMTE HauptLoopdurchlaufzeit: " + String(micros() - startzeithauptloop) + " microsec. ");
// =======================================================
} // Ende void LOOP()