Die Sketche wurden von mir teilweise stark modifiziert (OTA-Update, Zeitserver, Watchdog, usw.). Einfach mal durcharbeiten und unnötiges entfernen.
Code: Alles auswählen
//#define Serialprint
// Configuration of NTP
#define NTP_SERVER "ptbtime2.ptb.de"
#define TimeZone "CET-1CEST,M3.5.0/02,M10.5.0/03"
String vers = "1.1";
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <RunningMedian.h>
#include <ArduinoOTA.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <AddrList.h>
#include <time.h> // time() ctime()
time_t now; // this is the epoch
tm tm; // the structure tm holds time information in a more convient way
String datum = (__DATE__);
String zeit = (__TIME__);
String file = (__FILE__);
String dat = (datum+" - "+zeit);
String linkLocal, ipv6, ipv4, stamp, stampNow;
// CONSTANTS:
const int DISTANCE_RANGE_BEGIN = 1694; // Maximale Distanz in mm (Sensor > Tankboden)
// ******* Netzwerkeinstellungen *******
const char* ssid = ""; // SSID des vorhandenen WLANs
const char* password = ""; // Passwort für das vorhandene WLAN
IPAddress gateway(192,xxx,xxx,x); // IP-Adresse des WLAN-Gateways
IPAddress subnet(255,255,255,0); // Subnetzmaske
IPAddress ip(192,xxx,xxx,x); // feste IP-Adresse für den WeMos
IPAddress dns(192,xxx,xxx,x);
const char* host = "192.xxx.xxx.x"; // IP-Adresse der CCU (mit Punkten!)
ESP8266WebServer server(80); // Webserver initialisieren auf Port 80
// ******* Sensoreinstellungen *******
unsigned long deltaMessungSekunden = 10; //Zeitintervall (Sekunden) nach dem eine Messung erfolgt
int korrLevel = 0; // Korrekturwert fuer die Ölstandmessung
int korrTemp = 0; // Korrekturwert fuer die Temperaturmessung
// ******* Einstellungen fuer Meldungen an die CCU *******
String levelCCUname = "CUxD.CUX9000018:1.SET_STATE"; // CUxD-Gerät "Ölstand"
String tempCCUname = "Tanktemperatur"; // Bezeichnung der CCU-Systemvariable für die gemessene Temperatur
unsigned long deltaMeldungSekunden = 600; // Zeitintervall (Sekunden) nach dem eine CCU-Meldung erfolgt (0 bedeutet nie)
int deltaLevel = 1; // Füllhöheänderung bei der eine CCU-Meldung erfolgt (0 bedeutet nie)
int deltaTemp = 1; // Temperaturaenderung (°C) bei der eine CCU-Meldung erfolgt (0 bedeutet nie)
// US-100 ultrasonic rangefinder:
unsigned int HByte = 0, LByte = 0;
int level = 0, levelCCU = 0, temp = 0, tempCCU = 0, US100temp = 0, lastDistance = 0;
RunningMedian samplesMed = RunningMedian(5);
RunningMedian samplesAvg = RunningMedian(5);
unsigned long jetztMillis = 0;
unsigned long wifi_disc = 0;
unsigned long deltaMessungMillis = deltaMessungSekunden * 1000, letzteMessungMillis = 0;
unsigned long deltaMeldungMillis = deltaMeldungSekunden * 1000, letzteMeldungMillis = 0;
String antwort = "", meldung = "",letzteMeldungCCUzeit = "";
unsigned long previousMillis = 0;
int ledState;
unsigned long check_wifi = 30000;
String zeitstempel()// Betriebszeit als Stunde:Minute:Sekunde
{
char stempel[10];
int lfdStunden = millis()/3600000;
int lfdMinuten = millis()/60000-lfdStunden*60;
int lfdSekunden = millis()/1000-lfdStunden*3600-lfdMinuten*60;
sprintf (stempel,"%03d:%02d:%02d", lfdStunden, lfdMinuten, lfdSekunden);
return stempel;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
ESP.wdtDisable();
// Seriellen Monitor für Kontrollausgaben öffnen
Serial.begin(9600);
Serial.println("");
Serial.println("WeMos Oelstandsensor");
WiFi.persistent(false);
delay(100);
pinMode(LED_BUILTIN,OUTPUT);
configTime(TimeZone, NTP_SERVER);
// WLAN-Verbindung herstellen
WiFi.config(ip, gateway, subnet, dns); // auskommentieren, falls eine dynamische IP bezogen werden soll
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
pinMode(LED_BUILTIN,OUTPUT);
// Verbindungsaufbau abwarten
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("\nconnected IPv4\n");
ipv6config();
// Port (Default: 8266)
// ArduinoOTA.setPort(8266);
// Hostname (Default: esp8266-[ChipID])
ArduinoOTA.setHostname("Heizoelstand");
ArduinoOTA.onStart([]()
{
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
{
type = "sketch";
} else { // U_FS
type = "filesystem";
}
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]()
{
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
{
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.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();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// HTTP-Anfragen bearbeiten
server.on("/", wurzel_behandlung);
server.on("/level", melde_level);
server.on("/zeit", melde_zeit);
// HTTP-Server starten
server.begin();
// Startwerte fuer Zeittrigger
letzteMessungMillis = millis();
letzteMeldungMillis = millis();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
ArduinoOTA.handle();
if(WiFi.status() == WL_CONNECTED)
{
led(1000);
}
else
{
led(100);
}
// auf HTTP-Anfragen warten
server.handleClient();
jetztMillis = millis();
// neue Messung falls Zeitintervall erreicht
if(jetztMillis - letzteMessungMillis > deltaMessungMillis)
{
messung();
}
// neue Meldung an die CCU falls Zeitintervall erreicht
if(!deltaMeldungMillis == 0 && jetztMillis - letzteMeldungMillis > deltaMeldungMillis)
{
melde_CCU();
}
// neue Meldung an die CCU falls Ölstandänderung den Schwellwert erreicht
if(!deltaLevel == 0 && abs(level - levelCCU) >= deltaLevel)
{
melde_CCU();
}
// neue Meldung an die CCU falls Temperaturaenderung den Schwellwert erreicht
if(!deltaTemp == 0 && abs(temp - tempCCU) >= deltaTemp) // Neue Meldung wg. Temperaturaenderung
{
melde_CCU();
}
// WiFi-Kontrolle
if((WiFi.status() != WL_CONNECTED)&& (millis() > check_wifi))
{
wifi_disc = millis();
wifiReconnect();
}
if (wifi_disc < 10000)
{
ESP.wdtFeed();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void messung() // Sensor abfragen
{
// Serielle Schnittstelle für US-100 öffnen
Serial.begin(9600);
delay(100);
samplesAvg.clear();
for (int avgloop = 0; avgloop < 5; avgloop++)
{
ESP.wdtFeed();
samplesMed.clear();
for (int medloop = 0; medloop < 5; medloop++)
{
ESP.wdtFeed();
Serial.flush(); // Clear the serial1 buffer.
while (Serial.available() >= 1) // seemed like flush() was not working so I added this.
{
int junk = Serial.read();
}
Serial.write(0x55); // Send a "distance measure" command to US-100
delay(200); // US100 response time depends on distance.
if (Serial.available() >= 2) // at least 2 bytes are in buffer
{
HByte = Serial.read(); // Read both bytes
LByte = Serial.read();
int distance = (HByte * 256 + LByte);
samplesMed.add(distance);
}
}
int avrg = samplesMed.getMedian();
samplesAvg.add(avrg);
}
int Distance = samplesAvg.getAverage();
level = (DISTANCE_RANGE_BEGIN + korrLevel - Distance);
// Read temperature from the US-100 ultrasonic rangefinder's temp sensor at the top of the tank. The tank air heats up in the sun.
Serial.flush();
while (Serial.available() >= 1) // seemed like flush() was not working so I added this.
{
int junk = Serial.read();
}
Serial.write(0x50); // send command to request temperature byte.
delay(50); // temp response takes about 2ms after command ends.
if (Serial.available() >= 1)
{
US100temp = Serial.read();
if ((US100temp > 1) && (US100temp < 130))
{
US100temp -= 45; // Correct by the 45 degree offset of the US100.
}
}
// Serial.end();
temp = US100temp + korrTemp;
letzteMessungMillis = jetztMillis;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_CCU()// Werte an die CCU melden
{
WiFiClient client; // Webclient initialisieren
const int httpPort = 8181;
if (!client.connect(host, httpPort)) // mit dem CCU-Port 8181 verbinden
{
delay(100);
return;
}
String url = "/xy.exe?antwort1=dom.GetObject('" + levelCCUname + "').State('" + level + "')"
"&antwort2=dom.GetObject('" + tempCCUname + "').State('" + temp + "')";
client.println(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); // Daten an CCU melden
delay(100);
int i = 0;
while(client.available()) // Antwort der CCU zeilenweise auslesen
{
i ++;
String zeile = client.readStringUntil('\r'); // Zeitstempel der CCU ausfiltern und merken
if (i == 7)
{
String stamp = NtpTime();
stampNow = stamp;
}
}
letzteMeldungMillis = jetztMillis; // gemeldete Daten merken
levelCCU = level;
tempCCU = temp;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void wurzel_behandlung()// bei Aufruf des Root-Verzeichnisses
{
ipv6config();
String stamp = NtpTime();
String betriebszeit = zeitstempel();
antwort = "WeMos Oelstandsensor\n";
antwort = antwort + "\t" +file + " - Version: " + vers + "\n";
antwort = antwort + "\tUpdate: " + dat + "\n";
antwort = antwort + "\tBetriebszeit: " + betriebszeit + " (Std:Min:Sek)\n";
antwort = antwort + "\tUhr: " + stamp + "\n";
antwort = antwort + "\tVerbunden mit: " + ssid + "\n";
int rssi = WiFi.RSSI();
antwort = antwort + "\tSignalstaerke: " + String(rssi) + " dBm\n";
antwort = antwort + "\tIPv4-Adresse: http://" + ipv4 + "\n";
antwort = antwort + "\tIPv6-Global: http://[" + ipv6 + "]" + "\n";
antwort = antwort + "\tIPv6-Local: http://[" + linkLocal + "]" + "\n\n";
antwort = antwort + "Letzte Messwerte\n\tMessintervall: " + String(deltaMessungSekunden) + " Sekunden\n";
antwort = antwort + "\tFuellhoehe: " + String(level) + " mm\n";
antwort = antwort + "\tTemperatur: " + String(temp) + " *C\n";
antwort = antwort + "Korrekturwerte\n\tSensorabstand: " + String(korrLevel) + " mm\n";
antwort = antwort + "\tTemperatur: " + String(korrTemp) + " *C\n\n";
antwort = antwort + "Letzte Datenuebertragung CCU\n\t" + stampNow + "\n";
antwort = antwort + "\tGemeldete Fuellhoehe: " + String(levelCCU) + " mm\n";
antwort = antwort + "\tGemeldete Temperatur: " + String(tempCCU) + " *C\n\n";
antwort = antwort + "Ausloeser fuer Datenuebertragung CCU\n";
antwort = antwort + "\tSendeintervall: " + String(deltaMeldungSekunden) + " Sekunden\n";
antwort = antwort + "\tFuellhoehendifferenzwert: " + String(deltaLevel) + " mm\n";
antwort = antwort + "\tTemperaturdifferenzwert: " + String(deltaTemp) + " *C\n\n";
antwort = antwort + "HTTP-Befehlsuebersicht:\n";
antwort = antwort + "\"192.xxx.xxx.x/level\" gibt die zuletzt gemessenen Fuellhoehe aus (mm)\n";
antwort = antwort + "\"192.xxx.xxx.x/temp\" gibt die zuletzt gemessene Temperatur aus (Grad Celsius)\n";
antwort = antwort + "\"192.xxx.xxx.x/zeit\" gibt den CCU-Zeitstempel aus, an dem die letzte Meldung an die CCU erfolgte\n";
antwort = antwort + "\"192.xxx.xxx.x/temp?delta=\" setzt den Temperatur-Differenzwert in *C, der eine CCU-Meldung ausloest\n";
antwort = antwort + "\"192.xxx.xxx.x/temp?korr=\" setzt den Korrekturwert fuer die Temperaturmessung in *C\n";
antwort = antwort + "\"192.xxx.xxx.x/level?delta=\" setzt den Fuellhoehe-Differenzwert in mm, der eine CCU-Meldung ausloest\n";
antwort = antwort + "\"192.xxx.xxx.x/level?korr=\" setzt den Korrekturwert fuer Abstand Tankboden > Sensor in mm\n";
antwort = antwort + "\"192.xxx.xxx.x/zeit?delta=\" setzt die Zeitspanne (in Sekunden), nach der spaetestens eine CCU-Meldung erfolgt\n";
antwort = antwort + "\"192.xxx.xxx.x/zeit?mess=\" definiert das Messintervall (in Sekunden)\n";
server.send(300, "text/plain", antwort);
delay(150);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_level() // bei Aufruf von ".../level"
{
String delta = server.arg("delta");
String korr = server.arg("korr");
if (delta != "")
{
deltaLevel = delta.toFloat();
server.send(200, "text/plain", "Fuellhoehedifferenzwert fuer CCU-Meldungen auf " + delta + " mm gesetzt.");
delay(100);
}
else if (korr != "")
{
korrLevel = korr.toFloat();
server.send(200, "text/plain", "Korrekturwert fuer Abstand Tankboden > Sensor auf " + korr + " mm gesetzt.");
delay(100);
}
else {
server.send(200, "text/plain", String(level));
delay(100);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_temp() // bei Aufruf von ".../temp"
{
String delta = server.arg("delta");
String korr = server.arg("korr");
if (delta != "")
{
deltaTemp = delta.toFloat();
server.send(200, "text/plain", "Temperaturdifferenzwert fuer CCU-Meldungen auf " + delta + " *C gesetzt.");
delay(100);
}
else if (korr != "")
{
korrTemp = korr.toFloat();
server.send(200, "text/plain", "Korrekturwert fuer Temperaturmessung auf " + korr + " *C gesetzt.");
delay(100);
}
else
{
server.send(200, "text/plain", String(temp));
delay(100);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_zeit()// bei Aufruf von ".../zeit"
{
String delta = server.arg("delta");
String mess = server.arg("mess");
if (delta != "")
{
deltaMeldungSekunden = delta.toInt();
deltaMeldungMillis = deltaMeldungSekunden * 1000;
server.send(200, "text/plain", "Zeitintervall fuer CCU-Meldungen auf " + delta + " Sekunden gesetzt.");
delay(100);
}
else if (mess != "")
{
deltaMessungSekunden = mess.toInt();
deltaMessungMillis = deltaMessungSekunden * 1000;
server.send(200, "text/plain", "Zeitintervall fuer Sensorabfrage auf " + mess + " Sekunden gesetzt.");
delay(100);
}
else
{
server.send(200, "text/plain", "Letzte Aktualisierung der CCU\n" + letzteMeldungCCUzeit);
delay(100);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void wifiReconnect()
{
#ifdef Serialprint
Serial.println("WiFi-Reconnect");
#endif
// WiFi.config(ip, gateway, subnet); // auskommentieren, falls eine dynamische IP bezogen werden soll
// WiFi.mode(WIFI_STA);
WiFi.disconnect();
WiFi.begin(ssid, password);
// Serial.print("Verbindungsaufbau");
check_wifi = millis() + 30000;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void led(unsigned long interval)
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis; // save the last time you blinked the LED
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
{
ledState = HIGH;
}
else
{
ledState = LOW;
}
digitalWrite(LED_BUILTIN, ledState); // set the LED with the ledState of the variable:
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String NtpTime()
{
String SZ;
time(&now); // read the current time
localtime_r(&now, &tm); // update the structure tm with the current time
/* Serial.print("year:");
Serial.print(tm.tm_year + 1900); // years since 1900
Serial.print("\tmonth:");
Serial.print(tm.tm_mon + 1); // January = 0 (!)
Serial.print("\tday:");
Serial.print(tm.tm_mday); // day
Serial.print("\thour:");
Serial.print(tm.tm_hour); // hour 0-23
Serial.print("\tmin:");
Serial.print(tm.tm_min); // minutes 0-59
Serial.print("\tsec:");
Serial.print(tm.tm_sec); // seconds 0-61*
Serial.print("\twday");
Serial.print(tm.tm_wday); // days since Sunday 0-6
Serial.print(tm.tm_isdst); // 1 bei MESZ, sonst MEZ*/
String tag = String(tm.tm_mday);
String monat = String(tm.tm_mon + 1);
int jahre = (tm.tm_year + 1900);
String jahr = String(jahre);
String stunde = String(tm.tm_hour);
String minut = String(tm.tm_min);
String sekunde = String(tm.tm_sec);
if (tm.tm_isdst == 1)
SZ = ("[MESZ]");
else
SZ = ("[MEZ]");
stamp = (tag + '.' + monat + '.' + jahr + ' ' + '-' + ' ' + stunde + ':' + minut + ':' + sekunde + ' ' + SZ);
return stamp;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ipv6config()
{
#if LWIP_IPV6
// Warten auf die Globale IPv6
for (bool configured = false; !configured;)
{
for (auto addr : addrList)
if (configured = addr.isV6() && !addr.isLocal())
{
break;
}
Serial.print('.');
delay(500);
}
Serial.println("\nconnected IPv6\n");
for (auto a : addrList)
{
a.isV6() ? a.isLocal() ? linkLocal = a.toString() : ipv6 = a.toString() : ipv4 = a.toString();
}
#endif
}
Code: Alles auswählen
// Libraries to include:
#include <RunningMedian.h> // by Rob.Tillaart at gmail.com
// CONSTANTS:
const int DISTANCE_RANGE_BEGIN = 1635; // Maximale Distanz in mm (Tankboden)
const int DISTANCE_RANGE_END = 80; // Minimale Distanz in mm (maximaler Füllstand)
const uint32_t MEASUREMENT_PAUSE = 600;
const uint8_t OUT_RANGE_MIN = 0; // output start of range (8-bit)
const uint8_t OUT_RANGE_MAX = 254; // output end of range (OUT_RANGE_MAX must be greater than OUT_RANGE_MIN) (8-bit)
// Variables:
int difference = 0; // variable to hold temporary info during calculations
int q; // for loop counters.
// US-100 ultrasonic rangefinder:
unsigned int MSByte = 0;
unsigned int LSByte = 0;
unsigned int mmDistance = 0;
int Median_mm = 0; // Some of these I tried to use unsigned int and got an "ambiguous" compile error.
int TempmmDistance = 0; //Temporärer Speicher für die Mittelwertberechnung
int US100tempData = 0;
int junk;
RunningMedian US100reading = RunningMedian(9);
void send(uint8_t meas)
{
PORTB = (PORTB & 0b11100000) | (meas & 0b00011111);
PORTD = (PORTD & 0b00011111) | (meas & 0b11100000);
}
void blinkLed()
{
for (int idx = 0; idx < 9; ++idx)
{
digitalWrite(LED_BUILTIN, LOW); delay(100);
digitalWrite(LED_BUILTIN, HIGH); delay(100);
}
digitalWrite(LED_BUILTIN, LOW);
}
// SETUP ______________________________________________________________________________________________
void setup()
{
delay(5000);
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
// HomeMatic 8-bit transmitter; Set PB0 - PB4 and PD5 - PD7 to output
DDRB |= 0b00011111;
DDRD |= 0b11100000;
}
// MAIN LOOP _________________________________________________________________________________________________
void loop()
{
mmDistance = 0;
int DataToAvg = 9;
for (int avgloop = 1; avgloop < (DataToAvg + 1); avgloop++)
{
Serial.flush(); // Clear the serial1 buffer.
Serial.write(0x55); // Send a "distance measure" command to US-100
delay(200); // US100 response time depends on distance.
if (Serial.available() >= 2) // at least 2 bytes are in buffer
{
MSByte = Serial.read(); // Read both bytes
LSByte = Serial.read();//
TempmmDistance = (MSByte * 256 + LSByte);
delay(200);
mmDistance = mmDistance + TempmmDistance; // calculate distance in mm. Add to running sum.
if ((TempmmDistance > DISTANCE_RANGE_END) && (TempmmDistance < DISTANCE_RANGE_BEGIN)) // Test that the distance is in range.
{
US100reading.add(TempmmDistance); // put another datum into the buffer for a running median.
} else avgloop--; // discard this datum.
}
}
mmDistance = mmDistance/DataToAvg; // calculate the mean of N measurements.
Median_mm = US100reading.getMedian(); // get the current running median value of mm from sensor to surface of oil.
int Level = (DISTANCE_RANGE_BEGIN - Median_mm);
uint8_t measure = map(Level , 0 , 1600 , 0 , 254);
// measure and send
uint8_t meas = measure;
if(measure < 255)
{
send(meas);
}
else
{
blinkLed();
}
// Read temperature from the US-100 ultrasonic rangefinder's temp sensor at the top of the tank. The tank air heats up in the sun.
Serial.flush();
while (Serial.available() >= 1) // seemed like flush() was not working so I added this.
{
junk = Serial.read();
}
Serial.write(0x50); // send command to request temperature byte.
delay(50); // temp response takes about 2ms after command ends.
if (Serial.available() >= 1)
{
US100tempData = Serial.read();
if ((US100tempData > 1) && (US100tempData < 130))
{
US100tempData -= 45; // Correct by the 45 degree offset of the US100.
}
}
// pause for next measurement
delay(MEASUREMENT_PAUSE * 1000);
}