DIY-Wetterstation mit dem ESP8266 (2018)

Vorgeschichte

Nachdem ich bereits diverse Elektronikprojekte umgesetzt hatte, entstand im Jahr 2018 die Idee ein wenig mehr mit Sensoren zu experimentieren. Da der Bau meiner Innenraumtemperatursensoren* nun schon einige Jahre her war und ich zwischenzeitlich immer mal wieder Temperatursensoren in anderen Projekten eingesetzt hatte, dachte ich mir das es diesmal ruhig ein wenig mehr als nur eine Temperaturmessung sein darf. Damit war die Idee der DIY-Wetterstation geboren, welche ich hier kurz vorstellen möchte.

Leider habe ich das Layout der Platine und die Verkabelung der Sensoren damals „on the fly“  erstellt und nicht dokumentiert, weshalb ich leider keinen detaillierten Schaltplan liefern kann. Da die Pin-Belegung der Sensoren aber nicht sonderlich kompliziert ist, denke ich das es kein Problem sein sollte, die Wetterstation auch ohne einen Schaltplan komplett, oder auch nur teilweise nachzubauen. Falls dennoch jemand Unterstützung benötigen sollte, dann kann er sich gern per Mail unter info@langer-sebastian.de an mich wenden.      

 

Überlegungen vor Projektumsetzung

Bevor ich mit dem Bau der Wetterstation begonnen habe, überlegte ich mir erst einmal, was ich denn alles messen und anzeigen wollte. Außerdem stand für mich von Anfang an fest, dass ich die Messdaten nicht nur anzeigen, sondern auch dauerhaft speichern möchte, um mir zum einen die Auswertung auch über einen längeren Zeitraum anschauen zu können und zum anderen die Messdaten analysieren und so evtl. Vorabschätzung für künftiges Wetter machen zu können. Bei der Auswahl der Sensoren orientierte ich mich daran, was Wetterseiten im Internet an Messwerten bereitstellten und was für Sensoren käuflich verfügbar waren. Dies führte recht schnell zur Liste der gewünschten Messgrößen, welche sich wie folgt zusammensetzt:

  • Temperatur
  • Luftfeuchtigkeit    
  • Luftdruck
  • UV-Strahlung (Sonneneinstrahlung)
  • Helligkeit
  • Feuchtigkeit
  • Niederschlagsmenge
  • Windgeschwindigkeit
  • Windrichtung

 

Hardwareauswahl

Bei der Auswahl der restlichen Hardwarekomponenten musste ich meine vorab festgelegten Wünsche dahingehend berücksichtigen, dass ich die Messdaten von der Wetterstation irgendwie „herunterladen“ können muss, um sie für die Langzeiterfassung abspeichern zu können. Dies sollte natürlich so einfach wie möglich ablaufen.

Ich war mir relativ sicher, dass ich wahrscheinlich keine Lust haben werde die Daten in regelmäßigen Abständen manuell zu sichern, weshalb eine Lösung mittels verbautem Datenspeicher (SD Karte, USB-stick, etc…) von Anfang an ausgefallen ist. Die bereits häufiger zum Einsatz gekommenen 433MHz Funkmodule wollte ich für die Menge an zu übertragenden Daten ebenfalls nicht nutzen, da ich mit den vorhandenen Innenraumtemperatursensoren* schon genügend 433Mhz „Sender“ hatte und auch die bei meiner DIY-Hausautomatisierung* verwendeten Funksteckdosen auf 433MHz ihre Befehle empfangen. Aus diesem Grund brauchte ich eine andere Art der Übertragung. Hierfür fiel mir der ESP8266* ein, mit welchem ich schon einige Male herumgespielt und zum Beispiel eine Wifi-Anwesenheitserkennung umgesetzt hatte.

Dieser Micro-Controller hat neben den bereits vom Arduino bekannten Digital-Ein- und Ausgängen den großen Vorteil, dass er bereits „ab Werk“ über eine WLAN-Schnittstelle verfügt und viele Bibliotheken, welche für den Arduino verfügbar sind, auch auf dem ESP laufen, oder bereits als eigenständige ESP-Versionen umgesetzt wurden. Das spart beim Erstellen von Projekten Zeit und Mühe, welche beim Anbinden der Sensoren sonst in Form von Anlegen und Debuggen eigener Bibliotheken entstanden wäre. Da ich die Wetterstation sowieso nicht allzu weit entfernt von meiner Wohnung aufstellen wollte, konnte ich mit der begrenzten Reichweite der Wifi-Verbindung locker leben. 

            

Der in der Wetterstation verwendete ESP-8266 (ESP-12E)

 

Bei den Sensoren für die Messwerte wie Temperatur, Luftfeuchtigkeit und Luftdruck stand die Auswahl der Hardware ebenfalls recht zügig fest. Für die Messung von Temperatur und Luftfeuchtigkeit nahm ich den bereits häufiger verwendeten DHT22, für den Luftdruck nutze ich den BMP085, welcher ebenfalls über einen Temperatursensor verfügt. Die UV-Strahlung erfasse ich mit Hilfe eines ML8511, die Helligkeit durch eine normale Fotodiode und die Taufeuchtigkeit mittels eines Tropfensensors. Dies alles sind Sensoren, die entweder ohne besondere Bibliothek auskommen (analoger Ausgang), oder für die bereits eine Bibliothek vorhanden war.

Bei den Sensoren für die Niederschlagsmenge, die Windrichtung und die Windstärke nutzte ich Sensoren einer fertigen Wetterstation (WH-1080). Da ich nur die Sensoren und nicht die Hardware zum Auswerten der Messwerte benötigte, suchte ich nach „Ersatzteile für WH-1080“ und wurde fündig. Damit war nun also auch die Frage der Sensoren erledigt und es konnte ans Thema Spannungsversorgung gehen.    

     

433MHz Temperaturfühler mit Arduino Quelle: Amazon.de








 

Autarke Spannungsversorgung der Wetterstation

Da ich die Aufgabe der Datenübertragung bereits kabellos gelöst hatte, wollte ich für die Spannungsversorgung der Wetterstation natürlich auch kein Kabel verlegen müssen. Die Idee war die Spannungsversorgung des Micro-Controllers und auch der angeschlossenen Sensoren per Lithium-Ionen-Akku zu realisieren. Damit man zum Aufladen des Akkus ebenfalls nicht Hand an die Wetterstation anlegen muss, sollte der Akku per Solarpanel geladen werden, womit die Spannungsversorgung dann ebenfalls „wartungsfrei“ umgesetzt wäre.

Da unser Balkon an der Westseite des Hauses liegt und hier wenn überhaupt erst ab Mittag die Sonne anliegt, hatte ich die Befürchtung, dass die erzeugte Energie des Solarpanel nicht ausreichen würde um den Akku auch an trüben Tagen oder über die Wintermonate ausreichend nachladen zu können. Aus diesem Grund kaufte und verbaute ich 2 Panels, doch lieferten diese anscheinend nicht die angegebene Leistung, was dazu führte, dass der Wetterstation nach einigen Wochen trotzdem „der Saft“ ausging.

Aus diesem Grund tauschte ich im Frühjahr 2022 die alten Panels gegen zwei qualitativ bessere aus und bin bis jetzt damit mehr als zu frieden. Diese versorgen ein in der Wetterstation verbautes TP-4056* Ladeboard, welches dann wiederum den Lithium-Ionen-Akku lädt, bis dieser voll ist.

 

 

Da der Ladevorgang bereits in der Früh gegen 7:00Uhr beginnt und im Schnitt schon gegen 10:00Uhr die Ladeschlussspannung erreicht wird (also genau in der Zeit wo noch keine direkte Sonneneinstrahlung auf die Solarpanels trifft), gehe ich davon aus, dass ich hier genügend Reserven habe um auch mal über mehrere Schlechtwettertage zu kommen. Da der ESP8266* doch etwas „hungriger“ ist als ein normaler Micro-Controller, habe ich natürlich auch softwareseitig dafür gesorgt, dass der Verbrauch minimiert wird. Dazu später mehr!   

 

Spannungskurve des Lithium-Ionen-Akku vom 22.05.2022
Spannungskurven des Lithium-Ionen-Akku zwischen 04.05.2022 und 22.05.2022

 

Die verwendeten Solarpanels, welche ursprünglich zum Aufladen von im Außenbereich angebrachten Überwachungskameras gedacht waren, hatten im Lieferumfang eine Wandhalterung, mit welcher ich allerdings nicht viel anfangen konnte. Da ich sie an der gleichen Position wir die alten Panels montieren wollte, habe ich mit dem 3D-Drucker zwei neue Aufnahmen konstruiert, welche ich anschließend auf die Rückseite der Panels klebte. Die alten Panels hielten so die ganze Zeit über und auch die Neuen haben schon die ersten Sturmböen hinter sich, weshalb ich denke, dass die Art der Befestigung ausreichend ist. 

 

Die 2022 neu konstruierte Aufnahme für die größeren Solarpanels

 

ESP8266 Sleepmode, denn „Strom sparen“ war nötig 

Wie bereits erwähnt, ist der ESP nicht gerade das große Stromsparwunder unter den Micro-Controllern. Gerade beim Verbinden mit dem Netzwerk, oder dem  Senden von Daten steigt der Verbrauch gern mal in Richtung 200mA, was bei meiner autarken Stromversorgung natürlich nicht soll toll gewesen wäre. Der Lösungsansatz hierfür war, den ESP in den Sleep-Mode zu schicken und ihn nur alle 15min „aufzuwecken“. Nach dem Wiedereinschalten und Herstellen der Netzwerkverbindung werden die Messdaten der Sensoren abgerufen und per WLAN ins Netzwerk übertragen, um anschließend wieder für 15min in den Sleep-Mode zu gehen. Das hat den großen Vorteil, dass in dieser Zeit der Micro-Controller nur wenige µA verbraucht und so der verwendete Akku nur kurzzeitig belastet wird. 

Der Einsatz des Sleep-Modes hat sich in ähnlicher Form bereits bei meinen Arduino Funk-Temperatursensoren* bewährt und das 15 minütige Intervall der Messwertabfrage ist für die meisten Sensordaten auch vollkommen ausreichend. Problematisch wurde es aber bei den Messwerten der Windgeschwindigkeit und der Niederschlagsmenge. Die Windgeschwindigkeit nur alle 15min für x-Sekunden zu messen wäre noch einigermaßen vertretbar gewesen, wenn gleich auch eventuelle kurzzeitig auftretende Böen so nicht erfasst worden wären, doch die Wippe des Regensensors hält sich an keinen Zeitplan und löst aus, sobald das oben stehende Reservoir mit ausreichend Wasser gefüllt ist. 

Jetzt hätte man für die Lösung des Problems einen Interrupt nutzen können, welcher den ESP8266 „aufweckt“, eine Variable erhöht und den Chip dann wieder „schlafen“ schickt, doch entschied ich mich für eine andere Lösung.   

 

Microchip ATtiny45 im Dauerbetrieb 

Um die Messwerte für die Windgeschwindigkeit, Windrichtung und Niederschlagsmenge dauerhaft erfassen zu können, verwendete ich zwei weitere Micro-Controller der Firma Microchip mit der Bezeichnung ATtiny45. Diese kleinen Micro-Controller benötigen selbst im eingeschalteten Zustand weniger als 0,5mA, und eignen sich daher hervorragend für den Batteriebetrieb. Um alle 15 Minuten die Messwerte von den Tinys zum ESP übertragen zu können, nutze ich den I2C-Bus, wobei der ESP8266 als Master und die beiden Tinys jeweils als Slave fungieren.
Der ESP ruft also nach dem Start-Up die Messwerte der Tinys ab und sendet diese anschließend mit den am ESP selbst gemessenen Sensorwerten an das Verarbeitungsscript im Netzwerk. Das Abrufen der Daten führt in den Tinys zum Zurücksetzen der Messwertzähler, wodurch die Messwertaufzeichnung für die  nächsten 15min beginnt.

Leider besitzt der ATtiny45 nur 8-Pins. Durch die Verwendung des I2C-Bus (2 Pins), die Spannungsversorgung (2 Pins), den Reset-Pin und eine verwendete Status-LED waren leider nicht mehr genügend Pins vorhanden, um alle 3 Sensoren mit einem ATtiny45 abfragen zu können. Nach meinem heutigen Wissensstand und dem Weglassen der Status-LED wäre dies aber wahrscheinlich ebenfalls möglich sein!   

ATtiny45 in der verwendeten DIP8 Ausführung

 

Sonstige Bauteile und das Anfertigen der Hauptplatine 

Für die Hauptplatine verwendete ich, wie fast immer, eine einfache 2,54mm Lochrasterplatine. Da ich für den ESP8266* keinen Sockel hatte und die Abstände der Lötpads am Chip auch nicht im Raster von 2,54mm ausgeführt sind, nutze ich für dessen Montage kurze Kabelverbindungen. Das „Sockeln“ war damit zwar etwas langwieriger, funktioniert aber super!

Da der ESP8266* lediglich einen Analogeingang besitzt, ich aber mehrere Analogsensoren verbauen wollte, brauchte ich eine Möglichkeit den Analogeingang „mehrfach“ zu benutzen. Hierfür gibt es sogenannte Multiplexer-ICs. Diese bieten als Anschluss mehrere IO-Pins, welche auf einen IO-Pin „geroutet“ werden können, je nachdem wie die Steuer-Pins des IC gerade beschaltet sind. Ich entschied mich für den CD74HCT4051E* von Texas Instruments. Dieser bietet 8 Kanäle, zwischen welchen über 3 Steuer-Pins durch das Anlegen von verschiedenen High/Low-Pegel Konfigurationen leicht hin und her geschaltet werden kann.    

 

CD74HCT4051E

 

Damit war das Auslesen aller Analogsensoren am Analog-Pin des ESP problemlos möglich und auch die Unterscheidung der einzelnen Sensoren ist anhand der Steuer-Pin Konfiguration eindeutig identifizierbar. Des Weiteren galt es zu beachten, dass der ESP8266* lediglich einen Pegel von maximal 1V am Analogeingang tolerierte, weshalb ich für jeden Sensor einen Spannungsteiler benötigte.

Vorgreifender Exkurs:

Zur Kalibrierung der Analogmessung und um etwaige Leitungsverluste mit einzuberechnen, verlegte ich die Sensorleitungen um anschließend mittels Netzteil die maximale Sensorspannungen an die Leitungsenden anzulegen. Per Potentiometer stellte ich einen der beiden Vorwiderstände so ein, dass ich am Eingang des ESP genau auf 1,0V kam, was ich sowohl per digitalem Multimeter, als auch per Ausgabe des Analogwertes des ICs selbst überprüfte. Anschließend baute ich das Potentiometer wieder aus, ermittelte den eingestellten Widerstandswert und lötete einen festen Widerstand mit dem selben Wert auf die Platine. Dieses vorgehen ist vielleicht etwas fummelig und umweltbedingte Schwankungen sind damit auch nicht abgedeckt, doch ist für meine Zwecke der damit ermittelte Messwert genau genug.  

KOMMENTAR DES AUTORS

 

Somit standen alle benötigten Bauteile fest und konnten mit ein paar weiteren Bauteilen auf die Lochrasterplatine gelötet werden. Dies hab ich damals, wie bereits beschrieben, „on the fly“ und ohne vorheriges Anfertigen eines Schaltplanes umgesetzt. Das spart im Moment des Erstellens zwar Arbeit und auch ein wenig Zeit – möchte man jedoch später einmal kontrollieren, ob nicht doch noch etwas geändert werden muss, dann ist man mit dem „Re-Engineering“ der eigenen Schaltung so lang beschäftigt, dass sich die Zeitersparnis schneller ins Gegenteil verkehrt als man schauen kann. Aus diesem Grund fertige ich seit einiger Zeit zu jeder „Bastelei“ immer einen Schaltplan mit an.

     

Vorderseite der bestückten Lochrasterplatine
Rückseite der bestückten Lochrasterplatine

 

Befestigung der Sensoren

Jetzt benötigte ich für die Komponenten noch die entsprechenden Halterungen.

Für die Befestigung der Windsensoren und des Regensensors fand ich im Internet ein Seite, auf welcher man ebenfalls Ersatzteile aller Montagehalterungen der Wetterstationssensoren kaufen konnte. Hier bestellte ich mir zweimal den Satz aller Halterungen.
In einem Satz war hierbei jeweils der Halter für den Temperatursensor und die Traverse für die Windsensoren enthalten. Der Preis pro Halterungs-Satz lag unterhalb 10€, was auch der Grund war, weshalb ich davon abgesehen habe alle Halterungen mit dem 3D-Drucker zu erstellen. Für die „paar Euro“ war mir der Aufwand jede Halterung selbst konstruieren und ausdrucken zu müssen einfach zu groß.

Auf einer der gekauften Traversen befestigte ich die beiden Windsensoren, da dies ja sowieso der originale Befestigungspunkt für die Sensoren war. Die andere Traverse nutzte ich als Halterung der beiden Solarpanels. Die Aufnahme der oben bereits gezeigten Panel-Halterung habe ich so designed, dass diese die gleiche Form wie die Aufnahme der Windsensoren hat. Das hat den Vorteil, dass ich die Panels bei Bedarf ebenfalls von der Halterung demontieren kann. Die beiden übrigen Halter für den WH1080-Termperatursensor, welchen ich ja nicht besitze, nahm ich für die Befestigung des Regenmengensensors und des Gehäuses mit dem BMP-Temperatursensor und dem UV-Sensor, zu welchem ich gleich noch komme.

 

Gehäuse für die Komponenten

Jetzt fehlten nur noch die Gehäuse für die Elektronik. Hierfür bestellte ich einfach spritzwassergeschützte Universalgehäuse, welche man in allen erdenklichen Größen bei Amazon oder Ebay kaufen kann. Im größeren der beiden Gehäuse fand die Hauptplatine, das Ladeboard inkl. Akku und die Fotodiode für die Helligkeitsmessung ihren Platz.

  

Die im Gehäuse verbaute, angeschlossene Hauptplatine inkl. Ladeboard und Akku

 

Da dieses Gehäuse mit den genannten Komponenten und den Anschlussleitungen schon recht gut gefüllt war, nutzte ich für die Unterbringung der Sensoren ein zweites, kleineres Gehäuse. Für den UV-Sensor bohrte ich in den Deckel ein Loch und klebte anschließend eine UV-beständige Plexiglasscheibe ein, durch welche das Sonnenlicht auf den Sensor fallen kann. Daneben befestigte ich den Feuchtigkeitssensor, welchen ich geneigt positionierte, damit das Wasser ablaufen kann und nicht dauerhaft auf dem Sensor steht. Den DHT22 führte ich unten aus dem Gehäuse heraus. Um den Sensor vor direkter Sonneneinstrahlung und Wind zu schützen, druckte ich eine Abdeckung, welche ich anschließend über den Sensor schob und am Gehäuse der Sensoren befestigte.

 

Sensorgehäuse mit UV-Sensor und Feuchtigkeitssensor

 

Als Mast, an welchem alle Komponenten befestigt werden konnten, besorgte ich mir eine Metallstange (Gardinenstange) aus dem Baumarkt. Diese passte vom Durchmesser super, sodass ich alle Originalhalterungen problemlos daran befestigen konnte. Für die Befestigung des Platinengehäuses druckte ich mir eigene Halter (2 U-Schalen), mit welchen ich das Gehäuse auf gleiche Weise am Mast befestigte. 

Damit ich für die Befestigung des Mastes ebenfalls keine Löcher bohren musste, befestigte ich diesen mit Rohrschellen auf einem Holzbrett, welches wiederum mittels Haken einfach in die Geländerbrüstung des Balkons eingehangen wurde. Damit war der Hardwareteil soweit abgeschlossen und es konnte mit dem Erstellen der Software für die Anzeige und Auswertung der Messdaten weitergehen (siehe extra Beitrag).

 

Die fertige Wetterstation an ihrem Stammplatz

 

Micro-Controller-Software der Wetterstation

Die Software auf dem ESP8266

Die Software des ESP8266 ist eigentlich recht simpel. Nach dem erfolgreichen Herstellen der WLAN Verbindung werden die Messwerte der Sensoren ausgelesen, die Daten der beiden ATtiny45 abgerufen und anschließend alles per Aufruf einer Webseite auf meinem NAS via HTTP_GET zur weiteren Verarbeitung übertragen. Ist das geschehen, geht der ESP für ca. 15min in den Tiefschlaf um dann per Reset wieder geweckt zu werden. 

Für das Auslesen der Analogsensoren über den Multiplexer habe ich mir eine Funktion erstellt, welcher ich die Steuer-Pin Konfiguration des jeweils auszulesenden Sensors übergebe und alle Messungen über 100 Werte mittele. Anschließend gebe ich den gemessenen und bereits gemittelten Wert via return an die aufrufende Funktion zurück.

 

//deactivated for brightness measurement
ADC_MODE(0);

// include libraries
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Time.h>  
#include "DHT.h"
#include <Wire.h>
#include <Adafruit_BMP085.h>
#define RST 16

// define variables
Adafruit_BMP085 bmp;
DHT dht;  // import DHT as dht
String Device = "ESP-Wetterstation";  // device name for multiple devices
float fl_hum;  // humidity
float fl_temp;  // temperature
float pres_mbar;  // air pressure
float pres_mbar_sl;  // air pressure sea level
float pres_temp;  // temperature from pressure sensor
float pres_alt;  // calculated pressure by altitude
String windValue;  // string for sending wind value via URL
String wString = "";  // string to concatenate windspeed values
int rainSeaSaw;  // value of rain seasaw
int windSpeed;  //  value of windspeed

// select pins for ADC port expander
int S0 = 4;
int S1 = 0;
int S2 = 2;

bool BMP_found = true; // flag if air pressure sensor is ready


//SETUP PART
//NOTE: everything in setup gets executed after deep sleep reset
void setup()
{
    //
    // SETUP SERIAL CONNECTION, PINS AND SENSORS
    //

    // Serial
    Serial.begin(115200);

    // some restart time
    delay(1000);
    
    Wire.begin(13,12);

    // pre-check if pressure sensor is present
    if (!bmp.begin())
    {
        Serial.println("Could not find a valid BMP085 sensor, check wiring!");
        BMP_found = false;  // set flag to false to avoid boot loops
    }
    else
    {
        Serial.println("Found BMP085 sensor!");
        BMP_found = false;  // set flag to false to avoid boot loops
    }

    // set pins for ADC expander as outputs
    pinMode(S0, OUTPUT);
    pinMode(S1, OUTPUT);
    pinMode(S2, OUTPUT);

    // setup DHT pin
    dht.setup(5);

    //
    // SETUP AND CONNECT WIFI
    //

    // set ESP IP configuration data
    IPAddress ip(192, 168, 2, 228);
    IPAddress gateway(192, 168, 2, 1);
    IPAddress subnet(255, 255, 255, 0);

    // setup wifi connection mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(1000);

    // scan for wifi networks
    int amountNetworks = WiFi.scanNetworks();

    // WiFi settings
    const char* ssid = "WLAN-......";  // wifi ssid (name of network)
    const char* password = ".........";  // wifi password
    bool foundNetwork = false;  // flag for found given network
    int connectTimeout = 120;  // connection timeout

    // check if networks are nearby
    if (amountNetworks == 0)
    {
        // sleep for 1 min
        Serial.println("No networks found! --> enter sleep mode for 1 min!");  // serial debug message
        ESP.deepSleep(60000000);  // enter sleep mode for 1 min
    }
    else
    {
        // iterate over networks
        for (int i = 0; i < amountNetworks; ++i)
        {
            // check if actual network ssid matches given network ssid
            if (WiFi.SSID(i) == ssid)
            {
                Serial.println("Found network: " + String(ssid));  // serial debug message
                foundNetwork = true;  // set flag that network was found to true
            }
            delay(10);  // wait 10ms
        }

        // when ssid wasn't found in networks nearby --> foundNetwork isn't true
        if(!foundNetwork)
        {
            Serial.println("SSID not found in networks! --> enter sleep mode for 1 min!");  // serial debug message
            ESP.deepSleep(60000000);  // enter sleep mode for 1 min
        }
        else
        {
            WiFi.config(ip, gateway, subnet);  // configure wifi connection
            WiFi.begin(ssid, password);  // try to connect to network
        }
    }

    // check if wifi is already connected. If not --> loop until connectTimeout is reached
    while (WiFi.status() != WL_CONNECTED)
    {
        // check if connection timeout isn't reached
        if (connectTimeout > 0)
        {
            connectTimeout--;  // decrease connectTimeout
            Serial.print( "[" + String(connectTimeout) + "]");  // serial debug message
            delay(1000); // wait a second
        }
        else
        {
            Serial.println("Timeout on connecting / login to wifi --> enter sleep mode for 1 min!");  // serial debug message
            ESP.deepSleep(60000000);  // enter sleep mode for 1 min
        }
    }
}


// MAIN PROGRAM LOOP
void loop() 
{
    // get minimum sampling period
    delay(dht.getMinimumSamplingPeriod());
    fl_hum = dht.getHumidity();  // read humidity and set to variable
    fl_temp = dht.getTemperature();  //read temperature and set to variable

    // read voltage over voltage divider --> switched already at the end of last loop before deep sleep
    int batVoltage = readAnalogSensor(LOW, LOW, LOW);

    // calculate battery voltage (5,1V battery == 1024)
    float voltage_calc = 5.1 / 1024 * batVoltage;

    //read analog value from rain humidity board      
    int rain = readAnalogSensor(HIGH, LOW, LOW);

    // read analog value of photo diode
    int brightness = readAnalogSensor(LOW, LOW, HIGH);

    // measure uv reference voltage 
    int uV_Delta = readAnalogSensor(HIGH, HIGH, LOW); 

    // measure uv value and calculate real uv sensor voltage
    int uV_Measure = readAnalogSensor(LOW, HIGH, LOW); 
    float uV_Voltage      = 3.3 / uV_Delta * uV_Measure;

    // calculate uv intensitiy 
    float uV_Intensity = mapfloat(uV_Voltage, 0.99, 2.8, 0.0, 15.0);

    // BMP180 preasure sensor
    // check if sensor is ready (to avoid bootloops when not connected)
    if (BMP_found)
    {
        pres_temp = bmp.readTemperature();
        pres_mbar = float(bmp.readPressure()) / 100;
        pres_mbar_sl = float(bmp.readSealevelPressure()) / 100;
        pres_alt = bmp.readAltitude(101800);
    }

    // read value from ATTiny for rain sensor seasaw 
    // SLAVE address = 0x3
    Wire.requestFrom(3,1);

    while(Wire.available())
    {
        rainSeaSaw = Wire.read();
    }

    // read value from ATTiny for wind speed measurement
    //SLAVE Adresse = 0x2 -> Adresse 2
    Wire.requestFrom(2,30);

    Serial.println("Windspeed --> amount of bytes: " + String(Wire.available()));

    while(Wire.available())
    {
        windSpeed = Wire.read();  // read windspeed bytewise
        wString.concat("Windspeed: ");
        wString.concat(windSpeed);        

        windValue = windValue + windSpeed + ".";
    }

    // get wifi IP adress
    String iP_act = WiFi.localIP().toString();

    // for debuging print sensor values to serial
    Serial.println("");
    Serial.println("Wifi connected! IP: " + iP_act);
    Serial.println("Battery voltage: " + String(voltage_calc) + "V");
    Serial.println("Temperature DHT22: " + String(fl_temp) + " °C");
    Serial.println("Temperature BMP: " + String(pres_temp) + " °C");
    Serial.println("Pressure BMP: " + String(pres_mbar) + " mbar");
    Serial.println("Pressure BMP (sea level): " + String(pres_mbar_sl) + " mbar");
    Serial.println("Altitude BMP: " + String(pres_alt) + " m");
    Serial.println("Humidity DHT22: " + String(fl_hum) + " %" );
    Serial.println("Brightness: " + String(brightness) + " " );
    Serial.println("Rain / Humidity: " + String(rain) + " " );
    Serial.println("Count rain seasaw: " + String(rainSeaSaw) + " ");
    Serial.println("Uv Measurement: " + String(uV_Measure) + " ");
    Serial.println("Uv Intensity: " + String(uV_Intensity) + " mW/cm^2");
    Serial.println(wString);

    //call website and send payload
    WiFiClient client;
    HTTPClient http;

    if (http.begin(client, "http://192.168.2.198:8000/SHAS/Weatherstation.php?Device="+Device+"&IP="+iP_act+"&VCC="+String(voltage_calc)+"&Temp="+String(fl_temp)+"&Feuchte="+String(fl_hum)+"&Helligkeit="+String(brightness)+"&Regen="+String(rain)+"&UV="+String(uV_Intensity)+"&Temp_DS="+String(pres_temp)+"&p_DS="+String(pres_mbar)+"&p_DS_SL="+String(pres_mbar_sl)+"&p_Alt="+String(pres_alt)+"&RMS_Wippe="+String(rainSeaSaw)+"&Windgeschwindigkeit="+String(windValue)))
    {
        Serial.print("[HTTP] GET...\n");
        
        // start connection and send HTTP header
        int httpCode = http.GET();

        // httpCode will be negative on error
        if (httpCode > 0)
        {
            // HTTP header has been send and Server response header has been handled
            Serial.printf("[HTTP] GET... code: %d\n", httpCode);
    
            // file found at server
            if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
            {
                String payload = http.getString();
                // Serial.println(payload);
            }
        }
        else
        {
            Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }
        http.end();
    }
    else
    {
        Serial.printf("[HTTP} Unable to connect\n");
    }

    // set ADC expander input configuration to low
    digitalWrite(S0,LOW);
    digitalWrite(S1,LOW);
    digitalWrite(S2,LOW);

    // sleep 100ms to set ADC pins properly
    delay(100);

    //call soft reset function
    softReset();
}


int readAnalogSensor(uint8_t val_S0,uint8_t val_S1,uint8_t val_S2)
{
    // set ADC expander to specific pin to measure different sensors by one analog ic pin
    digitalWrite(S0,val_S0);
    digitalWrite(S1,val_S1);
    digitalWrite(S2,val_S2);
        
    // some time for switch on voltage of sensor boards
    delay(500);

    int value = 0;

    // read analog value of rain humidity board --> read and round multiple values
    for(int i=0; i < 100; i++)
        value += analogRead(A0);

    // divide through 100 loops
    value /= 100;

    return value;
}


// sleep and reset function
 void softReset() 
{
   
   // time to sleep (in seconds) (900 = default):
   int sleepTimeS = 900;
   
   //  deepsleep esp --> after sleep reset and start with setup part
   Serial.println("Data handshake done --> enter sleep mode for about " + String(sleepTimeS) + "seconds!");
   ESP.deepSleep(sleepTimeS * 1000000);

}


// Convert uv voltage to uv intensity
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

 

Code des ATtiny45 für die Messung der Windgeschwindigkeit

Der Programmcode des ATtiny45, welcher die Intervalle des Anemometer (Windgeschwindigkeitssensor) zählt und die Windrichtung misst, ist ebenfalls nicht allzu kompliziert. Neben einer Funktion für das Übermitteln der Messdaten an den ESP8266*, gibt es einen Timer, welcher jede Minute ein Mal die gezählten Intervalle des Anemometer und den aktuellen Wert der Windrichtung in jeweils einen dafür vorgesehenen Array schreibt. Diese Arrays haben 15 nullinitiierte Stellen und reichen somit für das Abrufintervall von 15 Minuten aus. Ist der Timer von einer Minute abgelaufen, verschiebe ich die bereits enthaltenen Array-Daten um eine Position nach links und füge die gerade gemessenen Daten an letzter Stelle (Index 14) ein. So steht der zuletzt gemessene Wert also immer an ganz rechter Position im Array. 

   

Speichern der Daten für Windgeschwindigkeit innerhalb eines Array

 

Das Entprellen des Reed-Kontaktes habe ich damals über eine Flagvariable und ein einfaches delay von 10 Millisekunden realisiert. Dies ist vielleicht nicht die beste Methode, zu den Vergleichsmessungen mit einem Handanemometer hat sich aber keine Messwertabweichung feststellen lassen. 

 

#define I2C_SLAVE_ADDRESS 0x2   // this slave address (0x1, 0x2, 0x3)
#include <Timer.h>              // the timer library
#include <TinyWireS.h>          // the ATTiny Wire library


int Pin_Windrichtung = 3;  // init pin for wind direction
int Kontakt_1 = 1;  // init pin for wind speed input
int led = 5;  // init pin for led
int i  = 0;  // init counter variable 

int Wind_Counter = 0;  // init variable for sensor value
int schon_gezaehlt = 0;  // init flag variable for debounce
int Wind;  //init variable for wind direction

// init arrays to store 15 measurment values 
int Windcounts[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int Windrichtung[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0};

// init actual array position
int Array_Pos = 0;

// set variable for timer
Timer t;


void setup() 
{
	// define pinmode for inputs and outputs
	pinMode(Kontakt_1, INPUT);
	pinMode(Pin_Windrichtung, INPUT);
	pinMode(led, OUTPUT);

	ACSR |= _BV(ACD);  //disable the analog comparator 
	MCUCR |= _BV(BODS) | _BV(BODSE);  // turn off the brown-out detection          

	// init I2C as slave and set to wait for request
	TinyWireS.begin(I2C_SLAVE_ADDRESS);
	TinyWireS.onRequest(requestEvent);

	// start timer for storing values to array
	int Aktualizer = t.after(61000, Timer_abgelaufen);
}


void loop() 
{
	// set status led to off
	digitalWrite(led, LOW);

	// This needs to be here
	TinyWireS_stop_check();

	// read value of reed contact
	Wind_Counter=digitalRead(Kontakt_1);

	// HIGH == contact open | LOW == contact closed

	// if contact high and already counted 1, then turn of wind sensor / deactivation detected
	if (Wind_Counter == HIGH && schon_gezaehlt == 1)
	{
		// reset flag for detecting actual sensor activation
		schon_gezaehlt = 0;  
	}

	// if contact is low and flag for already counted is zero so on turn of sensor is detected
	if (Wind_Counter == LOW && schon_gezaehlt == 0)
	{      
		i= i + 1;  // increase value of counts
		digitalWrite(led, HIGH);  // activate status led    
		schon_gezaehlt = 1;  // set flag for already counted to 1
		delay(10);  // ad some delay for debouncing the sensor  
	}

	// update timer for array storage
	t.update();
}


// gets called when the ATtiny receives an i2c request
void requestEvent()
{   
	// iterate over arrays
	while(Array_Pos < 15)
	{
		// devide wind counts by 24 to be able to send values greater 255 counts via 1 byte
		// so a maximum windspeed of 244,8 km/h could get measured (that's truly enough ;-D)
		int Windcounts_24tel = (Windcounts[Array_Pos] / 24);

		TinyWireS.send(Windcounts_24tel);  // send decreased wind values
		TinyWireS.send(Windrichtung[Array_Pos]);  // send wind direction values
		Array_Pos++;    
	}
	// reset array pos     
	Array_Pos = 0;     
}


// is called when timer is reached
void  Timer_abgelaufen()
{
	// reinit timer for next execution
	int Aktualizer = t.after(61000, Timer_abgelaufen);

	// read value for wind direction
	Wind=analogRead(Pin_Windrichtung);

	// copy values of array one position to left 
	while (Array_Pos < 15)
	{
		Windcounts[Array_Pos] =  Windcounts[Array_Pos + 1];
		Windrichtung[Array_Pos] =  Windrichtung[Array_Pos + 1];
		Array_Pos++;
	}

	// write new value to last position 
	Windcounts[14] = i;
	Windrichtung[14] = Wind;
	i = 0;
	Wind = 0;
	Array_Pos = 0;
}

 

Code des ATtiny45 für die Messung der Wippvorgänge des Regenmengensensor

Der Code für den ATtiny45 des Regenmengensensors ist sogar noch einfacher. Hier reichte es, dass ich die ermittelte Regenmenge für das  15 Minutenintervall zusammenzähle und dann nur dieser Wert übertragen wird. Aus diesem Grund konnte ich hier auf den Einsatz eines Arrays verzichten. Ansonsten finden sich viele Teile des Codes vom ATTiny45 des Windgeschwindigkeitssensor auch im Code für den Regemengensensor wieder, weshalb ich hier nicht noch einmal näher darauf eingehen werde.

    

#define I2C_SLAVE_ADDRESS 0x3   // this slave address (0x1, 0x2, 0x3)
#include <TinyWireS.h>          // the ATTiny Wire library

int led = 3;                    // init pin for led
int Kontakt_1 = 1;              // init pin for rain measurement
int i  = 0;                     // init counter variable
int Regen_Counter = 0;          // init variable for sensor value


void setup() 
{

	// define pinmode for inputs and outputs
	pinMode(led, OUTPUT);
	pinMode(Kontakt_1, INPUT);

	ADCSRA &= ~(1<<ADEN);                     //turn off ADC
	ACSR |= _BV(ACD);                         //disable the analog comparator
	MCUCR |= _BV(BODS) | _BV(BODSE);          //turn off the brown-out detector

	// init I2C as slave and set to wait for request
	TinyWireS.begin(I2C_SLAVE_ADDRESS);
	TinyWireS.onRequest(requestEvent);
}


void loop() 
{

	// set status led to off                 
	digitalWrite(led, LOW);

	// This needs to be here
	TinyWireS_stop_check();

	// read value of reed contact
	Regen_Counter=digitalRead(Kontakt_1);

	// HIGH == contact closed | LOW == contact open

	// check if rain sensor gets closed 
	if (Regen_Counter == HIGH)
	{      
		digitalWrite(led, HIGH);  // activate status led (led recommended for checking sensor function)   
		i= i + 1;  // increase count of tilting processes

		delay(100);  // delay to debounce the switch  
	} 
}

// Gets called when the ATtiny receives an i2c request
void requestEvent()
{  
    
	// send and reset value of tilting processes
	  TinyWireS.send(i);
	  i = 0;     
}

 

Damit war der Hardware- und auch Softwareteil der Wetterstation soweit fertiggestellt. Was jetzt noch fehlt sind die Scripte für das Speichern und Anzeigen der gemessenen Sensordaten. Um diesen Beitrag jedoch nicht noch länger werden zu lassen, habe ich diesen Teil in einem separaten Beitrag veröffentlicht. Um sofort zum Beitrag mit dem Titel Anzeige aufgezeichneter Wetterdaten* zu gelangen, kann gern der folgende Link* verwendet werden.

 

Bilder der DIY ESP8266 Wetterstation

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert