Ich beschäftige mich jetzt seit ca. 2 Monaten mit dem ioBroker, habe schon einige Anregungen und Lösungen aus diesem Forum entnommen und habe mich jetzt heute hier angemeldet um vielleicht auch mal eine Lösung beizutragen.
Es geht mal wieder um die Wetterstationen, ich habe mir eine Ventus W830 gekauft, da hier die Diskussion im Gange war und das Shell-Script von @SBorg vorhanden war.
Ich hab das dann eingesetzt und es hat alles gut funktioniert.
Da bei mir der ioBroker aber auf meiner Diskstation in einem Docker Container (und da ist kein bc installiert) läuft , würde der Raspi nur dafür gebraucht und ich brauche den für was anderes.
Also musste eine andere Lösung her.
Nun gibt es diese wunderbaren ESP8266 boards (5 €...)
Langer Rede kurzer Sinn: Kleines Programm drauf, läuft...
Kurz zur Vorgangsweise:
Grundsätzlich setzt das Ganze genau auf die Vorgangsweise von @SBorg auf,
nachzulesen in diesem Thread.
Man braucht genau wie bei der Shell-Script Lösung den RESTful Adapter und die entsprechenden Datenpunkte (anlegen durch das Script von @SBorg , oder manuell).
Es sind oben im Sketch folgende Angaben zu machen:
// Replace with your credentials
const char* ssid = "YOURSSID";
const char* password = "YOURPASSDWORD";
const char* IPP = "http://xxx.xxx.xxx.xxx:8087" ; //Protocol, IP and Port where RESTful Adapter waiting.
String DP="javascript.0.Wetterparser."; // Folder where the final datapoints are
ssid und password : Die WLAN-Settings
IPP: IP des ioBroker-Rechners und Port des RESTful Adapters (default:8087)
DP: Der Ordner wo die Datenpunkte liegen, wenn das Originalscript von SBorg genommen wurde, ist das "javascript.0.Wetterstation."
Ein paar allgemeine Hinweise zumEinrichten der Wetterstation mittels App:
in "path" braucht man statt "/weatherstation/updateweatherstation.php?" nur "/" einzugeben.
Der Port ist im Sketch auf 80 gesetzt, das sollte er auch bleiben, weil das Board auch eine kleine Webseite ausgibt, damit man auf die Schnelle überprüfen kann, ob es da ist und die Browser greifen standardmäßig auf 80 zu.
Also in der App auch auf 80 stellen.
Die Felder Station ID und Station Key: hier kann man irgendwas eingeben (sollte aber nicht leer sein), das Sketch holt nur die Daten aus dem String, diese Einträge werden nicht beachtet.
Nun der Code:
(kann ganz einfach in die Arduino-IDE geklatscht werden, Werte anpassen, kompilieren, downloaden und fertig).
/*********
FatBob
ioBroker Parser for Weatherstation Ventus W830
Ver.: 0.1.0
*********/
// Load Wi-Fi library
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
// Replace with your credentials
const char* ssid = "YOURSSID";
const char* password = "YOURPASSWORD";
const char* IPP = "http://xxx.xxx.xxx.xxx:8087" ; //Protocol, IP and Port where RESTful Adapter waiting.
String DP="javascript.0.Wetterparser."; // Folder where the final datapoints are
// Set web server port number to 80 to recieve the datastring from Weatherstation
WiFiServer server(80);
bool messagearrived ;
// Variable to store the HTTP request
String header;
const char* headerKeys[] = {"date","server"};
const size_t numberOfHeaders = 2 ;
// Variables to store the values
float values[19];
String date; // hold the date
// Conversion functions
float F2C (float F){ //Fahrenheit to Celsius
float result;
result = F - 32;
result = result / 1.8;
return result;
}
float CP (float p){ //convert Pressure to hPa
float result;
result = p * 33864 / 1000;
return result;
}
String CT (String date){ //Convert Time from weatherstation
String dateout="";
String Year, Month, Day, Hour, Minute, Second ;
Year = date.substring(0,4);
Month = date.substring (5,7);
Day = date.substring(8,10);
Hour = date.substring(13,15);
Minute = date.substring(16,18);
Second = date.substring(19,21);
dateout += Day + ".";
dateout += Month + ".";
dateout += Year + "%20";
dateout += Hour + ":";
dateout += Minute + ":";
dateout += Second ;
return dateout;
}
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
messagearrived = true;
//disassemble message
String message = ""; // make a String to hold pure message
String singlevalue; // make a string to hold single value
int j = 0; // counter for Arryindex
message = header.substring(0,header.indexOf("&softwaretype")); // cut the string after the interesting data
//loop to extract the single values
for (int i = 0; i <= message.length(); i++) {
if (message.charAt(i) == '&'){
singlevalue = message.substring(i+1,message.indexOf('&',i+1));
singlevalue = singlevalue.substring(singlevalue.indexOf('=',0)+1);
if (j < 18){
values[j] = singlevalue.toFloat();
}
if (j == 18){ // 2020-02-18%2012:22:53 steckt in "singlevalue"
date = CT(singlevalue);
}
j++;
}
}
// Display a HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head></head>");
// Web Page Heading
client.println("<body><h1>Weather Station Parser</h1>");
// Display current version
client.println("<p>Ver.: 0.1.0</p>");
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
if (messagearrived) {
//Build REST String for ioBroker RESTful simple Adapter
String restmessage = "";
restmessage = IPP;
restmessage += "/setBulk?";
restmessage += DP + "Innentemperatur=" + String (F2C(values[1])) + "&"; //indoortempf
restmessage += DP + "Aussentemperatur=" + String (F2C(values[2])) + "&"; //tempf
restmessage += DP + "Taupunkt=" + String (F2C(values[3])) + "&"; //dewptf
restmessage += DP + "Chillfaktor=" + String (F2C(values[4])) + "&"; //windchillf
restmessage += DP + "Innenfeuchtigkeit=" + String (values[5]) + "&"; //indoorhumidity
restmessage += DP + "Aussenfeuchtigkeit=" + String (values[6]) + "&"; //humidity
restmessage += DP + "Wind=" + String (1.61*(values[7])) + "&"; //windspeedmph
restmessage += DP + "Wind_max=" + String (1.61*(values[8])) + "&"; //windgustmph
restmessage += DP + "Windrichtung=" + String (values[9]) + "&"; //winddir
restmessage += DP + "Druck_absolut=" + String (CP(values[10])) + "&"; //absbaromin
restmessage += DP + "Druck_relativ=" + String (CP(values[11])) + "&"; //baromin
restmessage += DP + "Regen_aktuell=" + String (25.4*(values[12])) + "&"; //rainin
restmessage += DP + "Regen_Tag=" + String (25.4*values[13]) + "&"; //dailyrainin
restmessage += DP + "Regen_Woche=" + String (25.4*values[14]) + "&"; //weeklyrainin
restmessage += DP + "Regen_Monat=" + String (25.4*values[15]) + "&"; //monthlyrainin
restmessage += DP + "Sonnenstrahlung=" + String (values[16]) + "&"; //solarradiation
restmessage += DP + "UV_Index=" + String (values[17]) + "&"; //UV
restmessage += DP + "Zeitstempel=" + date + "&"; //dateutc
restmessage += "prettyPrint";
Serial.println("Send REST message:");
Serial.println(restmessage);
// initialize http client
HTTPClient http;
// send message
http.begin(restmessage);
// http.collectHeaders(headerKeys, numberOfHeaders), check for errors;
int httpCode = http.GET();
if (httpCode > 0) {
Serial.println("--- successfully sent ---");
}else{
Serial.println("An error occurred sending the request");
}
http.end();
messagearrived = false;
}
}
Ich weiß nicht, ob man sowas "Parser" nennen darf, ich tu es einfach
Das sollte auf jedem ESP8266 Board laufen, ich hab hier das NodeMCU Lua Lolin V3 Module ESP8266 ESP-12E in Verwendung.
Noch etwas:
Mir gefällt die Zeitkonvertierung in dem Sketch auch noch nicht so ganz, da die Wetterstation immer die Greenwich Mean time ausgibt, ich bin aber noch nicht draufgekommen, wie man das anständig konvertiert. Es ist mir aber egal, weil die Daten den richtigen Zeitstempel sowieso erst beim Loggen bekommen.
Überflüssig zu sagen: Der Einsatz des Sketches erfolgt auf eigene Gefahr, es gibt keinerlei Garantie oder Gewährleistung
Wenn Fragen auftauchen, gerne in diesem Thread fragen.
Nachdem ich mir über das Loggen in der Datenbank Gedanken gemacht habe, bin ich für mich
zu folgender Lösung gekommen:
Anlegen von 4 neuen Datenpunkten (alle Boolean), das ESP-board setzt diese Datenpunkte je nach Message von der Wetterstation auf true.
ddlw - wird täglich um 23:58:xx für 2 min "true" (do daily work)
dwlw - wird jeden Samstag um 23:58:xx für 2 min "true" (do weekly work) - (Wetterstation nullt um 00:00 Samstag/Sonntag)
dmlw - wird am letzten Tag jeden Monats (Schaltjahre sind berücksichtigt) um 23:58:xx für 2 min "true" (do monthly work)
dylw - wird am 31.12.xxxx um 23:58:xx für 2 min "true" (do yearly work)
Diese Datenpunkte können dann mittels Script zum Loggen der gewünschten Werte oder zum Kumulieren verwendet werden.
Warum gerade so ?
Nun, die Systemzeit der Wetterstation ist händisch eingestellt, da könnten Daten verloren gehen, wenn die Zeitquelle zum loggen von wo anders kommt.
Diese 4 Datenpunkte heissen also " jetzt kommen meine letzten Werte, in 1-2 Minuten sind sie weg".
Ich habs dann für mich im Script dann so gelöst, dass ich mir eigene Datenpunkte fürs loggen angelegt habe, diese im sql-Adapter auf "nur bei Änderung loggen" gesetzt habe und zur Logzeit diese mit den frischen Werten überschreibe, so kriege ich möglichst wenige Logs (ich will nicht bei jeder Bewegung der Regenwippe einen Log) und verliere auch nichts.
Die neuen Datenpunkte:
Der neue Code:
/*********
FatBob
ioBroker Parser for Weatherstation Ventus W830
Ver.: 0.2.0
*********/
// Load Wi-Fi libraries
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
// Load Time libraries
#include <Time.h>
#include <TimeLib.h>
#include <Timezone.h>
//####################### Replace with your credentials ###########################################
// Network environment
const char* ssid = "YOURSSID";
const char* password = "YOURPASSWORD";
const char* IPP = "http://xxx.xxx.xxx.xxx:8087" ; //Protocol, IP and Port where RESTful Adapter waiting.
String DP="javascript.0.Wetterparser."; // Folder where the final datapoints are
// TimeConversion rules
TimeChangeRule euCET = {"CET", Fourth, Sun, Mar, 2, +120}; //UTC + 2 hours (in minutes), set your credentials here for daylight saving time
TimeChangeRule euCEST = {"CEST", Fourth, Sun, Oct, 2, +60}; //UTC + 1 hours (in minutes), set your credentials here for standard time
//###################################################################################################
// Set web server port number to 80 to recieve the datastring from Weatherstation
WiFiServer server(80);
bool messagearrived ;
// Variable to store the HTTP request
String header;
const char* headerKeys[] = {"date","server"};
const size_t numberOfHeaders = 2 ;
// Variables to store the values
float values[19];
String date; // hold the date
time_t t; //hold time
// Conversion functions
float F2C (float F){ //Fahrenheit to Celsius
float result;
result = F - 32;
result = result / 1.8;
return result;
}
float CP (float p){ //convert Pressure to hPa
float result;
result = p * 33864 / 1000;
return result;
}
String CT (String date){ //Convert Time from weatherstation
char tdate[10], ttime[8];
String dateout="";
String Year, Month, Day, Hour, Minute, Second ;
time_t central, utc;
Timezone euCentral(euCET, euCEST);
Year = date.substring(0,4);
Month = date.substring (5,7);
Day = date.substring(8,10);
Hour = date.substring(13,15);
Minute = date.substring(16,18);
Second = date.substring(19,21);
setTime(Hour.toInt(),Minute.toInt(),Second.toInt(),Day.toInt(),Month.toInt(),Year.toInt()); // set system time to what came from weatherstation
utc = now();
central = euCentral.toLocal(utc); // change time as declared in rules
setTime(central); // set system time to converted local time
sprintf(tdate, "%02u.%02u.%4u",day(),month(),year()); //leading zero !
sprintf(ttime, "%02u:%02u:%02u",hour(),minute(),second()); // leading zero !!
for (int i = 0; i <= 9; i++) { //put date in dateout-string
dateout += tdate[i];
}
dateout += "%20"; // put (url)space in dateout-string
for (int i = 0; i <= 7; i++) { // put time in dateout-string
dateout += ttime[i];
}
Serial.println("Local datetime: " + dateout);
return dateout;
//setTime(utc); uncomment this, if your weatherstation deletes its values following "UTC"
}
// Current time for timeout
unsigned long currentTime = millis();
// Previous time for timeout
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
messagearrived = true;
//disassemble message
String message = ""; // make a String to hold pure message
String singlevalue; // make a string to hold single value
int j = 0; // counter for Arryindex
message = header.substring(0,header.indexOf("&softwaretype")); // cut the string after the interesting data
//loop to extract the single values
for (int i = 0; i <= message.length(); i++) {
if (message.charAt(i) == '&'){
singlevalue = message.substring(i+1,message.indexOf('&',i+1));
singlevalue = singlevalue.substring(singlevalue.indexOf('=',0)+1);
if (j < 18){
values[j] = singlevalue.toFloat();
}
if (j == 18){ // 2020-02-18%2012:22:53 steckt in "singlevalue"
date = CT(singlevalue);
}
j++;
}
}
// Display a HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head></head>");
// Web Page Heading
client.println("<body><h1>Weather Station Parser</h1>");
// Display current version
client.println("<p>Ver.: 0.2.0</p>");
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
if (messagearrived) {
//################################ Logging? #######################################
int monthHasDays[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
bool ly, ddlw, dwlw, dmlw, dylw;
// Leap year ?
int x =0, y=0, z=0;
x = year()% 4;
y = year()% 100;
z = year()% 400;
if (((x==0) & (y!=0)) || (z==0)){
ly =true;
monthHasDays[2] = 29;
}else {ly =false;}
if ((hour()>=23) && (minute() >=58)){
ddlw = true; // do daily work
}else{ddlw = false;}
if ((weekday() == 7) && (ddlw == true)){
dwlw = true; // do weekly work
}else{dwlw = false;}
if ((day() == monthHasDays[month()]) && (ddlw == true)){
dmlw = true; // do monthly work
}else{dmlw = false;}
if ((day()==31) & (month()==12) & (ddlw == true)){
dylw = true; //do yearly work
}else{dylw = false;}
//########################## Build REST String for ioBroker RESTful simple Adapter ################################
String restmessage = "";
restmessage = IPP;
restmessage += "/setBulk?";
restmessage += DP + "Innentemperatur=" + String (F2C(values[1])) + "&"; //indoortempf
restmessage += DP + "Aussentemperatur=" + String (F2C(values[2])) + "&"; //tempf
restmessage += DP + "Taupunkt=" + String (F2C(values[3])) + "&"; //dewptf
restmessage += DP + "Chillfaktor=" + String (F2C(values[4])) + "&"; //windchillf
restmessage += DP + "Innenfeuchtigkeit=" + String (values[5]) + "&"; //indoorhumidity
restmessage += DP + "Aussenfeuchtigkeit=" + String (values[6]) + "&"; //humidity
restmessage += DP + "Wind=" + String (1.61*(values[7])) + "&"; //windspeedmph
restmessage += DP + "Wind_max=" + String (1.61*(values[8])) + "&"; //windgustmph
restmessage += DP + "Windrichtung=" + String (values[9]) + "&"; //winddir
restmessage += DP + "Druck_absolut=" + String (CP(values[10])) + "&"; //absbaromin
restmessage += DP + "Druck_relativ=" + String (CP(values[11])) + "&"; //baromin
restmessage += DP + "Regen_aktuell=" + String (25.4*(values[12])) + "&"; //rainin
restmessage += DP + "Regen_Tag=" + String (25.4*values[13]) + "&"; //dailyrainin
restmessage += DP + "Regen_Woche=" + String (25.4*values[14]) + "&"; //weeklyrainin
restmessage += DP + "Regen_Monat=" + String (25.4*values[15]) + "&"; //monthlyrainin
restmessage += DP + "Sonnenstrahlung=" + String (values[16]) + "&"; //solarradiation
restmessage += DP + "UV_Index=" + String (values[17]) + "&"; //UV
restmessage += DP + "Zeitstempel=" + date + "&"; //dateutc
restmessage += DP + "ddlw=" + String(ddlw) + "&"; //do daily work
restmessage += DP + "dwlw=" + String(dwlw) + "&"; //do weekly work
restmessage += DP + "dmlw=" + String(dmlw) + "&"; //do monthly work
restmessage += DP + "dylw=" + String(dylw) + "&"; //do yearly work
restmessage += "prettyPrint";
Serial.println("Send REST message:");
Serial.println(restmessage);
// initialize http client
HTTPClient http;
// send message
http.begin(restmessage);
// http.collectHeaders(headerKeys, numberOfHeaders), check for errors;
int httpCode = http.GET();
if (httpCode > 0) {
Serial.println("--- successfully sent ---");
}else{
Serial.println("An error occurred sending the request");
}
http.end();
messagearrived = false;
}
}
Wer den Code liest, wird feststellen, dass die Datenpunkte zur lokalen Zeit ausgelöst werden.
Ich habe bei meiner Wetterstation festgestellt, dass sie zur lokalen Zeit um 00:00 nullt.
Im Thread zum ShellScript habe ich gesehen, dass bei anderen zur UTC genullt wird, warum das verschieden sein sollte, weiss ich nicht.
Wer sich sicher ist, dass seine Wetterstation bei UTC nullt, kann die Auskommentierung in Zeile 99 entfernen, dann wird nach dem Ausgeben der lokalen
Zeit (für den Datenpunkt "Zeitstempel") die Systemzeit wieder auf UTC gestellt.
Ich werde das jetzt mal so lassen, keine weiteren Ideen...
Ich hab das jetzt ein wenig erweitert, es wird jetzt die TimeZone Lib verwendet, also wird
die korrekte Zeit (Standard und Sommerzeit geht automatisch) eruiert.
@SBorg
Die NTP-Funktionalität ist mE problematisch weil:
Die Wetterstation hat selbst keine NTP-Funktion und auch keine Funkuhr, zumindest musste ich bei meiner Datum/Uhrzeit händisch einstellen.
Wenn man sich nun beim Loggen auf eine NTP-Zeit verlässt, kann man, wenn man zu spät dran ist (weil die Wetterstation "vorgeht") die Nullung der Regenmenge verpassen und die Daten verlieren.
Also ist es mE besser nur mittels Timezone die UTC der Wetterstation auf lokale Zeit umzusetzen.
Man muss sich dann beim Loggen auf jeden Fall auf die so errechnete Zeit stützen und nicht auf die Zeit des IO-Brokers.
Wie ich allerdings punktgenau ein Logging ausserhalb des sql-Adapters auslöse, weiß ich noch nicht .
Hier der Code:
/*********
FatBob
ioBroker Parser for Weatherstation Ventus W830
Ver.: 0.1.1
*********/
// Load Wi-Fi libraries
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
// Load Time libraries
#include <Time.h>
#include <TimeLib.h>
#include <Timezone.h>
//####################### Replace with your credentials ###########################################
// Network environment
const char* ssid = "YOURSSID";
const char* password = "YOURPASSWORD";
const char* IPP = "http://xxx.xxx.xxx.xxx:8087" ; //Protocol, IP and Port where RESTful Adapter waiting.
String DP="javascript.0.Wetterparser."; // Folder where the final datapoints are
// TimeConversion rules
TimeChangeRule euCET = {"CET", Fourth, Sun, Mar, 2, +120}; //UTC + 2 hours (in minutes), set your credentials here for daylight saving time
TimeChangeRule euCEST = {"CEST", Fourth, Sun, Oct, 2, +60}; //UTC + 1 hours (in minutes), set your credentials here for standard time
//###################################################################################################
// Set web server port number to 80 to recieve the datastring from Weatherstation
WiFiServer server(80);
bool messagearrived ;
// Variable to store the HTTP request
String header;
const char* headerKeys[] = {"date","server"};
const size_t numberOfHeaders = 2 ;
// Variables to store the values
float values[19];
String date; // hold the date
time_t t; //hold time
// Conversion functions
float F2C (float F){ //Fahrenheit to Celsius
float result;
result = F - 32;
result = result / 1.8;
return result;
}
float CP (float p){ //convert Pressure to hPa
float result;
result = p * 33864 / 1000;
return result;
}
String CT (String date){ //Convert Time from weatherstation
char tdate[10], ttime[8];
String dateout="";
String Year, Month, Day, Hour, Minute, Second ;
time_t central, utc;
Timezone euCentral(euCET, euCEST);
Year = date.substring(0,4);
Month = date.substring (5,7);
Day = date.substring(8,10);
Hour = date.substring(13,15);
Minute = date.substring(16,18);
Second = date.substring(19,21);
setTime(Hour.toInt(),Minute.toInt(),Second.toInt(),Day.toInt(),Month.toInt(),Year.toInt()); // set system time to what came from weatherstation
utc = now();
central = euCentral.toLocal(utc); // change time as declared in rules
setTime(central); // set system time to converted local time
sprintf(tdate, "%02u.%02u.%4u",day(),month(),year()); //leading zero !
sprintf(ttime, "%02u:%02u:%02u",hour(),minute(),second()); // leading zero !!
for (int i = 0; i <= 9; i++) { //put date in dateout-string
dateout += tdate[i];
}
dateout += "%20"; // put (url)space in dateout-string
for (int i = 0; i <= 7; i++) { // put time in dateout-string
dateout += ttime[i];
}
Serial.println("Local datetime: " + dateout);
return dateout;
}
// Current time for timeout
unsigned long currentTime = millis();
// Previous time for timeout
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
messagearrived = true;
//disassemble message
String message = ""; // make a String to hold pure message
String singlevalue; // make a string to hold single value
int j = 0; // counter for Arryindex
message = header.substring(0,header.indexOf("&softwaretype")); // cut the string after the interesting data
//loop to extract the single values
for (int i = 0; i <= message.length(); i++) {
if (message.charAt(i) == '&'){
singlevalue = message.substring(i+1,message.indexOf('&',i+1));
singlevalue = singlevalue.substring(singlevalue.indexOf('=',0)+1);
if (j < 18){
values[j] = singlevalue.toFloat();
}
if (j == 18){ // 2020-02-18%2012:22:53 steckt in "singlevalue"
date = CT(singlevalue);
}
j++;
}
}
// Display a HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head></head>");
// Web Page Heading
client.println("<body><h1>Weather Station Parser</h1>");
// Display current version
client.println("<p>Ver.: 0.1.1</p>");
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
if (messagearrived) {
//Build REST String for ioBroker RESTful simple Adapter
String restmessage = "";
restmessage = IPP;
restmessage += "/setBulk?";
restmessage += DP + "Innentemperatur=" + String (F2C(values[1])) + "&"; //indoortempf
restmessage += DP + "Aussentemperatur=" + String (F2C(values[2])) + "&"; //tempf
restmessage += DP + "Taupunkt=" + String (F2C(values[3])) + "&"; //dewptf
restmessage += DP + "Chillfaktor=" + String (F2C(values[4])) + "&"; //windchillf
restmessage += DP + "Innenfeuchtigkeit=" + String (values[5]) + "&"; //indoorhumidity
restmessage += DP + "Aussenfeuchtigkeit=" + String (values[6]) + "&"; //humidity
restmessage += DP + "Wind=" + String (1.61*(values[7])) + "&"; //windspeedmph
restmessage += DP + "Wind_max=" + String (1.61*(values[8])) + "&"; //windgustmph
restmessage += DP + "Windrichtung=" + String (values[9]) + "&"; //winddir
restmessage += DP + "Druck_absolut=" + String (CP(values[10])) + "&"; //absbaromin
restmessage += DP + "Druck_relativ=" + String (CP(values[11])) + "&"; //baromin
restmessage += DP + "Regen_aktuell=" + String (25.4*(values[12])) + "&"; //rainin
restmessage += DP + "Regen_Tag=" + String (25.4*values[13]) + "&"; //dailyrainin
restmessage += DP + "Regen_Woche=" + String (25.4*values[14]) + "&"; //weeklyrainin
restmessage += DP + "Regen_Monat=" + String (25.4*values[15]) + "&"; //monthlyrainin
restmessage += DP + "Sonnenstrahlung=" + String (values[16]) + "&"; //solarradiation
restmessage += DP + "UV_Index=" + String (values[17]) + "&"; //UV
restmessage += DP + "Zeitstempel=" + date + "&"; //dateutc
restmessage += "prettyPrint";
Serial.println("Send REST message:");
Serial.println(restmessage);
// initialize http client
HTTPClient http;
// send message
http.begin(restmessage);
// http.collectHeaders(headerKeys, numberOfHeaders), check for errors;
int httpCode = http.GET();
if (httpCode > 0) {
Serial.println("--- successfully sent ---");
}else{
Serial.println("An error occurred sending the request");
}
http.end();
messagearrived = false;
}
}
Zusätzlich zu den Netzwerksettings müssen nun die Konvertierungsregeln eingegeben werden, hier in der EU:
Last: letzte Woche // Hier wurde editiert, da zunächst falscher Eintrag
Sun: Sonntag
Mar/Oct: März/Oktober
2: 02:00 Uhr
+120/+60: CET/CEST in Minuten zur UTC. (Hier im deutschsprachigen Raum).
Wiederum einfach die Werte anpassen, kompillieren, runterladen, fertig.
Ach ja, bei mir kommt beim kompilieren immer eine Warnung, dass die Lib Time-Master möglicherweise nicht kompatibel zum neuen ESP8266 Board ist,
ich hab aber keine Probleme hier...
Du scheinst hier das Leerzeichen rauszuschneiden und dann dem Adapter mitzuteilen, dass jetzt eine UTC im danach eingegebenen Format kommt.
Der ioBroker zeigt dann im entsprechenden Feld die richtige, lokale Zeit an, oder ?
Leider hab ich von Linux, ausser Raspi-Gebastel, nicht viel Ahnung, daher kann ich das Script nicht ganz entziffern...
Wie muss denn der fertige String aussehen, damit das funktioniert ?
Ich beschäftige mich jetzt seit ca. 2 Monaten mit dem ioBroker, habe schon einige Anregungen und Lösungen aus diesem Forum entnommen und habe mich jetzt heute hier angemeldet um vielleicht auch mal eine Lösung beizutragen.
Es geht mal wieder um die Wetterstationen, ich habe mir eine Ventus W830 gekauft, da hier die Diskussion im Gange war und das Shell-Script von @SBorg vorhanden war.
Ich hab das dann eingesetzt und es hat alles gut funktioniert.
Da bei mir der ioBroker aber auf meiner Diskstation in einem Docker Container (und da ist kein bc installiert) läuft , würde der Raspi nur dafür gebraucht und ich brauche den für was anderes.
Also musste eine andere Lösung her.
Nun gibt es diese wunderbaren ESP8266 boards (5 €...)
Langer Rede kurzer Sinn: Kleines Programm drauf, läuft...
Kurz zur Vorgangsweise:
Grundsätzlich setzt das Ganze genau auf die Vorgangsweise von @SBorg auf,
nachzulesen in diesem Thread.
Man braucht genau wie bei der Shell-Script Lösung den RESTful Adapter und die entsprechenden Datenpunkte (anlegen durch das Script von @SBorg , oder manuell).
Es sind oben im Sketch folgende Angaben zu machen:
// Replace with your credentials
const char* ssid = "YOURSSID";
const char* password = "YOURPASSDWORD";
const char* IPP = "http://xxx.xxx.xxx.xxx:8087" ; //Protocol, IP and Port where RESTful Adapter waiting.
String DP="javascript.0.Wetterparser."; // Folder where the final datapoints are
ssid und password : Die WLAN-Settings
IPP: IP des ioBroker-Rechners und Port des RESTful Adapters (default:8087)
DP: Der Ordner wo die Datenpunkte liegen, wenn das Originalscript von SBorg genommen wurde, ist das "javascript.0.Wetterstation."
Ein paar allgemeine Hinweise zumEinrichten der Wetterstation mittels App:
in "path" braucht man statt "/weatherstation/updateweatherstation.php?" nur "/" einzugeben.
Der Port ist im Sketch auf 80 gesetzt, das sollte er auch bleiben, weil das Board auch eine kleine Webseite ausgibt, damit man auf die Schnelle überprüfen kann, ob es da ist und die Browser greifen standardmäßig auf 80 zu.
Also in der App auch auf 80 stellen.
Die Felder Station ID und Station Key: hier kann man irgendwas eingeben (sollte aber nicht leer sein), das Sketch holt nur die Daten aus dem String, diese Einträge werden nicht beachtet.
Nun der Code:
(kann ganz einfach in die Arduino-IDE geklatscht werden, Werte anpassen, kompilieren, downloaden und fertig).
/*********
FatBob
ioBroker Parser for Weatherstation Ventus W830
Ver.: 0.1.0
*********/
// Load Wi-Fi library
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
// Replace with your credentials
const char* ssid = "YOURSSID";
const char* password = "YOURPASSWORD";
const char* IPP = "http://xxx.xxx.xxx.xxx:8087" ; //Protocol, IP and Port where RESTful Adapter waiting.
String DP="javascript.0.Wetterparser."; // Folder where the final datapoints are
// Set web server port number to 80 to recieve the datastring from Weatherstation
WiFiServer server(80);
bool messagearrived ;
// Variable to store the HTTP request
String header;
const char* headerKeys[] = {"date","server"};
const size_t numberOfHeaders = 2 ;
// Variables to store the values
float values[19];
String date; // hold the date
// Conversion functions
float F2C (float F){ //Fahrenheit to Celsius
float result;
result = F - 32;
result = result / 1.8;
return result;
}
float CP (float p){ //convert Pressure to hPa
float result;
result = p * 33864 / 1000;
return result;
}
String CT (String date){ //Convert Time from weatherstation
String dateout="";
String Year, Month, Day, Hour, Minute, Second ;
Year = date.substring(0,4);
Month = date.substring (5,7);
Day = date.substring(8,10);
Hour = date.substring(13,15);
Minute = date.substring(16,18);
Second = date.substring(19,21);
dateout += Day + ".";
dateout += Month + ".";
dateout += Year + "%20";
dateout += Hour + ":";
dateout += Minute + ":";
dateout += Second ;
return dateout;
}
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
messagearrived = true;
//disassemble message
String message = ""; // make a String to hold pure message
String singlevalue; // make a string to hold single value
int j = 0; // counter for Arryindex
message = header.substring(0,header.indexOf("&softwaretype")); // cut the string after the interesting data
//loop to extract the single values
for (int i = 0; i <= message.length(); i++) {
if (message.charAt(i) == '&'){
singlevalue = message.substring(i+1,message.indexOf('&',i+1));
singlevalue = singlevalue.substring(singlevalue.indexOf('=',0)+1);
if (j < 18){
values[j] = singlevalue.toFloat();
}
if (j == 18){ // 2020-02-18%2012:22:53 steckt in "singlevalue"
date = CT(singlevalue);
}
j++;
}
}
// Display a HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head></head>");
// Web Page Heading
client.println("<body><h1>Weather Station Parser</h1>");
// Display current version
client.println("<p>Ver.: 0.1.0</p>");
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
if (messagearrived) {
//Build REST String for ioBroker RESTful simple Adapter
String restmessage = "";
restmessage = IPP;
restmessage += "/setBulk?";
restmessage += DP + "Innentemperatur=" + String (F2C(values[1])) + "&"; //indoortempf
restmessage += DP + "Aussentemperatur=" + String (F2C(values[2])) + "&"; //tempf
restmessage += DP + "Taupunkt=" + String (F2C(values[3])) + "&"; //dewptf
restmessage += DP + "Chillfaktor=" + String (F2C(values[4])) + "&"; //windchillf
restmessage += DP + "Innenfeuchtigkeit=" + String (values[5]) + "&"; //indoorhumidity
restmessage += DP + "Aussenfeuchtigkeit=" + String (values[6]) + "&"; //humidity
restmessage += DP + "Wind=" + String (1.61*(values[7])) + "&"; //windspeedmph
restmessage += DP + "Wind_max=" + String (1.61*(values[8])) + "&"; //windgustmph
restmessage += DP + "Windrichtung=" + String (values[9]) + "&"; //winddir
restmessage += DP + "Druck_absolut=" + String (CP(values[10])) + "&"; //absbaromin
restmessage += DP + "Druck_relativ=" + String (CP(values[11])) + "&"; //baromin
restmessage += DP + "Regen_aktuell=" + String (25.4*(values[12])) + "&"; //rainin
restmessage += DP + "Regen_Tag=" + String (25.4*values[13]) + "&"; //dailyrainin
restmessage += DP + "Regen_Woche=" + String (25.4*values[14]) + "&"; //weeklyrainin
restmessage += DP + "Regen_Monat=" + String (25.4*values[15]) + "&"; //monthlyrainin
restmessage += DP + "Sonnenstrahlung=" + String (values[16]) + "&"; //solarradiation
restmessage += DP + "UV_Index=" + String (values[17]) + "&"; //UV
restmessage += DP + "Zeitstempel=" + date + "&"; //dateutc
restmessage += "prettyPrint";
Serial.println("Send REST message:");
Serial.println(restmessage);
// initialize http client
HTTPClient http;
// send message
http.begin(restmessage);
// http.collectHeaders(headerKeys, numberOfHeaders), check for errors;
int httpCode = http.GET();
if (httpCode > 0) {
Serial.println("--- successfully sent ---");
}else{
Serial.println("An error occurred sending the request");
}
http.end();
messagearrived = false;
}
}
Ich weiß nicht, ob man sowas "Parser" nennen darf, ich tu es einfach
Das sollte auf jedem ESP8266 Board laufen, ich hab hier das NodeMCU Lua Lolin V3 Module ESP8266 ESP-12E in Verwendung.
Noch etwas:
Mir gefällt die Zeitkonvertierung in dem Sketch auch noch nicht so ganz, da die Wetterstation immer die Greenwich Mean time ausgibt, ich bin aber noch nicht draufgekommen, wie man das anständig konvertiert. Es ist mir aber egal, weil die Daten den richtigen Zeitstempel sowieso erst beim Loggen bekommen.
Überflüssig zu sagen: Der Einsatz des Sketches erfolgt auf eigene Gefahr, es gibt keinerlei Garantie oder Gewährleistung
Wenn Fragen auftauchen, gerne in diesem Thread fragen.