Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CCU

Problemlösungen und Hinweise von allgemeinem Interesse zur Haussteuerung mit HomeMatic

Moderator: Co-Administratoren

Ist der Beitrag nützlich

nicht relevant
1
4%
nein
0
Keine Stimmen
ja
18
69%
bitte weiterentwickeln (bitte als Kommentar angeben was weiterentwickelt/korrigiert werden soll)
7
27%
 
Abstimmungen insgesamt: 26

fsommer1968
Beiträge: 230
Registriert: 16.02.2008, 17:05
Danksagung erhalten: 9 Mal

Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CCU

Beitrag von fsommer1968 » 05.06.2017, 19:55

Ergänzungen stehen immer an Ende dieses Beitrags
Hallo Homematic und ESP User,

ich habe einen Arduino Sketch für ESP 8266 gebaut, der die gängigen I2C Sensoren BME280(THP), SHT-31(TH), BH1750(lux), VEML6070(UV) und YL-38 Regensensor in einem Sketch vereint und die Meßwerte an CCU Systemvariablen oder CuXD Geräte senden kann. Der Sketch ist so gestrickt, daß man nur die benötigten Sensoren per "#define" Direktive (und einige andere Parameter) setzen muß. Den Rest erledigt der Compiler. Deshalb kommen mit dem Sketch hoffentlich auch Leute zurecht, die sich nicht so intensiv in Arduino C-Programmierung einarbeiten möchten.

Batteriebetrieb
Einen Wemos D1 Mini (oder anderen ESP mit Low Dropout Regler) vorausgesetzt, kann man den Sketch an Akkus oder Batterien betreiben. Ich habe mit einem ähnlichen Sketch mit kombinierten Sensoren derzeit bis zu 4 Monate Laufzeit mit drei Eneloop Mignon-Akkus (2500mAh) am 5V Anschluß eines Wemos erreicht. Ein Konfigurationsschalter steuert ob der DeepSleep Modus des 8266 Akku/Batteriebetrieb) oder ein einfaches Delay (Dauerbetrieb) verwendet werden soll. Bei Batteriebetrieb muß natürlich eine Drahtbrücke, Widerstand oder, meine Empfehlung, eine Schottky-Diode eingesetzt werden. Die relativ lange Laufzeit wird insbesondere dadurch erreicht, daß im Idealfall Meßwerte nur gesendet werden, wenn sie von den vorherigen um einen bestimmten Prozentsatz oder absolut abweichen. Ab welcher Differenz die Werte gesendet werden, kann für jeden Meßwert festgelegt werden. Sobald ein Meßwert die Grenze überschreitet werden aber alle Meßwerte gesendet. Weiters wird der ESP beim Auslesen der Sensoren ohne WLAN betrieben, oder anders formuliert, nur wenn Meßwerte gesendet werden müssen wird das WLAN Modul eingeschaltet. Das hilft ebenfalls beim Strom sparen, denn der DeepSleep Modus unterstützt aufwachen mit oder ohne WLAN. Beim Dauerbetrieb benötigt man das nicht, hier ist die WLAN Antenne immer "an".
Der Sketch liest über eine interne Funktion des 8266 die anliegende Betriebsspannung (ADC_MODE(ADC_VCC)) ohne daß eine Drahtbrücke vom 3.3V Pin zu A0 gezogen werden muß. Trotzdem sind die Meßwerte mit Vorsicht zu genießen, speziell wenn an A0 ein Spannungsteiler angeschlossen ist (wie bei jedem mir bekannten 8266 Modul).

Wunderground
Neben Senden zur CCU kann der Sketch die Werte auch nach Wunderground senden. Im Sketch müssen die Credentials für die vorhandene Wunderground Meßstation hinterlegt werden.

Danksagung, Verbesserungen
Vielen Dank an den User klassisch, der für mich den Sketch mit einer real vorhandenen CCU getestet hat und die Konfiguration für CuxD Geräte beigesteuert hat, denn ich habe keine CCU. Wegen Diskussionen mit klassisch weiß ich, daß die Meßwerte des Lux Sensors BH1750 wohl diskussionsfähig sind. Falls jemand eine bessere Bibliothek kennt (die aber sparsam im Stromverbrauch sein muß), bitte ich um einen Link oder Beispielcode, dann werde ich den Sketch anpassen. Ganz allgemein sind die Meßwerte der gebräuchlichen Sonnen oder UV Sensoren nur begrenzt nutzbar.
Zumindest der UV Sensor liefert bei mir aber Meßwerte die den Meßwerten der umliegenden teuren Wetterstationen wie Davis sehr nahe kommen.
Für Angstuser (Filedownload), habe ich hier noch einmal den kompletten Sketch eingefügt:

Code: Alles auswählen

// Arduino WeMos D1 bzw. ESP8266 oder ESP8285 mit BME280, SHT-31, VEML6070, BH1750, YL-38 fuer CCU (khsommer bei web de) 
// Nach Bedarf: 
// BME280_MOD-1022 von https://github.com/embeddedadventures/BME280
// Adafruit_SHT31 von https://github.com/adafruit/Adafruit_SHT31
// SHT-31 geht nicht zusammen mit BME280, weil sie sich bestimmte Variablen teilen. Alle anderen Kombinationen sind moeglich
// Adafruit_VEML6070 von https://github.com/adafruit/Adafruit_VEML6070
// AS_BH1750 von https://github.com/hexenmeister/AS_BH1750
// YL-38 benoetigt keine spezielle Library, YL-38 an 3.3V betreiben
// Sketch unterstuetzt primaer Batteriebetrieb aber auch Dauerbetrieb  Fuer Batteriebetrieb muss #define DAUERBETRIEB auskommentiert sein
// und es wird, wie immer, eine Drahtbruecke (oder Schalter) oder (am Besten) eine Schottky-Diode zwischen D0 und RST benoetigt
// 1K Widerstand geht zur Not auch, macht aber manchmal Probleme  
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <ESP8266WiFi.h>
//------------------------------------------------------------------------------------------------------------------------------
// Ab hier Konfigurationen, bitte jede Definition beachten
    
#define FSDEBUG       // Fuer Debugmeldungen auf der Konsole, fuer normalen Betrieb auskommentieren
                      // Achtung: Serielle Konsole auf 74880 Baud einstellen!
#define DAUERBETRIEB  // Wenn Dauerbetrieb gesetzt ist, braucht es eine konstante Stromversorgung(kein Akku/Batterie)
                      // Bei Dauerbetrieb wird aber auch keine Bruecke von RST zu D0 benoetigt, 
                      // fuer Batteriebetrieb diese Zeile auskommentieren oder #undef DAUERBETRIEB eintragen
//#define DEV_BH1750    // auskommentieren oder undef wenn kein BH1750 Solarsensor vorhanden ist
//#define DEV_BME280    // auskommentieren oder undef wenn kein BME280 Sensor vorhanden ist
//#define DEV_VEML6070  // auskommentieren oder undef wenn kein VEML6070 UV Sensor vorhanden ist
//#define DEV_YL38      // auskommentieren oder undef wenn kein YL-38 Regensensor vorhanden ist
//#define DEV_SHT31     // auskommentieren oder undef wenn kein SH-31 vorhanden ist

#define CCU_HOST     "192.168.178.10"   // IP oder DNS Name der CCU, die komplette URL bastelt der Sketch selber
#define CCU_PORT     "8181"             // Port der CCU
#define CCU_DUMMYEXE "/eriwan.exe"      // oder was anderes 

#define CCU_CUXD  // define falls CUxD anstatt Systemvariable verwendet wird. Bei Verwendung von Systemvariablen: #undef CCU_CUXD

/***************************************
Falls CUxD eingesetzt wird:
Für Temperatur und Feuchte wird ein gemeinsames CUxD Universal Wrapper Thermostat Device (90)(3) benötigt
Für Druck, abs, rel, Helligkeit, und UV, und ggf Spannung wird jeweils ein eigenes CUxD Universal Wrapper Transform Device (90)(1) benötigt
S. Anleitung des CUxD Kapitel  5.7 

Das Anlegen der CUxD Devices macht etwas mehr Arbeit als das Anlegen einfacher Systemvariablen hat aber den Vorteil, daß man die Daten 
wie Geraetedaten behandeln und auch Raeumen und Gewerken zuordnen kann.
Auch bei der Verwendung in Programmen haben Geraete Vorteile gegenueber Systemvariablen
// FS: Dank an klassisch vom homematic-forum
***************************************/
#ifndef CCU_CUXD // Uebetragung an CCU Systemvariablen
 #define CCU_TEMPERATUR    "FS-BMETEMP"          // Bezeichnung in der CCU, nicht benoetigte Werte auskommentieren
 #define CCU_HUMIDITY      "FS-BMEHUM"
 #define CCU_PRESSURE_ABS  "FS-BMEPRESSURE_ABS"
 #define CCU_PRESSURE_DWD  "FS-BMEPRESSURE_DWD"
 #define CCU_DEWPOINT      "FS-BMEDEWPOINT"

 #define CCU_SOLAR         "FS-BH_SOLAR"
 #define CCU_UV            "FS-VEML_UV"
 #define CCU_WEMOSVDD      "FS-8266VDD"

 #define CCU_RAIN          "FS-YL38RAIN"

#endif

#ifdef CCU_CUXD //  Uebetragung an CCU CUxD Geraete
 #define CCU_TEMPERATUR   "CUxD.CUX9002900:1.SET_TEMPERATURE"          // Bezeichnung in der CCU, nicht benoetigte Werte auskommentieren
 #define CCU_HUMIDITY      "CUxD.CUX9002900:1.SET_HUMIDITY"
 #define CCU_PRESSURE_ABS  "CUxD.CUX9000900:1.SET_STATE"
 #define CCU_PRESSURE_DWD  "CUxD.CUX9000901:1.SET_STATE"
 #undef CCU_DEWPOINT      "CUxD.CUX9000902:1.SET_STATE" // Wird eigentlich nicht benoetigt, das kann der CUxD auch selbst machen

 #define CCU_SOLAR         "CUxD.CUX9000903:1.SET_STATE"
 #define CCU_UV            "CUxD.CUX9000904:1.SET_STATE"
 #define CCU_RAIN          "CUxD.CUX9000906:1.SET_STATE"

 #define CCU_WEMOSVDD      "CUxD.CUX9000905:1.SET_STATE"    // gemessene Spannung uebermitteln ggf. auskommentieren
#endif // CCU_CUXD Uebertragung an CUXD

//#define WU_STATION   "CHANGEME"      // fuer Versand nach Wunderground ggf. auskommentieren
//#define WU_PASSWORD  "CHANGEME"

// Existing WiFi network
const char* ssid     = "yyyyyyyy";   // WLAN Zugangsparameter 
const char* password = "xxxxxxxxxxx";    

// Achtung! Dieser Sketch arbeitet entweder mit einer festen IP Adresse,
// oder DHCP. Wenn DHCP gewuenscht ist, einfach den folgenden Block auskommentiert lassen
// Fest IP Adressen sparen aber Zeit beim WLAN Verbindungsaufbau und damit Energie!        
/*
 IPAddress gateway(192, 168, 178, 1); // IP-Adresse des WLAN-Gateways
 IPAddress subnet(255, 255, 255, 0);  // Subnetzmaske
 IPAddress dns(192, 168, 178, 1);     // DNS Server
 IPAddress ip(192, 168, 178, 253);     // feste IP-Adresse fuer den WeMos
*/

#define I2CData   15               // Data GPIO Pin des I2C Busses, bitte eigene Belegung eintragen
#define I2CClock  13               // SCL GPIO Pin des I2C Busses, bitte eigene Belegung eintragen
#define BHRESETPIN 4               // GPIO Pin fuer HW-Reset auf BH1750 Chip, es muß eine Leitung von BHRESETPIN zum BH1750 gezogen werden,
                                   // Der Solarsensor kann aber auch ohne diese Verbindung gelesen werden, der Reset PIN kann angeschlossen werden,
                                   // wenn bei Batteriebetrieb ein Drift des Sensors bemerkt wird. Der Sketch macht vor dem Lesen des Sensors einen kurzen Reset    
#define YL38POWERPIN    5          // Spannungsversorgung YL-38 - zum Test, bitte eigene Belegung eintragen  
#define YL38DATAPIN     11         // Digitaler Statuspin YL-38 - , bitte eigene Belegung eintragen     

#define HOEHEUEBERNN   155         // Hoehe des Standortes fuer Luftdruckberechnung nach DWD
#define MEASURE_UPDATE_OTA 300     // alle 300 Messungen == Counter auf 300 wird OTA gestartet bzw. vdd gesendet
#define WLAN_MAX_WAIT 9000UL       // Zeit in ms die max. auf die WLAN Verbindung gewartet wird, 9 Sekunden ist Default
#define SLEEP_TIME_MEASURE_SEC 10  // Messintervall in sec, default 290 sekunden, zum Testen 30 sekunden
#define MEASURE_UPDATE_CNT 3       // Mess/Update Verhätnis, alle 3 mal wird zwangsgesendet, Vorschlag fuer default ist 6 mal
#define BME280_ADR 0x76            // Adresse des BME280, 0x76 ist die Defaultadresse
#define SHT31_ADR 0x44             // Adresse des SHT31, 0x45 ist die Alternativadresse
#define ABWEICHUNG_TEMP 0.14F      // Abweichung +/- 0.14 Grad legt fest ab welcher Abweichung Sensordaten sofort gesendet werden
#define ABWEICHUNG_HUM 01.5F       // Abweichung +/- 1.5 Prozent Luftfeuchtigkeit
#define ABWEICHUNG_PRES 0.31F      // Abweichung +/- 0.31 Hektopascal 
#define ABWEICHUNG_LUX 10          // Abweichung +/- 10 Lux
#define ABWEICHUNG_UV 1            // Abweichung +/- 1
#define OFFSET_TEMP 0.0F           // Offset Temperatur wird auf den Messwert aufaddiert vor der Weiterverarbeitung
#define OFFSET_PRES 0.0F           // Offset Druck in hPa!
#define OFFSET_HUMI 0.0F           // Offset abs. Luftfeuchte 0% bis 100%
#define FAKTOR_UV   1.0F           // Korrekturfaktor fuer Gehaeuse fuer UV-Index (VEML6070)
#define FAKTOR_LUX  1.0F           // Korrekturfaktor fuer Gehaeuse in Watt/Quadratmeter (BH1750 i.V. mit Wunderground)


// Schluss mit Konfigurationen
// --------------------------------------------------------------------------------------------------------------------------
// Don´t change anything unless you know what you are doing :-) 
#define SKETCH_RELEASE     "1712201908000"  // Version vom Sketch Autor
#define WU_PUBINTERVAL  SLEEP_TIME_MEASURE_SEC // Bei Wunderground wird das Messintervall immer mitgesendet
#define YL38RAINDETECT     0x1020  // Signatur fuer Regen erkannt
#define YL38NORAINDETECT   0x2010  // Signatur fuer keinen Regen erkannt
#define SLEEP_TIME_UPDATE_MSEC 300 // Schlafen zwischen messen und senden in Millisekunden, 300 ist gut

#if (MEASURE_UPDATE_CNT) == 0
 #define MEASURE_UPDATE_CNT 1
#endif

#if (MEASURE_UPDATE_OTA) < 2
 #define MEASURE_UPDATE_OTA 2
#endif
 
#if defined (DEV_BH1750)
 #include <Wire.h>
 #include <AS_BH1750.h>
 AS_BH1750 bh1750;
 uint32_t lux=999, lux_old=999;
#endif

#if defined (DEV_BME280)
 #include <math.h>
 #include <Wire.h>
 #include <SPI.h> 
 #include <BME280_MOD-1022.h>
 unsigned long measuringTime = 25; //for Sensor measuring without readout in ms
 // in forced mode with oversampling=1 in all 3 values the max measuring time should be < 22.8ms
 // for other oversampling settings refer Bosch datasheet chapter 9.1 Measurement time
 // in forced mode with all oversamplings to x16 (max value) the measring time should be <112,8
 float temperature=0, temperature_old=0, humidity=0, humidity_old=0, pressure=0, pressure_old=0, pressure_orgold=0;// Raw float values from the sensor
#endif

#if defined (DEV_SHT31)
 #include <math.h>
 #include <Wire.h>
 #include "Adafruit_SHT31.h"
 Adafruit_SHT31 sht31 = Adafruit_SHT31();
 float temperature=0, temperature_old=0, humidity=0, humidity_old=0; // Raw float values from the sensor
#endif

#if defined (DEV_VEML6070)
 #include "Adafruit_VEML6070.h"
 Adafruit_VEML6070 uv=Adafruit_VEML6070();
 uint32_t uvidx=0, uvidx_old=0;
#endif

#if defined (DEV_YL38)
 uint32_t raindet=0, raindet_old=0;
#endif

//RTC Backup Registers (max. 512 Byte, 128 Registers â 4 Byte)
#define BKP_SEND 64
#define BKP_VDD 65
#define BKP_TEMP 66
#define BKP_HUM 68
#define BKP_PRE 69
#define BKP_LUX 67
#define BKP_OTA 70
#define BKP_UVIDX 71
#define BKP_ORGPRS 72
#define BKP_YL38RAIN 73

// Include API-Headers
extern "C" {
 #include "ets_sys.h"
 #include "os_type.h"
 #include "osapi.h"
 #include "mem.h"
 #include "user_interface.h"
 #include "cont.h"
}
   
//1.- The range of operating voltage of ESP8266 is 1.8V~3.6V
//2.- getVcc function (system_get_vdd33): is only available when TOUT pin17(Wemos Pin A0) is suspended (floating), 
// this function measure the power voltage of VDD3P3 pin 3 and 4 (in the ESP8266 chip)
//3.- RF must be enabled.
ADC_MODE(ADC_VCC); //vcc read

// Use WiFiClient class to create TCP connections
WiFiClient wclclient;
HTTPClient wclhttp;

WiFiClient wuwificlient;
HTTPClient wuhttp;
      
uint32_t cnt=MEASURE_UPDATE_CNT;
uint32_t otacnt=MEASURE_UPDATE_OTA;


float absf(float f) { // Float ABS
  if (f<0.0F)
    return -f;
  else
    return f;
}

boolean weather_to_webservice (String object_string, String value_string) { // Sendet einen Wert an CCU

  String ccu_webservice = F("http://");
  ccu_webservice += F(CCU_HOST);
  ccu_webservice += F(":");
  ccu_webservice += F(CCU_PORT);
  ccu_webservice += F(CCU_DUMMYEXE); 

  String ccu_request = F("?antwort=dom.GetObject('");
  ccu_request += object_string;
  ccu_request += F("').State('");
  ccu_request += value_string;
  ccu_request += F("')");
  
  #ifdef FSDEBUG
  Serial.print("weather_to_webservice [HTTP] URL: ");
  Serial.println(ccu_webservice+ccu_request);
  #endif

  //wclhttp.setReuse(true);
  wclhttp.begin(wclclient, ccu_webservice+ccu_request);
  int httpCode = wclhttp.GET();
  // httpCode will be negative on error
  if(httpCode > 0) {
    #ifdef FSDEBUG
    // HTTP header has been send and Server response header has been handled
    Serial.printf("[HTTP] GET... code: %d\n", httpCode);
    // file found at server
    #endif
    if((httpCode == HTTP_CODE_OK)||(httpCode == 302)) {
      #ifdef FSDEBUG
      String payload = wclhttp.getString();
      Serial.print("Webserver Payload: ");
      Serial.println(payload);
      #endif
      return(true);
     } else {
      #ifdef FSDEBUG
      String payload = wclhttp.getString();
      Serial.print("Webserver Payload: ");
      Serial.println(payload);
      #endif
    }
   } else {
   #ifdef FSDEBUG
   Serial.printf("[HTTP] GET... failed, error: %s\n", wclhttp.errorToString(httpCode).c_str());
   #endif
  }
  return(false); //fake to true for Test
}

boolean readSensor() { // Liest Sensoren und verarbeitet die Messwerte
  #if defined (DEV_BH1750)
   lux = (unsigned int) bh1750.readLightLevel();
   #ifdef FSDEBUG
   Serial.print("Lux read from BH1750: ");
   Serial.println(lux);
   #endif
   if (abs(lux-lux_old)>ABWEICHUNG_LUX) {
     #ifdef FSDEBUG       
     Serial.println("Force update lux");
     #endif
     cnt=1;  //Send the next time
   }   
  #endif // DEV_BH1750

  #if defined (DEV_VEML6070)
   uvidx = (unsigned int) uv.readUV()/2;
   #ifdef FSDEBUG
   Serial.print("UV read from VEML6070: ");
   Serial.println(uvidx);
   #endif
   if (abs(uvidx-uvidx_old)>ABWEICHUNG_UV) {
     #ifdef FSDEBUG       
     Serial.println("Force update UV");
     #endif
     cnt=1;  //Send the next time
   }
  #endif  //DEV_VEML6070

  #if defined (DEV_YL38)   
   byte portstatus=digitalRead(YL38DATAPIN);
   #ifdef FSDEBUG
   Serial.print("Rain status read from YL38: ");
   Serial.println(portstatus);
   #endif
   switch (portstatus) {
     case 0: raindet = YL38RAINDETECT;
         break;
     case 1: raindet = YL38NORAINDETECT;
         break;
     default: raindet = 0;
   }
   
   if (raindet!=raindet_old) {
     #ifdef FSDEBUG       
     Serial.println("Force update rain");
     #endif
     cnt=1;  //Send the next time
   }   
  #endif // DEV_YL38

     
  #if defined (DEV_BME280)
   BME280.readMeasurements();
   if (BME280.isMeasuring()) { // this should not happen becaus wait time is long enough
     #ifdef FSDEBUG       
     Serial.println("Lesefehler BME280");
     #endif
     return(false);
   }
   temperature = BME280.getTemperatureMostAccurate()+OFFSET_TEMP;
   humidity = BME280.getHumidityMostAccurate()+OFFSET_HUMI;  
   float pressurebme_new = (BME280.getPressureMostAccurate()+OFFSET_PRES)*100.0F;
   pressure = pressurebme_new;
   #ifdef FSDEBUG
   Serial.print("Pressure absolut ");
   Serial.println(pressurebme_new);
   #endif  
   float pf_rel = humidity/100.0F;  // relative Luftfeuchte (0-1.0)     
   float pg_n = 9.80665F;           // Erdbeschleunigung (m/s^2)
   float pgam = 0.0065F;            // Temperaturabnahme in K pro geopotentiellen Metern (K/gpm)
   float Rpp = 287.06F;             // Gaskonstante fuer trockene Luft (R = R_0 / M)
   float pM = 0.0289644F;           // Molare Masse trockener Luft (J/kgK)
   float pR_0 = 8.314472F;          // allgemeine Gaskonstante (J/molK)
   float pT_0 = 273.15F;            // Umrechnung von Grad Celsius in K
   float pC = 0.11F;                // DWD-Beiwert fuer die Beruecksichtigung der Luftfeuchte
   float pE_0 = 6.11213F;           // (hPa)
   float temp_tmp;
   float pe_d;
   // momentaner Stationsdampfdruck (hPa)
   temp_tmp = temperature-OFFSET_TEMP;  // Korrekturwert wieder rausnehmen
   pe_d = pf_rel * pE_0 * exp((17.5043 * temp_tmp) / (241.2 + temp_tmp));
   pressure = pressure/100 * exp((pg_n * HOEHEUEBERNN) / (Rpp * (temp_tmp + pT_0 + pC * pe_d + ((pgam * HOEHEUEBERNN) / 2))));
   pressure *= 100; 
   #ifdef FSDEBUG
   Serial.print("Pressure DWD ");
   Serial.println(pressure);
   #endif
   temperature=round(temperature*100.0F)/100.0F;  // runden auf zwei Nachkommestellen
   if ((temperature >50)||(pressure <96000)||(temperature <-20)||( pressure >105000)) { 
     String tmp_cmd = F("BME280 Lesefehler");
     #ifdef FSDEBUG       
     Serial.println(tmp_cmd);
     Serial.print("Temperature = ");
     Serial.print(temperature);
     Serial.println(" *C");
     Serial.print("Pressure = ");
     Serial.print(pressure);
     Serial.println(" hPa");   
     Serial.print("Humidity = ");
     Serial.print(humidity);
     Serial.println(" %");
     #endif
     return(false);
   }     
  
   if ((absf(temperature_old-temperature)>(ABWEICHUNG_TEMP))||(absf(pressure_old-pressure)>(ABWEICHUNG_PRES*100))||(absf(humidity_old-humidity)>(ABWEICHUNG_HUM))) {
     #ifdef FSDEBUG       
     Serial.println("Force update BME data ");
     #endif
     cnt=1;  //Send the next time
   }   
  #endif // DEV_BME280
  #if defined (DEV_SHT31)
   temperature = sht31.readTemperature()+OFFSET_TEMP;
   humidity = sht31.readHumidity()+OFFSET_HUMI;  
   #ifdef FSDEBUG
   Serial.print("Temperature read from SHT31: ");
   Serial.println(temperature);
   Serial.print("Humidity read from SHT31: ");
   Serial.println(humidity);
   #endif
  if (isnan(temperature)||isnan(humidity)) {  // check if 'is not a number'
     String tmp_cmd = F("SHT31 Lesefehler");
     #ifdef FSDEBUG       
     Serial.println(tmp_cmd);
     Serial.print("Temperature = ");
     Serial.print(temperature);
     Serial.print("Humidity = ");
     Serial.print(humidity);
     Serial.println(" %");
     #endif
     return(false);
  }

   temperature=round(temperature*100.0F)/100.0F;  // runden auf zwei Nachkommestellen
   if ((temperature >50)||(temperature <-30)) { 
     String tmp_cmd = F("SHT31 unplausibel");
     #ifdef FSDEBUG       
     Serial.println(tmp_cmd);
     Serial.print("Temperature = ");
     Serial.print(temperature);
     Serial.println(" *C");
     Serial.print("Humidity = ");
     Serial.print(humidity);
     Serial.println(" %");
     #endif
     return(false);
   }     

     if ((absf(temperature_old-temperature)>(ABWEICHUNG_TEMP))||(absf(humidity_old-humidity)>(ABWEICHUNG_HUM))) {
     #ifdef FSDEBUG       
     Serial.println("Force update SHT31 data ");
     #endif
     cnt=1;  //Send the next time
   }   
  #endif // DEV_SHT31
  if (cnt==1) {
    #if defined (DEV_BME280)
      system_rtc_mem_write(BKP_HUM, &humidity, 4);
      system_rtc_mem_write(BKP_PRE, &pressure, 4);
      system_rtc_mem_write(BKP_TEMP, &temperature, 4);
      system_rtc_mem_write(BKP_ORGPRS, &pressurebme_new, 4);   
    #endif // DEV_BME280
    #if defined (DEV_SHT31)
      system_rtc_mem_write(BKP_HUM, &humidity, 4);
      system_rtc_mem_write(BKP_TEMP, &temperature, 4);
    #endif // DEV_SHT31
    #if defined (DEV_BH1750)
      system_rtc_mem_write(BKP_LUX, &lux, 4); 
    #endif  // DEV_BH1750
    #if defined (DEV_VEML6070)
      system_rtc_mem_write(BKP_UVIDX, &uvidx, 4); 
    #endif  // DEV_VEML6070
    #if defined (DEV_YL38)
      system_rtc_mem_write(BKP_YL38RAIN, &raindet, 4); 
    #endif // DEV_YL38
  }   
  return (true);
}

#if defined (DEV_BME280)
 boolean config_bme (void) { // Initialisiert den BME 280 fuer force mode
   #ifdef FSDEBUG
   byte chipID = 0; // for BME280 Cip IF
   chipID = BME280.readChipId();
   Serial.print (F("BME280 Chip Id (0x): "));
   Serial.println (chipID, HEX);
   #endif // FSDEBUG
   // need to read the NVM compensation parameters
   BME280.readCompensationParams();
   // Need to turn on 1x oversampling, default is os_skipped, which means it doesn't measure anything
   BME280.writeOversamplingPressure(os1x);     // 1x over sampling (ie, just one sample)
   BME280.writeOversamplingTemperature(os1x);  // 1x over sampling (ie, just one sample)
   BME280.writeOversamplingHumidity(os1x);     // 1x over sampling (ie, just one sample)
   measuringTime = (int) (0.5 + (1.25 + (2.3 * 1) + (2.3 * 1 + 0.575) + (2.3 * 1 + 0.575))); // measuringTime is unsigned long
   /* in forced mode with oversampling=1 in all 3 values the max measuring time should be < 22.8ms
      in forced mode with all oversamplings to x16 (max value) the measring time should be <112,8
      for other oversampling settings refer Bosch datasheet chapter 9.1 Measurement time
      measuringTime = 1.25+(2.3*temp_os) + (2.3*humid_os + 0.575) + (2.3*press_os + 0.575)  */
   // init a forced sample.  After taking the measurement the chip goes back to sleep
   BME280.writeMode(smForced);
   delay(measuringTime + 25); // wait until measurement is done. With oversampling=1 in all 3 values the max measuring time should be < 22.8ms
   // for other oversampling settings refer Bosch datasheet chapter 9.1 Measurement time 
   //BME280.readMeasurements();
   return (true);    
 }
#endif // DEV_BME280

void configSensor() {  // Sensoren konfigurieren
  #if defined (DEV_BH1750) || defined (DEV_VEML6070) || defined (DEV_BME280)|| defined (DEV_SHT31)
   Wire.begin(I2CData,I2CClock);
   delay(10);     
   #ifdef FSDEBUG
   Serial.print("I2C initialized after ");
   Serial.println(millis());
   #endif
  #endif
  
  #ifdef DEV_BH1750         // BH 1750 konfigurieren
   pinMode(BHRESETPIN, OUTPUT_OPEN_DRAIN);
   digitalWrite(BHRESETPIN, LOW);
   delay(2);
   digitalWrite(BHRESETPIN, HIGH);
   delay(10);   
   bh1750.begin(); 
   #ifdef FSDEBUG
   Serial.print("BH1750 initialized after ");
   Serial.println(millis());
   #endif
  #endif //DEV_BH1750

  #ifdef DEV_VEML6070       // VEML 6070 konfigurieren
   uv.begin(VEML6070_4_T);  // pass in the integration time constant
   delay(10);               // warten auf Verarbeitung 
   #ifdef FSDEBUG
   Serial.print("VEML6070 initialized after ");
   Serial.println(millis());
   #endif
  #endif  //DEV_VEML6070

  #ifdef DEV_SHT31       // SHT 31 konfigurieren
  if (! sht31.begin(SHT31_ADR)) {   // Set to 0x45 for alternate i2c addr
    Serial.println("!!ERROR: Couldn't find SHT31");
   }
   delay(10);               // warten auf Verarbeitung 
   #ifdef FSDEBUG
   Serial.print("If no error above, SHT31 initialized after ");
   Serial.println(millis());
   #endif
  #endif  //DEV_SHT31
  
  #if defined (DEV_YL38)
   pinMode(YL38POWERPIN, OUTPUT);
   digitalWrite(YL38POWERPIN, HIGH);
   pinMode(YL38DATAPIN, INPUT);
   delay(500);
   #ifdef FSDEBUG     
   Serial.print("YL38 initialized after ");
   Serial.println(millis());
   #endif
  #endif //DEV_YL38

  #if defined (DEV_BME280)  // BME280 konfigurieren
   config_bme();            // ist aufwaendiger, deshalb ein UP
   #ifdef FSDEBUG
   Serial.print("BME280 initialized after ");
   Serial.println(millis());
   #endif
  #endif  //DEV_BME280
}

void stopSensor() {  // Routinen um Sensoren zu stoppen bzw. Strom abschalten
  #if defined (DEV_YL38)
   pinMode(YL38POWERPIN, OUTPUT);
   digitalWrite(YL38POWERPIN, LOW);
   #ifdef FSDEBUG     
   Serial.print("YL38 stopped after ");
   Serial.println(millis());
   #endif
  #endif //DEV_YL38
}


int updateServices() {   //Routinen zum WLAN Aufbau und Daten senden
  boolean result=false;
  // Connect to your WiFi network
  // WiFi.mode(WIFI_STA);
  #ifdef gateway                       // wenn Gateway definiert ist,  
   WiFi.config(ip,gateway,subnet,dns); // IP Adresse statisch konfigurieren
  #endif // statisches IP Netzwerk
  
  unsigned int starttime=millis();
  char isSSID[64];
  strcpy(isSSID,WiFi.SSID().c_str());
  if ((strcmp(isSSID,ssid)!=0)&&(strcmp(ssid,"")!=0)){
    #ifdef FSDEBUG
    Serial.println("");
    Serial.print("new WLAN config: ");
    Serial.println(millis());
    #endif    
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    wifi_station_set_auto_connect(true);
   } else {
    #ifdef FSDEBUG
    Serial.println("");
    Serial.print("existing WLAN config: ");
    Serial.println(millis());
    #endif
  }
  while ((WiFi.status() != WL_CONNECTED) && ((millis()-starttime) < WLAN_MAX_WAIT)) {
    delay(400);
    #ifdef FSDEBUG
    Serial.print(".");
    #endif
  }  
  #ifdef FSDEBUG
  Serial.println("");
  Serial.print("Elapsed time after WLAN connect: ");
  Serial.println(millis());
  #endif    
  result=true;
  if (WiFi.status() != WL_CONNECTED) {
    #ifdef FSDEBUG
    Serial.println("\nWiFi connection failed");
    #endif
    return false; // Kein WLAN, deshalb abbrechen
   } else {
    String dataurl;
    
    #if defined (DEV_BH1750)
     float sun_tmp = round((((float)lux_old*0.0079F*FAKTOR_LUX)*10.0F)/10.0F); // Runden auf eine Nachkommastelle
     weather_to_webservice (CCU_SOLAR, String(lux_old));
     dataurl = dataurl + F("&solarradiation=")+String(sun_tmp);
    #endif // DEV_BH1750
    
    #ifdef DEV_VEML6070
     float uv_tmp =(round((((float)uvidx_old)/747.0F*FAKTOR_UV)*10.0F)/10.0F); // Runden auf eine Nachkommastelle, der Quotient von 747 soll
                                                                         // den Messwert des VEML6070 auf einen sinnvollen UV Wert abbilden
     weather_to_webservice (CCU_UV, String(uv_tmp));
     dataurl = dataurl + F("&UV=")+String(uv_tmp);
    #endif //VEML_6070

    #if defined (DEV_YL38)
     String rain_status_s;
     switch(raindet_old) {
       case YL38RAINDETECT:
            rain_status_s ="0.0";
            break;
       case YL38NORAINDETECT:
            rain_status_s ="1.0";
            break;
       default: rain_status_s ="2.0";
     } 
     weather_to_webservice (CCU_RAIN, rain_status_s);
    #endif // DEV_YL38
    
    #if defined (DEV_BME280)
     float dewpoint;   
     if ((pressure_old <95000)||(pressure_old>110000)||(humidity_old==0)) {        
       #ifdef FSDEBUG
       Serial.print("Luftdruck unplausibel: ");
       Serial.println(String(pressure_old));
       #endif 
       return(false);
     }
     if (temperature_old > 0 )
       dewpoint = 243.12F*((17.62F*temperature_old)/(243.12F+temperature_old)+log(humidity_old/100))/((17.62F*243.12F)/(243.12F+temperature_old)-log(humidity_old/100));
        else
       dewpoint = 272.62F*((22.46F*temperature_old)/(272.62F+temperature_old)+log(humidity_old/100))/((22.46F*272.62F)/(272.62F+temperature_old)-log(humidity_old/100));
      #ifdef CCU_TEMPERATUR
       if (!(weather_to_webservice(CCU_TEMPERATUR, String(temperature_old))))
         result=false; 
      #endif
      #ifdef CCU_DEWPOINT
       if (!weather_to_webservice(CCU_DEWPOINT, String(dewpoint)))
          result=false;
      #endif
      #ifdef CCU_HUMIDITY
       if (!weather_to_webservice(CCU_HUMIDITY, String(humidity_old)))
          result=false;
      #endif
      #ifdef CCU_PRESSURE_DWD
       if (!weather_to_webservice(CCU_PRESSURE_DWD, String(pressure_old/100.0F)))  //der BME liefert den Druck in Pa nicht hPa, deshalb durch 100
          result=false;      
      #endif 
      #ifdef CCU_PRESSURE_ABS 
       if (!weather_to_webservice(CCU_PRESSURE_ABS, String(pressure_orgold/100.0F)))  //der BME liefert den Druck in Pa nicht hPa, deshalb durch 100 
          result=false;
      #endif 
    #endif //BME

 #if defined (DEV_SHT31)
     float dewpoint;   
     if (humidity_old==0) {        
       #ifdef FSDEBUG
       Serial.print("Luftfeuchte unplausibel: ");
       Serial.println(String(humidity_old));
       #endif 
       return(false);
     }
     if (temperature_old > 0 )
       dewpoint = 243.12F*((17.62F*temperature_old)/(243.12F+temperature_old)+log(humidity_old/100))/((17.62F*243.12F)/(243.12F+temperature_old)-log(humidity_old/100));
        else
       dewpoint = 272.62F*((22.46F*temperature_old)/(272.62F+temperature_old)+log(humidity_old/100))/((22.46F*272.62F)/(272.62F+temperature_old)-log(humidity_old/100));
      #ifdef CCU_TEMPERATUR
       if (!(weather_to_webservice(CCU_TEMPERATUR, String(temperature_old))))
         result=false; 
      #endif
      #ifdef CCU_DEWPOINT
       if (!weather_to_webservice(CCU_DEWPOINT, String(dewpoint)))
          result=false;
      #endif
      #ifdef CCU_HUMIDITY
       if (!weather_to_webservice(CCU_HUMIDITY, String(humidity_old)))
          result=false;
      #endif
    #endif //DEV_SHT31
    
    if (!result)
        return(false);
    #if defined WU_STATION
      int httpCode;
      String wuurl="", presurl="", frequrl="";
       
      #ifdef DEV_BME280
       float dewpoint_f, temperature_f, pressure_in;
       temperature_f = (temperature_old*9.0F/5.0F) +32;
       pressure_in = (pressure_old)/3386.530749F;
       dewpoint_f = (dewpoint*9/5) +32;
       dataurl = dataurl + F("&tempf=")+String(temperature_f);
       dataurl = dataurl + F("&humidity=")+String(humidity_old);
       dataurl = dataurl + F("&dewptf=")+String(dewpoint_f);
      #endif // DEV_BME280

      #ifdef DEV_SHT31
       float dewpoint_f, temperature_f, pressure_in;
       temperature_f = (temperature_old*9.0F/5.0F) +32;
       dewpoint_f = (dewpoint*9/5) +32;
       dataurl = dataurl + F("&tempf=")+String(temperature_f);
       dataurl = dataurl + F("&humidity=")+String(humidity_old);
       dataurl = dataurl + F("&dewptf=")+String(dewpoint_f);
      #endif // DEV_SHT31
      
      wuurl = F("http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php");
      wuurl = wuurl+ F("?ID="); 
      wuurl = wuurl + WU_STATION; 
      wuurl = wuurl + F("&PASSWORD=")+WU_PASSWORD;
      wuurl = wuurl + F("&dateutc=now");
      frequrl = F("&action=updateraw");             
      #ifdef DEV_BME280
       pressure_in = (pressure_old)/3386.530749F;   
       presurl= ("&baromin=")+String((pressure_in));
      #endif
      #ifdef FSDEBUG
      Serial.print("Temperature = ");
      Serial.print(temperature_f);
      Serial.println(" *F");
      Serial.print("Pressure = ");
      Serial.print(pressure_in);
      Serial.println(" inHg");   
      Serial.print("Humidity = ");
      Serial.print(humidity_old);
      Serial.println(" %");
      Serial.print("URL fuer Wunderground: ");
      Serial.println(wuurl+dataurl+presurl+frequrl);
      #endif         
      wuhttp.begin(wuwificlient, wuurl+dataurl+presurl+frequrl); //HTTP
      httpCode = wuhttp.GET();
      // httpCode will be negative on error
      if(httpCode > 0) {
       // HTTP header has been send and Server response header has been handled
       #ifdef FSDEBUG
       Serial.printf("[HTTP] GET... code: %d\n", httpCode);
       #endif
       // file found at server
       if((httpCode == HTTP_CODE_OK)||(httpCode == 302)) {
         #ifdef FSDEBUG
         String payload = wuhttp.getString();
         Serial.print("Wunderground Payload: ");
         Serial.println(payload);
         #endif
         wuhttp.end();
         return(true);
        } else {
         #ifdef FSDEBUG
         Serial.printf("[HTTP] GET... failed, error: %s\n", wuhttp.errorToString(httpCode).c_str());
         #endif
         wuhttp.end();
         return(false);
       }
      } else {
       #ifdef FSDEBUG
       Serial.printf("[HTTP] GET... failed, error: %s\n", wuhttp.errorToString(httpCode).c_str());
       #endif 
      }
      wuhttp.end();
      return(false);
    #endif   //WU_STATION
  }
  return(true);
}

void setup(void) {  // Bei Sketchen mit ESP_Deep_Sleep steht der Code i.a. nur in der Setup Funktion,
                    // ist aber keine Bedingung
  #ifndef DAUERBETRIEB
   // Connect D0 to RST to wake up
   pinMode(D0, WAKEUP_PULLUP);
  #endif
  // Open the Arduino IDE Serial Monitor to see what the code is doing
  #ifdef FSDEBUG
  Serial.begin(74880);
  Serial.println("  \n  \nGeht los!");
  Serial.print("Firmware Version: ");
  Serial.print(__TIMESTAMP__);
  Serial.print(" Sketch vom: ");
  Serial.println(SKETCH_RELEASE);
  #if defined (DEV_BH1750)
   Serial.println("WeMos BH1750 Client");
  #endif
  #if defined (DEV_VEML6070)
   Serial.println("WeMos VEML6070 Client");
  #endif
  #if defined (DEV_BME280)
   Serial.println("WeMos BME280 Client");
  #endif //DEV_BME280
  #if defined (DEV_YL38)
   Serial.println("WeMos YL-38 Client");
  #endif //DEV_YL38
  #if defined (DEV_SHT31)
   Serial.println("WeMos SHT-31 Client");
  #endif //DEV_YL38
  #endif //FSDEBUG

  //Lese letzte Werte aus RTC Backup Register
  system_rtc_mem_read(BKP_SEND, &cnt, 4);
  if (cnt>MEASURE_UPDATE_CNT)
    cnt=2;            // sollte nicht vorkommen
  system_rtc_mem_read(BKP_OTA, &otacnt, 4);
  if (otacnt>MEASURE_UPDATE_OTA)
    otacnt=2;          // sollte nicht vorkommen
      
  #ifdef FSDEBUG
  Serial.print ("Sleep time in seconds between measurements: ");
  Serial.println(SLEEP_TIME_MEASURE_SEC);
  Serial.print ("Sleep time in milliseconds between measure() and updateServices(): ");
  Serial.println(SLEEP_TIME_UPDATE_MSEC);
  Serial.print("Measure counter was ");
  Serial.println(cnt);
  Serial.print("OTA counter was ");
  Serial.println(otacnt);
  #endif
  
  #if defined (DEV_BH1750)   
   system_rtc_mem_read(BKP_LUX, &lux_old, 4);
   #ifdef FSDEBUG
   Serial.print("Lux_old was ");
   Serial.println(lux_old);
   #endif 
  #endif
  
  #if defined (DEV_VEML6070)   
   system_rtc_mem_read(BKP_UVIDX, &uvidx_old, 4);
   #ifdef FSDEBUG
   Serial.print("UVidx_old was ");
   Serial.println(uvidx_old);
   #endif
  #endif
  
  #if defined (DEV_YL38)   
   system_rtc_mem_read(BKP_YL38RAIN, &raindet_old, 4);
   #ifdef FSDEBUG
   Serial.print("raindet_old was ");
   Serial.println(raindet_old);
   #endif 
  #endif

  #if defined (DEV_BME280)   
   system_rtc_mem_read(BKP_TEMP, &temperature_old, 4);
   system_rtc_mem_read(BKP_HUM, &humidity_old, 4);
   system_rtc_mem_read(BKP_PRE, &pressure_old, 4);
   system_rtc_mem_read(BKP_ORGPRS, &pressure_orgold, 4);
   #ifdef FSDEBUG
   Serial.print("temperature_old was ");
   Serial.println(temperature_old);
   Serial.print("humidity_old was ");
   Serial.println(humidity_old);
   Serial.print("pressure_old (DWD) was ");
   Serial.println(pressure_old);
   Serial.print("pressure_orgold (absolute) was ");
   Serial.println(pressure_orgold);
   #endif
  #endif //DEV_BME280
  #if defined (DEV_SHT31)   
   system_rtc_mem_read(BKP_TEMP, &temperature_old, 4);
   system_rtc_mem_read(BKP_HUM, &humidity_old, 4);
   #ifdef FSDEBUG
   Serial.print("temperature_old was ");
   Serial.println(temperature_old);
   Serial.print("humidity_old was ");
   Serial.println(humidity_old);
   #endif
  #endif //DEV_SHT31
  if (otacnt)
    otacnt--;
   else // otacount ist null, firmware laden?
     if (cnt) //wegen otacnt ist null datensenden erzwingen,
       cnt=1; //weiter unten wird auf null subtrahiert
  system_rtc_mem_write(BKP_OTA, &otacnt, 4);  //schreibe otacounter
  if(cnt) { 
    configSensor();   // Sensoren konfigurieren 
    readSensor();     // Alle konfigurierten Sensoren auslesen
    stopSensor();     // Sensoren ausschalten
    #ifdef FSDEBUG 
    Serial.print("Mesurement done after "); 
    Serial.println(millis());
    #endif       
    cnt--;    
    //Schreibe Sendecounter
    system_rtc_mem_write(BKP_SEND, &cnt, 4);
    if(cnt){ // cnt groesser null, nicht senden
       #ifndef DAUERBETRIEB
        #ifdef FSDEBUG
        Serial.print("Sleeping with WAKE_RF_DISABLED for SLEEP_TIME_MEASURE_SEC seconds after readSensor(), elapsed time ");
        Serial.println(millis());
        #endif
        ESP.deepSleep(SLEEP_TIME_MEASURE_SEC*1000000UL, WAKE_RF_DISABLED);
        delay(100);
       #else
        #ifdef FSDEBUG
        Serial.print("Waiting for SLEEP_TIME_MEASURE_SEC seconds after readSensor(), elapsed time ");
        Serial.println(millis());
        #endif           
        delay(SLEEP_TIME_MEASURE_SEC*1000);
        ESP.restart(); // Neustart
       #endif
       } else {//cnt ist null, wecken mit wlan einschalten
       #ifndef DAUERBETRIEB
        #ifdef FSDEBUG
        Serial.print("Sleeping for SLEEP_TIME_MEASURE_MSEC milliseconds with WAKE_RF_DEFAULT after readSensor(), elapsed time ");
        Serial.println(millis());
        #endif           
        ESP.deepSleep(SLEEP_TIME_UPDATE_MSEC*1000UL, WAKE_RF_DEFAULT);
        delay(100);
       #else
        #ifdef FSDEBUG
        Serial.print("Waiting for SLEEP_TIME_MEASURE_MSEC milliseconds after readSensor(), elapsed time ");
        Serial.println(millis());
        #endif           
        delay(SLEEP_TIME_UPDATE_MSEC);
        ESP.restart(); // Neustart
       #endif
    }
   } else {  //Send Data after Wakeup
     boolean wlan_ok;
     wlan_ok=updateServices();   
     if (wlan_ok==true) { 
         #ifdef FSDEBUG
         Serial.print("updateServices() successful, elapsed time: "); 
         Serial.println(millis());
         #endif
         //Initialisiere Sendecounter
         cnt=MEASURE_UPDATE_CNT;
       } else {
        #ifdef FSDEBUG 
        Serial.print("updateServices() NOT successful trying again soon, elapsed time: "); 
        Serial.println(millis());
        #endif
        cnt=1;   //Send again next time
     }
     system_rtc_mem_write(BKP_SEND, &cnt, 4); 
     if (otacnt) {         
        //Reinitialisiere Updateintervall
        #ifndef DAUERBETRIEB
        #ifdef FSDEBUG
        Serial.print("Sleeping with WAKE_RF_DISABLED for SLEEP_TIME_MEASURE_SEC seconds after updateServices(), elapsed time ");
        Serial.println(millis());
        #endif
         ESP.deepSleep(SLEEP_TIME_MEASURE_SEC*1000000UL, WAKE_RF_DISABLED);
         delay(100);
        #else
         #ifdef FSDEBUG
         Serial.print("Waiting for SLEEP_TIME_MEASURE_SEC seconds after updateServices(), elapsed time ");
         Serial.println(millis());
         #endif
         delay(SLEEP_TIME_MEASURE_SEC*1000);
         ESP.restart(); // Neustart
        #endif
        } else {
        if (wlan_ok==false) { 
           cnt=1;
           system_rtc_mem_write(BKP_SEND,&cnt,4);
          }
         else  {
           cnt=MEASURE_UPDATE_CNT;
           system_rtc_mem_write(BKP_SEND,&cnt,4);
           //Reinitialisiere Updateintervall
           otacnt = MEASURE_UPDATE_OTA;
           system_rtc_mem_write(BKP_OTA,&otacnt,4);
           //Initialisiere Sendecounter
        }   
        #ifdef CCU_WEMOSVDD
        float vdd=ESP.getVcc()/1024.0;  // Hoehe der Betriebsspannung an CCU senden
        weather_to_webservice(CCU_WEMOSVDD,String(vdd)); //Der gemessene Wert ist aber nur eine Näherung 
        #endif
        #ifndef DAUERBETRIEB
         #ifdef FSDEBUG
         Serial.print("Sleeping with WAKE_RF_DISABLED for SLEEP_TIME_MEASURE_SEC seconds after updateServices() and otaupdate, elapsed time ");
         Serial.println(millis());
         #endif
         ESP.deepSleep(SLEEP_TIME_MEASURE_SEC*1000000UL, WAKE_RF_DISABLED);
         delay(100);
        #else
         #ifdef FSDEBUG
         Serial.print("Waiting for SLEEP_TIME_MEASURE_SEC seconds after updateServices() and otaupdate, elapsed time ");
         Serial.println(millis());
         #endif
         delay(SLEEP_TIME_MEASURE_SEC*1000);
         ESP.restart(); // Neustart
        #endif         
      }
   }
   delay(100);
}

void loop(void) { // should be never reached
  #ifdef FSDEBUG
  Serial.print("ERROR: loop() reached, elapsed time ");
  Serial.println(millis());
  #endif
  ESP.restart(); // Neustart
}
Update 15.6.2017
Ich habe den Sketch um Unterstützung für Regendetektoren wie YL-38 ergänzt. Es wird der digitale Ausgang des YL-38 ausgewertet, nicht der analoge Ausgang! Mal sehen ob das funktioniert. Der YL-38 muß mit 3,3V betrieben werden, damit er den Eingang des 8266 nicht zerstört (obwohl die 8266 eigentlich bis 5,5V spezifiziert sind). Im Sketch wird D5 als Statuspin des Regensensors angenommen, kann natürlich angepasst werden. Zus. habe ich D3 als "Spannungsversorgung" für den YL-38 vorgesehen. Der Pin wird vor dem Messen auf High geschaltet, dann wird 500ms gewartet und erst dann der Statuspin ausgelesen. Dann wird D3 wieder auf Low geschaltet. Wer mag kann ja mal probieren ob D3 (oder ein anderer Pin) als Spannungsversorgung für den Regensensor ausreicht. Falls das geht, kann man den Regensensor mglw. auch im Batteriebetrieb einsetzen. Ansosnten bleibt nur der Modus Duaerbetrieb mit fester Versorgungsspannung. Der YL-38 darf aber nur mit 3,3V betrieben werden (ggf. vom Board abgreifen).

Vielen Dank wieder an klassisch fürs Testen und korrigieren.

Update 19.6.2017
Fehler bei der I2C Initialisierung beseitigt.

Update 13.03.2018
WLAN Initialisierung optimiert, ABS Funktion für float eingefügt, Differenzbestimmung für Messwerte bei Konfiguration mit mehreren Sensoren gleichzeitig korrigiert
NB: Ich habe die Version nicht ausgiebig getestet, bei Problemen bitte melden.

Update 28.12.2019
Sketch um Unterstützung für SHT-31 Temperatur.- und Luftfeuchtesensor ergänzt. SHT-31 und BME 280 können nicht zsuammen eingesetzt werden, denn sie teilen sich bestimmte Variablen (Temperatur und Feuchte). Alle anderen Kombinationen sind möglich.

F.Sommer
Dateianhänge
homematic_bme280-veml6070-bh1750-YL38-201803.zip
Version März 2018
(9.74 KiB) 247-mal heruntergeladen
homematic_bme280-veml6070-bh1750-YL38.zip
Version Juni 2017
(9.58 KiB) 270-mal heruntergeladen
Zuletzt geändert von fsommer1968 am 30.01.2020, 21:54, insgesamt 14-mal geändert.

dondaik
Beiträge: 12927
Registriert: 16.01.2009, 18:48
Wohnort: Steingaden
Hat sich bedankt: 1603 Mal
Danksagung erhalten: 222 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von dondaik » 05.06.2017, 20:01

hört sich sehr gut an !!
doch wo finde ich ihn ? :-) - ah gerade ist er "angekommen" ...

dann werde ich mal sensoren zusammensuchen ... der VEML6070 hat ja wohl auch eine neues layout in der bucht bekommen.
und ggf mal schauen wie ich in w-ground eine wetterstation anmelde :-)
gut das der neue lötkolben in den nächsten tagen eintrifft !

sauberes sketsch !!! hut ab.
-------
!!! der download der handbüchern auf den seiten von eq3 und das lesen der tips und tricks kann das hm-leben sehr erleichtern - das nutzen der suche nach schlagworten ebenso :mrgreen: !!!
wer schreibfehler findet darf sie behalten.

knopers1
Beiträge: 96
Registriert: 03.09.2014, 22:04

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von knopers1 » 06.06.2017, 11:50

Hallo fssommer.
Eine Frage habe ich noch...
Du schreibst "Zitat"
Bei Batteriebetrieb muß natürlich eine Drahtbrücke, Widerstand oder, meine Empfehlung, eine Schottky-Diode eingesetzt werden.

Kannst Du mir mal sagen, welchen Vorteil eine Schottky Diode hat? Ich betreibe ähnliche Wetterstation mit einer Drahtbrücke. Mit einem 4,800 Ah bin jetzt bei 4 Wochen Laufzeit... So wie es aussieht, ist es aber bald vorbei und muß geladen werden. Ich lasse mir die Daten drei mal in der Std. auf die CCU senden.
Du schreibst dass Du 4 Monate mit 2500 Ah auskommst? Wie oft werden bei Dir die Daten übetrtragen?

dondaik
Beiträge: 12927
Registriert: 16.01.2009, 18:48
Wohnort: Steingaden
Hat sich bedankt: 1603 Mal
Danksagung erhalten: 222 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von dondaik » 06.06.2017, 12:03

könnte das schon mal helfen?
#define SLEEP_TIME_MEASURE_SEC 30 // Messintervall in sec, default 290 sekunden, zum Testen 30 sekunden
#define MEASURE_UPDATE_CNT 3 // Mess/Update Verhätnis, alle 3 mal wird zwangsgesendet, Vorschlag fuer default ist 6 mal
-------
!!! der download der handbüchern auf den seiten von eq3 und das lesen der tips und tricks kann das hm-leben sehr erleichtern - das nutzen der suche nach schlagworten ebenso :mrgreen: !!!
wer schreibfehler findet darf sie behalten.

fsommer1968
Beiträge: 230
Registriert: 16.02.2008, 17:05
Danksagung erhalten: 9 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von fsommer1968 » 06.06.2017, 13:01

dondaik hat geschrieben:könnte das schon mal helfen?
#define SLEEP_TIME_MEASURE_SEC 30 // Messintervall in sec, default 290 sekunden, zum Testen 30 sekunden
#define MEASURE_UPDATE_CNT 3 // Mess/Update Verhätnis, alle 3 mal wird zwangsgesendet, Vorschlag fuer default ist 6 mal
Könnte helfen, aber nicht bei knopers1 :D

Hallo Knopers1,
wenn man einen Widerstand oder eine Schottky-Diode anstelle einer Drahtbrücke nimmt, kann man das Modul jederzeit flashen ohne irgendwas entfernen zu müssen. Man kann dann das Modul (batteriebetrieben) auch aus dem Sketch heraus per OTA neu programmieren. In meinem Sketch oben habe ich das insoweit vorgesehen. Es fehlt nur noch der Aufruf der Flash-Routine (und natürlich ein Webserver der das Image ausliefern kann). Mit einem Widerstand hatten Leute speziell in diesem Forum Probleme gemeldet, z.B. auch der Kollege dondaik. Deshalb eine Schottky-Diode (vgl. wired and/or Schaltung).

Die geringe Laufzeit in deinem Fall ist aus meiner Sicht darauf zurückzuführen, daß Du die Adafruit BME Library verwendest ohne den BME im Force-Mode zu initialisieren. Der BME280 kennt mind. 2 Modi Normal Mode und Force Mode. Im Normalmode (default bei den meisten Libraries) ist der BME280 immer "an" und misst ständig. Das ist für Anwendungen wie Inroom-Navigation gedacht. Dadurch verbraucht er relativ viel Strom.
Für Anwendungen bei den denen sich die Werte langsam ändern wie bei Wetterstationen gibt es den Force-Mode. Hier misst, vereinfacht gesagt, nach dem Initialisieren der BME280 nur einmal und legt sich dann, stromsparend, schlafen. Per Reset kann er wieder aufgeweckt werden.

Abhilfe bei Dir:
Entweder meinen Sketch nehmen oder Deinen Sketch umbauen und den BME280 im Force Mode initialisieren.
Die Routine in meinem Sketch hierzulautet: config_bme():

Code: Alles auswählen

  unsigned long measuringTime = 25; //for Sensor measuring without readout in ms
 // in forced mode with oversampling=1 in all 3 values the max measuring time should be < 22.8ms
 // for other oversampling settings refer Bosch datasheet chapter 9.1 Measurement time
 // in forced mode with all oversamplings to x16 (max value) the measring time should be <112,8

// need to read the NVM compensation parameters
   BME280.readCompensationParams();
   // Need to turn on 1x oversampling, default is os_skipped, which means it doesn't measure anything
   BME280.writeOversamplingPressure(os1x);     // 1x over sampling (ie, just one sample)
   BME280.writeOversamplingTemperature(os1x);  // 1x over sampling (ie, just one sample)
   BME280.writeOversamplingHumidity(os1x);     // 1x over sampling (ie, just one sample)
   measuringTime = (int) (0.5 + (1.25 + (2.3 * 1) + (2.3 * 1 + 0.575) + (2.3 * 1 + 0.575))); // measuringTime is unsigned long
   /* in forced mode with oversampling=1 in all 3 values the max measuring time should be < 22.8ms
      in forced mode with all oversamplings to x16 (max value) the measring time should be <112,8
      for other oversampling settings refer Bosch datasheet chapter 9.1 Measurement time
      measuringTime = 1.25+(2.3*temp_os) + (2.3*humid_os + 0.575) + (2.3*press_os + 0.575)  */
   // init a forced sample.  After taking the measurement the chip goes back to sleep
   BME280.writeMode(smForced);
   delay(measuringTime + 25); // wait until measurement is done. With oversampling=1 in all 3 values the max measuring time should be < 22.8ms
   // for other oversampling settings refer Bosch datasheet chapter 9.1 Measurement time
   //BME280.readMeasurements();
   
Du brauchst dazu die BME280 Bibliothek BME280_MOD-1022.h von https://github.com/embeddedadventures/BME280
Mit dieser Bibliothek kann man den BME dann so auslesen:

Code: Alles auswählen

BME280.readMeasurements();
   if (BME280.isMeasuring()) { // this should not happen because wait time is long enough
     Serial.println("Lesefehler BME280");
     return(false);
   }
   float temperature = BME280.getTemperatureMostAccurate();
   float humidity = BME280.getHumidityMostAccurate(); 
   float pressurebme = BME280.getPressureMostAccurate();

knopers1
Beiträge: 96
Registriert: 03.09.2014, 22:04

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von knopers1 » 06.06.2017, 14:03

woow, das hört sich echt toll an. Ich werde dein Sketch zeitnah ausprobieren. :D

dondaik
Beiträge: 12927
Registriert: 16.01.2009, 18:48
Wohnort: Steingaden
Hat sich bedankt: 1603 Mal
Danksagung erhalten: 222 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von dondaik » 06.06.2017, 14:48

frage: wo hat der BH1750 seinen reset-pin ? --> vcc, gnd, sca , scl, adr. / bei meinen 1750
eigentlich haben alle drei sensoren keinen Reset-eingang - oder ?

ach: gibt es eine idee für das anbringen der sensoren ? der UV-sensor mag die sonne - und der 1750 geht dann ins limit .... zb blende für den 1750.

ps.: ja, eigentlich verstoße ich gegen die überschrift-... keine fragen.
-------
!!! der download der handbüchern auf den seiten von eq3 und das lesen der tips und tricks kann das hm-leben sehr erleichtern - das nutzen der suche nach schlagworten ebenso :mrgreen: !!!
wer schreibfehler findet darf sie behalten.

fsommer1968
Beiträge: 230
Registriert: 16.02.2008, 17:05
Danksagung erhalten: 9 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von fsommer1968 » 06.06.2017, 17:30

Hallo dondaik,

der Reset-Pin ist tatsächlich nicht rausgeführt, sondern Pin 4 (DVI) des Chips. Da hängt bereits ein Pull-down Widerstand dran. Über das Sketch wird der via Open-Drain Konfiguration für den BHRESETPIN für 2 ms oder so nach GND gezogen.

Bei mir hängen die BH1750, wie der UV Sensor auch, in direktem Sonnenlicht nur durch eine dünne Glasscheibe (Objektträger) vor Regen geschützt. Wie soll er sonst die Sonneneinstrahlung messen?

kafisc
Beiträge: 131
Registriert: 08.09.2015, 15:14
Hat sich bedankt: 18 Mal
Danksagung erhalten: 4 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von kafisc » 13.06.2017, 09:28

Vielen Dank @fsommer1968 für den tollen Sketch und die ganze Arbeit.
Ich fände es Klasse, wenn man einen Regensensor integrieren könnte. Beispielweise fällt mir da der YL-38 ein.

Viele Grüße
kafisc

fsommer1968
Beiträge: 230
Registriert: 16.02.2008, 17:05
Danksagung erhalten: 9 Mal

Re: Batterie-Kombi-Wettersensor Arduino 8266 wie Wemos an CC

Beitrag von fsommer1968 » 13.06.2017, 20:13

Hallo kafisc,

interessante Ergäzung die Du vorschlägst. Im Prinzip ist das ein low Brainer und schnell gemacht.
Ohne zus. Hardware geht das aber nur mit "Polling": Alle x Sekunden (Minuten) wird der Sensor geprüft ob Regen erkannt wurde. Wenn Regen erkannt wurde, wird das an die CCU bzw. CuxD gemeldet (und z.B. erst wieder wenn es aufgehört hat zu regnen). Das bedeutet daß zwischen den ersten Regentropfen und dem "Polling-Zyklus" etwas Zeit vergeht, im ungünstigsten Fall die eingestellen Sekunden oder Minuten Zykluszeit. Ist das so wie Du Dir das vorstellst?
Bedenken habe ich wegen dem Stromverbrauch für den YL-38. Der zieht mind. 2 mA plus Strom für die beiden LED die auf der Platine sind. Das ist evtl. nur bei Dauerbetrieb gut zu machen (ob ein Ausgang des 8266 als Spannungsquelle für den Sensor verwendet werden kann muß man mal ausprobieren).
Ich habe keinen YL-38, ich kann deshalb den Sketch nur ergänzen und testen müsste das jemand der entspr. Hardware (Sensor) hat.


-Frank

Antworten

Zurück zu „HomeMatic Tipps & Tricks - keine Fragen!“