Weichwasseranlage mit ESP8266 (mod 12f) abfragen
Einleitung
Ich möchte gerne einen meiner RaspberryPI von ein paar Aufgaben entbinden und suche alternativen um die Aufgaben zu übernehmen. Z.B. ist die Abfrage der Weichwasseranlage die Haupttätigkeit (weil das Script jede Minute läuft) die ich abgeben möchte.
Da ich angefangen habe mit Arduino und ESP zu experimentieren sollte eins dieser kleinen Helfer die Aufgabe übernehmen und die Werte gleichzeitig mittels MQTT für Openhab zur Verfügung stellen.
Ziel ist also die Daten von der Anlage zu holen, zu analysieren und an die Influx-DB sowie per MQTT an Openhab zu schicken.
Ich habe mir dazu folgenden Code zusammengestellt (der aus vielen verschiedenen Quellen stammt und modifiziert wurde). Ich danke allen die mit ihren Fragen und vor allem den Antworten dazu beigetragen haben diesen Chaos-Code zu erstellen. Er ist nicht schön, aber funktioniert wunderbar.
Features
- Weichwasseranlage abfragen
- zusätzlich angebundene Sensoren
- Temperatursensor
- Luftfeuchtesensor
- Luftdurcksensor
- Uhrzeit per NTP (inkl. Sommer- und Winterzeit)
- Webserver um ein paar Daten direkt anzuzeigen
- Daten an InfluxDB schicken
- Daten per MQTT an Openhab schicken
Material
ESP8266-12F * Affiliatelink
Jumper-Kabel * Affiliatelink
BMP180 * Affiliatelink
Code
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <InfluxDbClient.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP8266WebServer.h>
// #############################################
// WLAN-Zugang
// #############################################
#define wifi_ssid "WLAN-SSID"
#define wifi_password "WLAN-PW"
// #############################################
// MQTT-Server (Openhab)
// #############################################
#define mqtt_server "192.168.0.13"
#define mqtt_user "openhabian"
#define mqtt_password ""
// #############################################
// MQTT-Topics für BME280
// ############################################
#define humidity_topic "esp1/humidity"
#define temperature_topic "esp1/temperature"
#define pressure_topic "esp1/pressure"
// #############################################
// InfluxDB-Server und DB-Name
// #############################################
#define INFLUXDB_URL "http://192.168.0.12:8086"
#define INFLUXDB_DB_NAME "weichwasser"
// #############################################
// NTP-Client
// #############################################
#define MY_NTP_SERVER "192.168.2.1"
#define MY_TZ "CET-1CEST,M3.5.0/02,M10.5.0/03"
// #############################################
// Datentypen BME280 definieren
// #############################################
float temp = 0.0;
float hum = 0.0;
float pres = 0.0;
float diff = 0.5;
time_t now; // this is the epoch
tm tm; // the structure tm holds time information in a more convient way
String TimeStamp;
ESP8266WebServer server(80);
String htmltext;
Adafruit_BME280 bme; // I2C
unsigned long delayTime;
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
while(!Serial); // time to get serial running
unsigned status;
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
while (1);
}
delayTime = 1000;
configTime(MY_TZ, MY_NTP_SERVER);
server.on("/", handle_OnData);
server.on("/f.css", handleCss);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
server.handleClient();
long now = millis();
if (now - lastMsg > 60000) {
lastMsg = now;
getValues();
fetchWeichwasser();
timeClient.update();
}
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(wifi_ssid);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
bool checkBound(float newValue, float prevValue, float maxDiff) {
return !isnan(newValue) &&
(newValue < prevValue - maxDiff || newValue > prevValue + maxDiff);
}
void getValues() {
float newPres = bme.readPressure() / 100.0F;
float newTemp = bme.readTemperature();
float newHum = bme.readHumidity();
if (checkBound(newTemp, temp, diff)) {
temp = newTemp;
Serial.print("New temperature:");
Serial.println(String(temp).c_str());
client.publish(temperature_topic, String(temp).c_str(), true);
}
if (checkBound(newHum, hum, diff)) {
hum = newHum;
Serial.print("New Huminity:");
Serial.println(String(hum).c_str());
client.publish(humidity_topic, String(hum).c_str(), true);
}
if (checkBound(newPres, pres, diff)) {
pres = newPres;
Serial.print("New Pressure:");
Serial.println(String(pres).c_str());
client.publish(pressure_topic, String(pres).c_str(), true);
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopic", "ESP01 alive");
// ... and resubscribe
client.subscribe("inTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void fetchWeichwasser() {
String hostURL = "http://192.168.0.14/mux_http"; //URL der Wasserenthaertungsanlage
String postData = "getXML(\"id=625&show=D_Y_1|D_Y_5|D_Y_10_1|D_A_1_1|D_A_1_2|D_A_1_3|D_A_2_1|D_A_3_1|D_A_3_2~\",1);";
if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
HTTPClient http; //Declare object of class HTTPClient
http.begin(espClient, hostURL); //Specify request destination
http.addHeader("Content-Type", "application/xml"); //Specify content-type header
int httpCode = http.POST(postData); //Send the request
String payload = http.getString(); //Get the response payload
//Serial.println(httpCode); //Print HTTP return code
if (String(httpCode).c_str()!="200") {
delay(30000);
}
//Serial.println(payload); //Print request response payload
payload.replace("<code>ok</code>", "");
//Serial.println(payload);
/*
D_Y_1 Wasserverbrauch gestern l
D_Y_5 Aktueller Regenerationsschritt
D_Y_10_1 Aktuelle Restkapazität Austauscher %
D_A_1_1 Aktueller Durchfluss m³
D_A_1_2 Restkapazität m³*°dH
D_A_1_3 Kapazitätszahl m³*°dH
D_A_2_1 Restzeit/-menge Reg.Schritt l oder min
D_A_3_1 Letzte Regeneration h
D_A_3_2 Prozentsatz der letzten Regeneration
*/
String D_Y_1 = xmlTakeParam(payload, "D_Y_1");
String D_Y_5 = xmlTakeParam(payload, "D_Y_5");
String D_Y_10_1 = xmlTakeParam(payload, "D_Y_10_1");
String D_A_1_1 = xmlTakeParam(payload, "D_A_1_1");
String D_A_1_2 = xmlTakeParam(payload, "D_A_1_2");
String D_A_1_3 = xmlTakeParam(payload, "D_A_1_3");
String D_A_2_1 = xmlTakeParam(payload, "D_A_2_1");
String D_A_3_1 = xmlTakeParam(payload, "D_A_3_1");
String D_A_3_2 = xmlTakeParam(payload, "D_A_3_2");
D_Y_1.trim();
D_Y_5.trim();
D_Y_10_1.trim();
D_A_1_1.trim();
D_A_1_2.trim();
D_A_1_3.trim();
D_A_2_1.trim();
D_A_3_1.trim();
D_A_3_2.trim();
// Single InfluxDB instance
InfluxDBClient influx_client(INFLUXDB_URL, INFLUXDB_DB_NAME);
// Define data point with measurement name 'device_status`
Point pointDevice("softliqSC18");
// Add data
pointDevice.addField("D_Y_1", D_Y_1.toFloat());
pointDevice.addField("D_Y_5", D_Y_5.toFloat());
pointDevice.addField("D_Y_10_1", D_Y_10_1.toFloat());
pointDevice.addField("D_A_1_1", D_A_1_1.toFloat());
pointDevice.addField("D_A_1_2", D_A_1_2.toFloat());
pointDevice.addField("D_A_1_3", D_A_1_3.toFloat());
pointDevice.addField("D_A_2_1", D_A_2_1.toFloat());
pointDevice.addField("D_A_3_1", D_A_3_1.toFloat());
pointDevice.addField("D_A_3_2", D_A_3_2.toFloat());
// Write data
influx_client.writePoint(pointDevice);
if (!client.connected()) {
reconnect();
}
client.publish("ww/wasserverbrauch", String(D_Y_1).c_str(), true);
client.publish("ww/regenerationsschritt", String(D_Y_5).c_str(), true);
client.publish("ww/restkapazitaet_prozent", String(D_Y_10_1).c_str(), true);
client.publish("ww/durchfluss", String(D_A_1_1).c_str(), true);
client.publish("ww/restkapazitaet_qmdH", String(D_A_1_2).c_str(), true);
client.publish("ww/kapazitaetszahl_qmdH", String(D_A_1_3).c_str(), true);
client.publish("ww/restzeitregeneration", String(D_A_2_1).c_str(), true);
client.publish("ww/letzteregeneration", String(D_A_3_1).c_str(), true);
client.publish("ww/prozentsatzletzteregeneration", String(D_A_3_2).c_str(), true);
time(&now);
localtime_r(&now, &tm);
TimeStamp = String(tm.tm_mday) + "." + String(tm.tm_mon + 1) + "." + String(tm.tm_year + 1900) + " " + String(tm.tm_hour) + ":" + String(tm.tm_min) + ":" + String(tm.tm_sec);
Serial.println(TimeStamp);
client.publish("ww/time", String(TimeStamp).c_str(), true);
htmltext = "";
htmltext += "<h2>Uhrzeit</h2>";
htmltext += "<p><b>" + String(TimeStamp) + "</b></p>";
htmltext += "<h2>Klimadaten</h2>";
htmltext += "<p>Temperatur: <b>" + String(temp) + " °C</b></p>";
htmltext += "<p>Luftfeuchte: <b>" + String(hum) + " %</b></p>";
htmltext += "<p>Luftdruck: <b>" + String(pres) + " hPa</b></p>";
htmltext += "<h2>Austauscher</h2>";
htmltext += "<p>Restkapazitaet: <b>" + String(D_Y_10_1) + " %</b></p>";
htmltext += "<p>Durchfluss: <b>" + String(D_A_1_1) + " m³/h</b></p>";
htmltext += "<p>Verbrauch gestern: <b>" + String(D_Y_1) + " l</b></p>";
htmltext += "<h2>Regeneration Austauscher</h2>";
htmltext += "<p>Regeneration vor <b>" + String(D_A_3_1) + " h</b></p>";
htmltext += "<p>Regeneration über <b>" + String(D_A_3_2) + " %</b></p>";
htmltext += "<p>Regenerationsschritt: <b>" + String(D_Y_5) + "</b></p>";
htmltext += "<p>Schrittdauer: <b>" + String(D_A_2_1) + " %/min</b></p>";
http.end(); //Close connection
} else {
Serial.println("Error in WiFi connection");
}
}
String xmlTakeParam(String inStr,String needParam)
{
if(inStr.indexOf("<"+needParam+">")>0){
int CountChar=needParam.length();
int indexStart=inStr.indexOf("<"+needParam+">");
int indexStop= inStr.indexOf("</"+needParam+">");
return inStr.substring(indexStart+CountChar+2, indexStop);
}
return "not found";
}
void handle_OnData() {
server.send(200, "text/html", SendHTML(htmltext));
}
void handle_OnConnect() {
server.send(200, "text/html", SendHTML("Boot"));
}
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
String SendHTML(String addtext){
String ptr = "";
ptr += "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n";
ptr += "<html lang='de'>\n";
ptr += "<head>\n";
ptr += "<title>Weichwasser Webserver</title>\n";
ptr += "<link rel='stylesheet' type='text/css' href='/f.css'>\n";
ptr += "<meta content='text/html; charset=UTF-8' http-equiv='content-type'>\n";
ptr += "</head>\n";
ptr += "<body>\n";
ptr += "<h1>Weichwasser Webserver</h1>\n";
ptr += addtext;
ptr += "</body>\n";
ptr += "</html>";
return ptr;
}
void handleCss()
{
String message = "";
message += "*{font-family:sans-serif}\n";
message += "body{margin:10px;width:300px}\n";
message += "h1, h2{color:white;background:#8FCE00;text-align:center}\n";
message += "h1{font-size:1.2em;margin:1px;padding:5px}\n";
message += "h2{font-size:1.0em}\n";
message += "h3{font-size:0.9em}\n";
message += "a{text-decoration:none;color:dimgray;text-align:center}\n";
message += ".small{font-size:0.6em}\n";
message += ".value{font-size:1.8em;text-align:center;line-height:50%}\n";
message += "footer p, .infodaten p{font-size:0.7em;color:dimgray;background:silver;";
message += "text-align:center;margin-bottom:5px}\n";
message += "nav{background-color:silver;margin:1px;padding:5px;font-size:0.8em}\n";
message += "nav a{color:white;padding:10px;text-decoration:none}\n";
message += "nav a:hover{text-decoration:underline}\n";
message += "nav p{margin:0px;padding:0px}\n";
message += ".btn{background-color:#C0C0C0;color:dimgray;text-decoration:none;";
message += "border-style:solid;border-color:dimgray}\n";
message += ".on, .off{margin-top:0;margin-bottom:0.2em;margin-left:3em;";
message += "font-size:1.4em;background-color:#C0C0C0;";
message += "border-style:solid;width:5em;height:1.5em;text-decoration:none;text-align:center}\n";
message += ".on{border-color:green}\n)";
server.send(200, "text/css", message);
}
Ich entschuldige mich ausdrücklich für den schlechten Codestil, ich nehme gerne Optimierungen entgegen :)
Ich wünsche trotzdem viel Spaß damit. Und so sieht es dann aus:
Schreibe einen Kommentar