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
}
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