{"id":126,"date":"2019-05-27T15:31:06","date_gmt":"2019-05-27T14:31:06","guid":{"rendered":"http:\/\/wetter.cuprum.de\/?page_id=126"},"modified":"2020-10-12T13:53:23","modified_gmt":"2020-10-12T12:53:23","slug":"126-2","status":"publish","type":"page","link":"http:\/\/wetter.cuprum.de\/?page_id=126","title":{"rendered":"Konzept &#8211; Umsetzung &#8211; Code"},"content":{"rendered":"\n<p><strong>Konzept<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Konzept.jpg\" alt=\"\" class=\"wp-image-148\" width=\"377\" height=\"182\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Konzept.jpg 753w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Konzept-300x145.jpg 300w\" sizes=\"(max-width: 377px) 100vw, 377px\" \/><\/figure>\n\n\n\n<p><strong>Sensoren<\/strong><\/p>\n\n\n\n<ul><li> BME280: Temperatur[\u00b0C], Druck[hPa], relative Feuchte [%] <\/li><li>LDR: Helligkeit, dimensionslos (0..100)<\/li><\/ul>\n\n\n\n<p><strong>Kommunikation<\/strong><\/p>\n\n\n\n<p>Das ESP-Board ist mit dem lokalen WLAN verbunden und \u00fcbertr\u00e4gt die Sensordaten in die Datenbank beim Web-Provider. Dazu wird \u00fcber den Aufruf einer HTML-Seite ein php-Skript ausgef\u00fchrt, das die Datenbank bef\u00fcllt. Der Timestamp des Datensatzes stammt dabei vom DB-Server.<\/p>\n\n\n\n<ul><li> Aktuelle Daten: ein Datensatz je Minute, gemittelt \u00fcber 10 Messwerte, max. 360 Datens\u00e4tze <\/li><li>Wochendaten: ein Datensatz alle 10 Minuten, \u00fcber aktuelle Daten je Minute gemittelt, max. 1008 Datens\u00e4tze (10080min=168h=7d)<\/li><li>Monatsdaten: ein Datensatz je Stunde,  \u00fcber aktuelle Daten gemittelt, max 744 Datens\u00e4tze (365,25d)<\/li><li>Jahresdaten: wie Monatsdaten, aber max. 8766 Datens\u00e4tze<\/li><\/ul>\n\n\n\n<p><strong>Visualisierung<\/strong><\/p>\n\n\n\n<p>Die Daten werden mit php aus der Datenbank ausgelesen und mit jpgraph visualisiert. \u00dcber Woody snippets werden die php-Skripte in WordPress eingebunden.  <\/p>\n\n\n\n<p><strong>Verbindungen<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schema.jpg\" alt=\"\" class=\"wp-image-131\" width=\"317\" height=\"205\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schema.jpg 634w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schema-300x194.jpg 300w\" sizes=\"(max-width: 317px) 100vw, 317px\" \/><\/figure>\n\n\n\n<p><strong>Schaltung v1<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schaltung.jpg\" alt=\"\" class=\"wp-image-133\" width=\"445\" height=\"246\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schaltung.jpg 889w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schaltung-300x166.jpg 300w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Schaltung-768x424.jpg 768w\" sizes=\"(max-width: 445px) 100vw, 445px\" \/><\/figure>\n\n\n\n<p><strong>Schaltung v2<\/strong><\/p>\n\n\n\n<p> Da die Schaltung so einfach ist, habe ich diesmal keine Platine ge\u00e4tzt, sondern direkt auf einer Lochrasterplatine aufgebaut.  <\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/Wetterstation2020_v2.jpg\" alt=\"\" class=\"wp-image-256\" width=\"394\" height=\"381\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/Wetterstation2020_v2.jpg 682w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/Wetterstation2020_v2-300x290.jpg 300w\" sizes=\"(max-width: 394px) 100vw, 394px\" \/><figcaption> <\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_02.jpg\" alt=\"\" class=\"wp-image-258\" width=\"272\" height=\"250\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_02.jpg 440w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_02-300x276.jpg 300w\" sizes=\"(max-width: 272px) 100vw, 272px\" \/><figcaption>Da lag noch ein durchsichtiges Ge\u00e4use in der Bastelkiste. Touch funktioniert auch durch den Deckel hindurch! Ist quick-and-dirty mit Tesa angeklebt. An der Seite und unten sind L\u00f6cher f\u00fcr die Bel\u00fcftung gebohrt.<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_01.jpg\" alt=\"\" class=\"wp-image-257\" width=\"271\" height=\"222\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_01.jpg 528w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_01-300x247.jpg 300w\" sizes=\"(max-width: 271px) 100vw, 271px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_03.jpg\" alt=\"\" class=\"wp-image-259\" width=\"273\" height=\"204\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_03.jpg 640w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2020\/10\/wsv2_03-300x225.jpg 300w\" sizes=\"(max-width: 273px) 100vw, 273px\" \/><\/figure>\n\n\n\n<p><strong>Programmierung<\/strong><\/p>\n\n\n\n<ul><li>Arduino-IDE f\u00fcr ESP2866 <\/li><li>Brackets f\u00fcr php<\/li><li>WordPress<\/li><\/ul>\n\n\n\n<p><strong>Bauteile<\/strong><\/p>\n\n\n\n<ul><li>ESP2866<\/li><li>BME280<\/li><li>LDR + Widerstand 1-10k, je nach LDR. Unkritisch, da keine physikalische Gr\u00f6sse gemessen werden soll.<\/li><li>OLED-Display 128 x 64<\/li><li>Touch-Board oder Taster (Display an\/aus, mit 3min Display-TimeOut)<\/li><li>Platine oder &#8222;fliegende Verkabelung&#8220;<\/li><li>Geh\u00e4use<\/li><li>USB-Kabel<\/li><li>USB-Netzteil<\/li><\/ul>\n\n\n\n<p><strong>Display<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img decoding=\"async\" loading=\"lazy\" src=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Display.jpg\" alt=\"\" class=\"wp-image-194\" width=\"228\" height=\"119\" srcset=\"http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Display.jpg 667w, http:\/\/wetter.cuprum.de\/wp-content\/uploads\/2019\/05\/Display-300x157.jpg 300w\" sizes=\"(max-width: 228px) 100vw, 228px\" \/><figcaption>OLED-Display mit 128 x 64 Pixel.  F\u00fcr die Ausgabe der Sensordaten w\u00e4re ein OLED-Display mit 128 x 32 Pixel ausreichend.<\/figcaption><\/figure>\n\n\n\n<p><strong>Skripte &amp; Code: <\/strong><\/p>\n\n\n\n<p>Vielleicht sind die folgenden Skripte f\u00fcr den einen oder anderen hilfreich &#8211; Lesen und Verwendung auf eigene Gefahr! :o) Hinweise und Anmerkungen gerne an mich. Bei den mysql und php-Skripten bitte keinen Herzanfall bekommen.<\/p>\n\n\n\n<p><strong>Arduino<\/strong><\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">Wetterstation2.07.ino<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/******************************************************\nWetterstation mit ESP2866, BME280, LDR, OLED-Display 128x64\nSensoren f\u00fcr Temperatur, Druck, relative Feuchtigkeit und Helligkeit (dimensionslos).\nBM_E_280 hat zus\u00e4tzlich einen Feuchtesensor, BM_P_280 nicht.\n\nDie gesammelten Daten werden via http-GET an php-Skript auf dem Webserver geschickt. \nDas Skript legt die Daten in einer  mysql-DB in verschiedenen Tabellen ab.\nTimestamp stammt vom Server.\n\nDaten           Tabelle    max.  Datens\u00e4tze\nAktuelle Daten  h6_1min        360   6h x 60min\nMonatsdaten     monat_1h       744  31d x 24h\nWochendaten     woche_10min   1008   7d x 24h x 6\nJahresdaten     jahr_1h       8766  365,25d x 24h\n5-Jahresdaten   jahre5_1h    43830  5 x 365,25d x 24h\n\nWemos-D1-Board D1:SCL(GPIO5), D2:SDA(GPIO4) oder Wire.begin(int sda, int scl)\nD5: Touch-Taste f\u00fcr Display on\/off, Timeout f\u00fcr Abschaltung\nGND---LDR---R---3V3   U=3V3*LDR\/(LDR+R)\n       |      Board hat Spannungsteiler von A0 auf ADC von 3V3 auf 1V1 \n       A0     R an LDR-Werte anpassen, unkritisch. R~Wurzel(LDRmin*LDRmax)\n\n(ca. 950..1060hPa min\/max in DL)\n******************************************************\/\n\n#define Version \"Wetterstation_2.07\"\n\n\/\/===== Eintragen! =======================================================\nchar *ssid = \"\";      \/\/ SSID des lokalen WLAN\nchar *password = \"\";  \/\/ Passwort -\"-\nString DBKEY = \"\";    \/\/ Schl\u00fcssel zum \u00dcbertragen der Daten zum Server.\n                      \/\/ Muss mit $dbkey in tab_insert.php \u00fcbereinstimmen \n\/\/========================================================================\n\n#include &lt;Wire.h>\n#include \"SparkFunBME280.h\"\n#include &lt;ESP8266WiFi.h>\n#include &lt;WiFiClient.h>\n\n\/\/#include &lt;ArduinoOTA.h>     \/\/ OTA-Handling, darf nicht auskommentiert werden, sonst wirft es Fehler\n#include \"SSD1306Ascii.h\"   \/\/ Display\n#include \"SSD1306AsciiWire.h\"\n\n#define headerString \"Uptime \\tT[\u00b0C] \\tP[hPa] \\tRH[%] \\tLDR\" \/\/F\u00fcr serielle Ausgabe\n\nWiFiClient client;\n\nBME280 BME;\n\nchar   strBuf[20];\nString s, hs;\ndouble BME_p, BME_T, BME_RH, LDR;\ndouble st, sp, sf, sl, sth, sph, sfh, slh; \ndouble mint, minp, minf, minl, maxt, maxp, maxf, maxl;\nint hi, mi;\nchar TAB = 0x09;\n\n\/\/----- Display -----\n#define DISPLAY_ADDRESS 0x3C    \/\/0x3C or 0x3D\nSSD1306AsciiWire oled;\nconst int TouchPin = 14;      \/\/f\u00fcr Touch-Taster an D5\nconst int OLED_TimeOut = 3 * 60; \/\/TimeOut f\u00fcr OLED-Display in Sekunden. \nint OLED_Timer = OLED_TimeOut;\nvolatile boolean Display_on = true;\n\/\/-------------------\n\n\/\/===== structs =============================================\nstruct SensorData {\n  double t, p, f, l;\n  String ts, ps, fs, ls;\n};\n\nSensorData sensors; \/\/Globale Variable, um mitteln zu k\u00f6nnen\n\n\/\/===========================================================\n\n\nvoid php_tab_insert (String tabname, int maxnum) {\n  \/\/aktuelle Werte an Tabelle tabname anh\u00e4ngen, falls mehr als maxnum Datens\u00e4tze vorhanden,\n  \/\/werden die ersten Datens\u00e4tze entfernt bis L\u00e4nge stimmt.\n  String server = \"cuprum.de\";  \/\/Als Konstant ganz nach oben ziehen!\n  String s;\n  if (client.connect(server, 80)) {\n    Serial.println(\"connecting...\");\n    \/\/ send the HTTP PUT request:\n    \/\/ GET \/wetter\/tab_insert.php?tabelle=TABNAME&amp;max=MAXNUM&amp;key=xxxx&amp;temperatur=23.29&amp;druck=998.96&amp;feuchte=33.22&amp;licht=90.00\n    String s = \"GET \/wetter\/tab_insert.php?tabelle=\" + tabname + \"&amp;max=\";\n    s += maxnum; \n    s += \"key=\";  \/\/key\n    s += DBKEY;\n    \/\/Daten aus globaler Variable sensors holen\n    s += \"&amp;temperatur=\";\n    s += sensors.t;\n    s += \"&amp;druck=\";\n    s += sensors.p;\n    s += \"&amp;feuchte=\";\n    s += sensors.f;\n    s += \"&amp;licht=\";\n    s += sensors.l;\n    \n    Serial.println(\"tab_insert\");\n    Serial.println(s);\n\n    client.print(s);\n    client.println(\" HTTP\/1.1\");\n    client.print(\"Host: \");\n    client.println(server);\n    client.println(\"Connection: close\");\n    client.println();\n\n    while (client.available()) { \/\/unn\u00f6tig?\n      char c = client.read();\n      Serial.write(c);\n      \/\/s = client.read();\n      \/\/Serial.println (c);\n    }\n    client.stop();\n  }\n  else {\n    \/\/ if you couldn't make a connection:\n    Serial.println(\"Verbindung gescheitert.\");\n    Serial.println(\"Trennung.\");\n    client.stop();\n  }\n} \/\/php_tab_insert\n\nICACHE_RAM_ATTR void handleTouchInterrupt(){ \n\/\/Hatte vorher ohne ICACHE_RAM_ATTR funktioniert, jetzt Fehlermeldung\n  Display_on = !Display_on;\n  if (Display_on == true){\n    Serial.println(\"Display EIN\");\n    OLED_Timer = millis();\n    oled.clear();\n    oled.println (\"Display EIN\");\n  }\n  else{oled.clear();\n    }\n}\n\nvoid setminmax (double v, double &amp;minv, double &amp;maxv){\n   if (v &lt; minv){minv = v;}\n   if (v > maxv){maxv = v;}\n} \/\/setminmax\n\n\/\/=== Setup =======================================================================\nvoid setup() {\n\n  Serial.begin(115200);\n  Serial.print (\"Version: \"); Serial.println (Version);\n\n  \/\/--- OLED-Display ---\n  Wire.begin ();\n  Wire.setClock(100000);\n  oled.begin(&amp;Adafruit128x64, DISPLAY_ADDRESS);\n  \/\/oled.set400kHz();\n  oled.setFont(Callibri15); \/\/Aus Adafruit-Library\n  oled.clear();\n  oled.setContrast (100);\n  oled.println(\"Version: \"); oled.println (Version);\n  oled.println(\"Mit WLAN verbinden.\");\n\n  \/\/--- Touch-Pin ---\n  pinMode(TouchPin, INPUT);\n  attachInterrupt(digitalPinToInterrupt(TouchPin), handleTouchInterrupt, RISING);\n  \/\/--- WLAN ---\n  WiFi.mode(WIFI_STA);\n  WiFi.begin(ssid, password);\n  \/\/Verbindung zum WLAN aufbauen\n  int counter = 0; \/\/Counter f\u00fcr Timeout\n  while (WiFi.status() != WL_CONNECTED) {\n    delay (250);\n    Serial.print(\".\");\n    oled.print(\".\");\n    counter++;\n    if (counter > 100) {\n      Serial.println(\"Kein WLAN. Restart!\");\n      ESP.restart();\n    }\n  }\n  Serial.print(\"Connected to \");\n  Serial.println(ssid);\n  Serial.print(\"IP address: \");\n  Serial.println(WiFi.localIP());\n  oled.println(WiFi.localIP());\n\n  \/\/----- BME280-Settings -----\n  BME.settings.commInterface = I2C_MODE;\n  BME.settings.I2CAddress = 0x76;  \/\/oder 0x77\n  BME.settings.runMode = 3; \/\/Normal mode\n  BME.settings.tStandby = 0;\n  BME.settings.filter = 2;\n  BME.settings.tempOverSample = 1;\n  BME.settings.pressOverSample = 1;\n  delay(10); \/\/F\u00fcr Startup BMP280\n  \n  Serial.print (\"BME280...\");\n  if (!BME.begin()) {\n    Serial.println(\"nicht gefunden!\");\n  }\n  else {\n    Serial.println(\"OK.\");\n  }\n  for (int i = 0; i &lt; 5; i++) { \/\/die ersten f\u00fcnf Werte verwerfen\n    BME_p  = BME.readFloatPressure() \/ 100; \/\/ [hPa]\n    BME_T  = BME.readTempC();\n    BME_RH = BME.readFloatHumidity(); \/\/hat BMP nicht\n  }\n\n  \/\/----- Header -----\n  Serial.println(\"\");\n  Serial.println(headerString);\n\n  oled.clear();\n\n  st = 0; sp = 0; sf = 0; sl = 0;\n  sth = 0; sph = 0; sfh = 0; slh = 0;\n  hi = 0; mi = 0;\n\n  mint =  100; minp = 2000; minf = 101; minl = 101;\n  maxt = -100; maxp =    0; maxf =   0; maxl =   0;\n  \n} \/\/setup\n\n\/\/=== Loop =======================================================================\nvoid loop() {\n  int sec;\n  int osec;\n  sec = millis() \/ 1000;\n  osec = sec;\n  while (osec == sec) {\n    sec = millis() \/ 1000;  \/\/auf neue Sekunde warten\n    yield();\n  }\n  sec = millis() \/ 1000;\n\n  if ((sec % 60) == 0) {    \/\/1 x pro Minute\n    int ms0 = millis();\n    \/\/--- Mitteln ---\n    float v;\n    v = BME.readTempC();                st += v; sth += v; \/\/setminmax (v, mint, maxt);\n    v = BME.readFloatPressure() \/ 100;  sp += v; sph += v; \/\/setminmax (v, minp, maxp);\n    v = BME.readFloatHumidity();        sf += v; sfh += v; \/\/setminmax (v, minf, maxf);\n    v = getLight();                     sl += v; slh += v; \/\/setminmax (v, minl, maxl);\n    mi += 1;\n    hi += 1;\n    \/\/--------------\n    getSensorData();\n    php_tab_insert (\"h6_1min\", 360); \/\/Daten an Server senden\n    ms0 = millis() - ms0;\n  }\n  if ((sec % 600) == 0) { \/\/Alle 10 Minuten\n    int ms0 = millis();\n    sensors.t = st\/mi;    \/\/Gemittelte Daten, statt getSensorData()\n    sensors.p = sp\/mi;\n    sensors.f = sf\/mi;\n    sensors.l = sl\/mi;\n    php_tab_insert (\"woche_10min\", 1008); \/\/Daten an Server senden\n    st = 0; sp = 0; sf = 0; sl = 0;\n    mi = 0;\n    ms0 = millis() - ms0;\n    Serial.print (ms0);\n    Serial.println (\" ms\");\n  }\n\n  if ((sec % 3600) == 0) { \/\/Alle 60 Minuten\n    int ms0 = millis();\n    sensors.t = sth\/hi;    \/\/Gemittelte Daten, statt getSensorData()\n    sensors.p = sph\/hi;\n    sensors.f = sfh\/hi;\n    sensors.l = slh\/hi;\n    \/\/Daten an Server senden\n    php_tab_insert (\"monat_1h\", 744);    \/\/Diese drei Aufrufe k\u00f6nnten ggf. in ein PHP-Skript\n    php_tab_insert (\"jahr_1h\", 8766);\n    php_tab_insert (\"jahre5_1h\", 43830); \n    sth = 0; sph = 0; sfh = 0; slh = 0;\n    hi = 0;\n    ms0 = millis() - ms0;\n    Serial.print (ms0);\n    Serial.println (\" ms\");\n  }\n\n  if ((sec % 2) == 0) {   \/\/Alle zwei Sekunden Werte seriell ausgeben\n    s = getSensorString();  \/\/ f\u00fcr Ausgabe auf Display\n    Serial.print (millis() \/ 1000);\n    Serial.print (\" \");\n    Serial.println(s);\n    \/\/-----  Display ---\n    BME_p  = BME.readFloatPressure() \/ 100; \/\/ [hPa]\n    BME_T  = BME.readTempC();\n    BME_RH = BME.readFloatHumidity();\n    LDR    = getLight();\n\n    if (millis() > OLED_Timer + 1000*OLED_TimeOut){Display_on = false; oled.clear();}\n    \n    if  (Display_on == true) {\n      int i;\n      char c[10];\n      oled.home();\n      dtostrf(BME_T, 5, 1, c);\n      oled.print(c); oled.print(\" C    \");\n      dtostrf(BME_RH, 2, 0, c);\n      oled.print(c); oled.print(\" %RH  \");\n      oled.println(); \/\/ oled.clearToEOL();\n\n      dtostrf(BME_p, 4, 0, c);\n      oled.print(c); oled.print(\" hPa \");\n      dtostrf(LDR, 4, 1, c);\n      oled.print(c); oled.print(\" L  \");\n      oled.println();\n\n      oled.println(millis() \/ 1000); oled.print(\" \");\n      oled.print(WiFi.localIP());\n      oled.print(\" D:\"); \n      if ((OLED_Timer + 1000*OLED_TimeOut - millis())\/1000 &lt; OLED_TimeOut){\n        oled.print((OLED_Timer + 1000*OLED_TimeOut - millis())\/1000);}\n      oled.println(\"    \");\n    }\n    \/\/------------------\n  }\n} \/\/end loop\n\nString getSensorString () {\n  String s;\n  \/\/msh = millis(); Serial.print (\"Sensoren \");\n  double t, p, f, l;\n  t = 0; p = 0; f = 0; l = 0;\n  int num_samples = 10; \/\/ ~ 43ms\n  for (int i = 0; i &lt; num_samples; i++) {\n    p += BME.readFloatPressure() \/ 100; \/\/ [hPa]\n    t += BME.readTempC();\n    f += BME.readFloatHumidity();\n    l += getLight();\n    \/\/yield();\n  }\n  t = t \/ num_samples;\n  p = p \/ num_samples;\n  f = f \/ num_samples;\n  l = l \/ num_samples;    \n  s  = dtostrf(t, 4, 1, strBuf);  \/\/s = formatSensorString(t, 6, 1);\n  s += TAB;\n  s += dtostrf(p, 6, 1, strBuf);\n  s += TAB;\n  s += dtostrf(f, 4, 1, strBuf);\n  s += TAB;\n  s += dtostrf(l, 4, 1, strBuf);\n  s.replace(\".\", \",\");\n  return s;\n} \/\/getSensorString\n\nfloat getLight (){\n  float l = (1024 - analogRead(A0))\/10.23;   \/\/auf 0..100 normieren \n  return l;\n}\n\nvoid getSensorData () {\n  \/\/Werte in globale Variable sensors speichern\n  sensors.t = BME.readTempC();\n  sensors.p = BME.readFloatPressure() \/ 100; \/\/ [hPa]\n  sensors.f = BME.readFloatHumidity();\n  sensors.l = getLight();\n  String s;\n  s = dtostrf(sensors.t, 5, 1, strBuf); s.replace(\".\", \",\");\n  sensors.fs = s;\n  s = dtostrf(sensors.p, 5, 1, strBuf); s.replace(\".\", \",\");\n  sensors.ps = s;\n  s = dtostrf(sensors.f, 2, 0, strBuf); s.replace(\".\", \",\");\n  sensors.fs = s;\n  s = dtostrf(sensors.l, 5, 1, strBuf); s.replace(\".\", \",\");\n  sensors.ls = s;\n} \/\/getSensorData\n<\/code><\/pre>\n\n\n\n<p><strong>PHP (Serverscripte)<\/strong><\/p>\n\n\n\n<p>Als absoluter Neuling in PHP, mysql und WordPress, musste ich einiges &#8222;zusammenbasteln&#8220;, um es ans Laufen zu bringen.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\"><strong>db_header.php<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\n\/*\nAlle Variablen testen?\nif (isset($_GET['aid']) &amp;&amp; is_numeric($_GET['aid'])) {\n    $aid = (int) $_GET['aid'];\n} else {\n    $aid = 1;\n}\n== \n$aid = $_GET['aid']) ?? 1;\n*\/\n\n$LF = \"&lt;br \\>\";\n\n\/\/----- in include file verlagern -----\n$servername = \"\"; \/\/Anpassen!\n$username   = \"\";\n$password   = \"\";\n$dbname     = \"\"; \/\/muss mit DBKEY aus dem Arduino-Skript \u00fcbereinstimmen\n\n$mysqli = new mysqli(\"$servername\", \"$username\", \"$password\", \"$dbname\");\n\/\/ Check connection\nif ($mysqli->connect_errno) {\n    echo \"Error: MySQL connection failed:\" . $LF;\n    echo \"Errno: [\" . $mysqli->connect_errno . \"]\" . $LF;\n    echo \"Error: [\" . $mysqli->connect_error . \"]\" . $LF;\n    exit;\n}\n\n\/\/echo \"Connection OK.\" . $LF;\n\nfunction sql_query ($mysqli, $query){\n\tif (!$result = $mysqli->query($query)) {\n    \t\techo \"Error: Query failed to execute:\";\n            echo $LF;\n    \t\techo \"Query: [\" . $query . \"]\" . $LF;\n    \t\techo \"Errno: [\" . $mysqli->errno . \"]\" . $LF;\n    \t\techo \"Error: [\" . $mysqli->error . \"]\" . $LF;\n    \t\techo mysqli_errno ();\n            echo $LF;\n    \t\texit;\n\t\t}\n    return $result;\n} \/\/sql_query \n\nmysqli_select_db ($dbname);\n\n?><\/code><\/pre>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\"><strong>tab_insert.php<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\n\/\/----- in include file verlagern -----\n\/\/bzw. include 'db_header.php'; weiter oben\n$dbkey = 'xxx'; \/\/muss mit DBKEY aus dem Arduino-Skript \u00fcbereinstimmen\n$LF = \"&lt;br \\>\";\n$datum = date (\"Y-m-d H:i:s\", time());\necho $datum . $LF;\n\n$tabelle =  $_GET['tabelle'];\n$maxcount = $_GET['max'];\n\n$key = $_GET['key'];\nif ($key &lt;> $dbkey){\n    echo \"Key ung\u00fcltig\";\n    exit();} \/\/Script beenden, falls key falsch ist\n\n$temperatur = $_GET['temperatur'];\n$druck = $_GET['druck'];\n$feuchte = $_GET['feuchte'];\n$licht = $_GET['licht'];\necho \"$temperatur \u00b0C\" . $LF;\necho \"$druck hPa \" . $LF;\necho \"$feuchte %RH \" . $LF;\necho \"$licht L \" . $LF;\n?>\n\n&lt;?php\ninclude 'db_header.php';\n\/\/===== Werte eintragen =====\n$query = \"INSERT INTO \" . $tabelle . \" (Date, T, p, RH, L) VALUES ('$datum', '$temperatur', '$druck', '$feuchte', '$licht')\";\necho $sql . $LF;\n$result = sql_query ($mysqli, $query);\n\n\/\/************\n\/\/schenller als \"SELECT * FROM ..\"\n$query = \"SELECT COUNT(*) AS anzahl FROM \" . $tabelle;\necho $sql . $LF;\n$result = sql_query ($mysqli, $query);\n$row = $result->fetch_assoc();\n$count = $row['anzahl'];\necho $count . \" Datens\u00e4tze\" . $LF;\n\/\/************\n\n\n\/\/----- falls mehr als $maxcount Eintr\u00e4ge dann erste raus -----\n\/\/langsam und umst\u00e4ndlich\necho \"Erstes raus: \" . $LF;\n$query = \"SELECT * FROM \" . $tabelle . \" ORDER BY DATE\";\n$result = sql_query ($mysqli, $query);\n$count = $result->num_rows;\n$geloescht = 0;\n\n\/* \n\/\/ungef\u00e4hr so? PDO??? benutzen \n$sql = \"DELETE FROM \" . $tabelle . \" WHERE Date = '\" . $row['Date'] . \"'\";\n$result = $conn->query ($sql);\nwhile count > maxcount do begin\n    $row = $result->fetch_assoc();\n    $sql = \"DELETE FROM \" . $tabelle . \" WHERE Date = '\" . $row['Date'] . \"'\";\n    $result_x = $conn->query ($sql); \/\/result und result_x gleichzeitig m\u00f6glich ?\n    $count = $count - 1;\nend\n\n*\/\n\necho \"max: \" . $maxcount . $LF;\necho \"count: \" . $count . $LF;\nwhile ($count > $maxcount){\n$row = $result->fetch_assoc();\nif ($count > $maxcount){\n        $geloescht = $geloescht + 1;\n        $query = \"DELETE FROM \" . $tabelle . \" WHERE Date = '\" . $row['Date'] . \"'\";\n        $result = sql_query ($mysqli, $query);\n        \/\/print_r($result); echo \"&lt;br \\>\";\n\n        $query = \"SELECT * FROM \" . $tabelle . \" ORDER BY DATE\";\n        $result = sql_query ($mysqli, $query);\n        $count = $result->num_rows;\n        echo $count . \" \";\n    }\n}\n\necho \"&lt;br \\>\"; \necho \"Gel\u00f6schte Dates\u00e4tze: \" . $geloescht  . \"&lt;br \\>\";\n$result->free();\n$mysqli->close();\n\/\/===================================\n\n$conn->close();\n\necho \"Ende\";  \n?><\/code><\/pre>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\"><strong>PHP-Script f\u00fcr die graphische Darstellung,  \u00fcber Woody snippets direkt in WordPress eingebunden<\/strong>. <\/p>\n\n\n\n<p>Auf dem Webserver  muss  jpgraph  installiert werden. Ich habe noch keine M\u00f6glichkeiten gefunden die Achsen mit Datumsangaben zu beschriften.<\/p>\n\n\n\n<p>Aufruf \u00fcber den Shortcode wbcr_php_snippet id=&#8220;##&#8220; tabelle=&#8220;Tabellenname&#8220;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/tab_graph\n\/\/$LF = \"&lt;br \\>\";\n\ninclude (\"jpgraph\/src\/jpgraph.php\"); \ninclude (\"jpgraph\/src\/jpgraph_bar.php\"); \ninclude (\"jpgraph\/src\/jpgraph_line.php\"); \n\ninclude 'meine\/db_header.php';\n\n$TempKorr = 0.0; \/\/ !!!!!\n\n$query = \"SELECT * FROM \" . $tabelle;\n$result = sql_query ($mysqli, $query);\n\nif ($result->num_rows === 0) {\n    echo \"ID $aid nicht gefunden\";\n    exit;\n}\n\n$count = $result->num_rows;\n\/\/echo $count; echo \" Datens\u00e4tze\" . $LF; \n\nwhile ($row = $result->fetch_row()) {$rows[] = $row;}\n\n\/\/echo microtime($asfloat) - $ms . \" s\" . $LF;\n\n\/\/Datens\u00e4tze in einzelne Array umsortieren\n$num = $count;\n$datax = array();\n$datayl = array();\n$datayp = array();\n$datayf = array();\n$datayt = array();\n$numdatashow = 0;\nif ($numdatashow > 0){echo \"Ausgabe:\" . $LF;}\nfor ($i=0;$i&lt;$num;$i++){\n    if ($i &lt; $numdatashow) {echo $i . \"  \"; }\n    for ($j=0; $j &lt; 5; $j++){\n\t\tif ($i &lt; $numdatashow) {echo $rows[$i][$j]; echo \"  \";} \n        if ($j == 0){$y = $rows[$i][$j]; array_push($datax, strtotime($y)\/60); \/\/Minuten\n                    \/\/echo $i . \" \" . $y\/60 . $LF;\n                    } \/\/In Minuten\n        if ($j == 1){$y = $rows[$i][$j]; array_push($datayt, $y);}\n        if ($j == 2){$y = $rows[$i][$j]; array_push($datayp, $y);}\n        if ($j == 3){$y = $rows[$i][$j]; array_push($datayf, $y);}\n        if ($j == 4){$y = $rows[$i][$j]; array_push($datayl, $y);}\n\t}\n\tif ($i &lt; $numdatashow) {echo $LF;}\n}\n\n\/\/Minimum von allen x-Werten (Timestamp) suchen und abziehen\n$minx = min($datax);\nfor ($i = 0; $i &lt; $num; $i++) {$datax[$i] = $datax[$i] - $minx;}\n\n\/\/Temperatur korrigieren  + $TempKorr\nfor ($i = 0; $i &lt; $num; $i++) {$datayt[$i] = $datayt[$i] + $TempKorr;}\n\n$minx = min($datax);  $maxx = max($datax);\n$mint = min($datayt); $maxt = max($datayt);\n$minp = min($datayp); $maxp = max($datayp);\n$minf = min($datayf); $maxf = max($datayf);\n$minl = min($datayl); $maxl = max($datayl);\n\necho \"Letzte Werte:\" . $LF;\necho \"&lt;table>\";\necho \"&lt;thead>&lt;tr>&lt;th>T[\u00b0C]&lt;\/th>&lt;th>p[hPa]&lt;\/th>&lt;th>F[%RH]&lt;\/th>&lt;th>L[]&lt;\/th>&lt;\/tr>&lt;\/thead>\";\necho \"&lt;tbody>\";\necho \"&lt;tr>\";\n$i = intval(10*$datayt[$num-1]); echo '&lt;th>' . $i\/10 . '&lt;\/th>';\n$i = intval(10*$datayp[$num-1]); echo '&lt;th>' . $i\/10 . '&lt;\/th>';\n$i = intval(10*$datayf[$num-1]); echo '&lt;th>' . $i\/10 . '&lt;\/th>';\n$i = intval(10*$datayl[$num-1]); echo '&lt;th>' . $i\/10 . '&lt;\/th>';\necho \"&lt;\/tr>\\n\";\necho \"&lt;\/tbody>\";\necho \"&lt;\/table>\";\n\nswitch ($tabelle){\n    case \"h6_1min\":     $xtitle = \"t[min]\";    break;\n    case \"woche_10min\": $xtitle = \"t[10min]\";  break;\n    case \"monat_1h\":    $xtitle = \"t[h]\";      break;\n    case \"jahr_1h\":     $xtitle = \"t[h]\";      break;\n    case \"jahre5_1h\":   $xtitle = \"t[h]\";      break;\n}\n\nfunction graph_plot($w, $h, $gtitle, $xtitle, $ytitle, $datay){\n    $graph = new Graph($w, $h, 0); \/\/($w, $h, \"auto\");\n    $graph->SetScale(\"intint\");\n\t$graph->title->Set($gtitle);\n    $graph->xaxis->SetTitle($xtitle); \n    $graph->xaxis->SetTitleSide(\"SIDE_TOP\");\n    $graph->xaxis->SetTitleMargin(-8);     \n    $graph->yaxis->SetTitle($ytitle, \"high\"); \n    $graph->yaxis->SetTitleSide(\"SIDE_RIGHT\");\n    $graph->yaxis->SetTitleMargin(12);\n    $bplott = new LinePlot($datay);\n    $graph->Add($bplott);\n    $pngname = $gtitle . \".png\";\n    $graph->Stroke($pngname); \n    echo \"&lt;img src='\" . $pngname . \"'>\" . $LF; \n}\n\ngraph_plot (800, 200, \"Temperatur\", $xtitle, \"T[\u00b0C]\", $datayt);\ngraph_plot (800, 200, \"Druck\", $xtitle, \"p[hPa]\", $datayp);\ngraph_plot (800, 200, \"Feuchte\", $xtitle, \"RH[%]\", $datayf);\ngraph_plot (800, 200, \"Helligkeit\", $xtitle, \"E[]\", $datayl);\n\n\/\/echo microtime($asfloat) - $ms . \" s\" . $LF;\n\n$result->free();\n$mysqli->close();\n\n?><\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Konzept Sensoren BME280: Temperatur[\u00b0C], Druck[hPa], relative Feuchte [%] LDR: Helligkeit, dimensionslos (0..100) Kommunikation Das ESP-Board ist mit dem lokalen WLAN verbunden und \u00fcbertr\u00e4gt die Sensordaten in die Datenbank beim Web-Provider. Dazu wird \u00fcber den Aufruf einer HTML-Seite ein php-Skript ausgef\u00fchrt, das die Datenbank bef\u00fcllt. Der Timestamp des Datensatzes stammt dabei vom DB-Server. Aktuelle Daten: ein &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/wetter.cuprum.de\/?page_id=126\" class=\"more-link\"><span class=\"screen-reader-text\">\u201eKonzept &#8211; Umsetzung &#8211; Code\u201c<\/span> weiterlesen<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":2,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":[],"_links":{"self":[{"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/pages\/126"}],"collection":[{"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=126"}],"version-history":[{"count":61,"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/pages\/126\/revisions"}],"predecessor-version":[{"id":283,"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/pages\/126\/revisions\/283"}],"up":[{"embeddable":true,"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=\/wp\/v2\/pages\/2"}],"wp:attachment":[{"href":"http:\/\/wetter.cuprum.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}