DIY Wetterstation mit einem Wemos D1 Mini – Teil 1

Wie du deine eigene kleine DIY Wetterstation mit einem Wemos D1 Mini aufbaust, erfährst du in diesem Beitrag.

Wemos D1 Mini - Wetterstation
Wemos D1 Mini – Wetterstation

Ich habe dir bereits einige Projekte mit diversen Sensoren auf meinem Blog vorgestellt und auch einige kleine Projekte um die aktuelle Temperatur, den Luftdruck oder ähnliches anzeigen zu lassen. Dieses Mal soll es aber ganz anders werden.

Was soll diese Wetterstation alles können?

Die kleine Wetterstation soll dir folgende Werte anzeigen:

  • die Temperatur,
  • die rel. Luftfeuchtigkeit,
  • den Luftdruck,
  • die UV Intensität,

Und all das soll auf einem Dashboard im lokalen Netzwerk verfügbar sein.

Dashboard für die DIY Wetterstation
Dashboard für die DIY Wetterstation

Was wird für den Aufbau der DIY Wetterstation benötigt?

Für den Aufbau der DIY Wetterstation benötigst du einen Mikrocontroller mit WiFi Schnittstelle, einige Sensoren, einen Widerstand sowie diverse Breadboardkabel und ein großes Breadboard.

Zunächst bauen wir die Schaltung auf einem Breadboard auf, damit können wir flexibel auf Änderungen eingehen. Daher benötigen wir noch zusätzlich:

Aufbau der Schaltung

Nachfolgend zeige ich dir nun die Schaltung mit den oben aufgeführten Sensoren am Wemos D1 Mini.

Aufbau der Schaltung - Wemos D1 Mini - Wetterstation
Aufbau der Schaltung – Wemos D1 Mini – Wetterstation

Zuordnung der Pins

SensorWemos D1 Mini
DHT11
VCC5V
GNDG – GND
DATAGPIO12 – D6
BMP180
VCC3v3
GNDG – GND
SCLGPIO5 – SCL – D1
SDAGPIO4 – SDA – D2
UV Sensor – ML8511
VCC3v3
GNDG – GND
OUTA0

Programmieren des Sketches

Das Programm bzw. Sketch wird in der Arduino IDE programmiert. Jedoch benötigst du zunächst zwei zusätzliche Bibliotheken, welche du zunächst einbinden musst.

einbinden der benötigten Bibliotheken

Wie erwähnt benötigst du zwei Bibliotheken:

Wie du eine Bibliothek in die Arduino IDE einbindest habe ich dir im Beitrag Arduino IDE, Einbinden einer Bibliothek ausführlich erläutert.

erstellen des Programmes, Schritt für Schritt

Wenn die beiden Bibliotheken eingebunden wurden, so müssen diese im Sketch eingebunden werden. Für den UV Sensor wird keine zusätzliche Bibliothek benötigt da dieser “nur” über einen analogen Pin ausgelesen wird. (Wertebereich von 0 bis 1023)

//Bibliothek zur Kommunikation mit dem DHT11 Sensor
#include "DHT.h"
//OneWire Bibliothek für den BMP180 Sensor
#include <Wire.h>
//Bibliothek zur Kommunikation mit dem BMP180 Sensor
#include <BMP180.h>

Danach werden wir nun die 3 Sensoren definieren und die Objekte instanziieren.

Zunächst den DHT11 Sensor, da die eingebundene Bibliothek auch für den DHT22 sowie dem AM2320 funktioniert, müssen wir definieren, dass wir einen DHT11 Sensor angeschlossen haben und auslesen möchten.

//DHT11 Sensor am digitalen Pin D6 angeschlossen
#define DHTPIN 12
//Festlegen welcher Typ von DHT Sensor verwendet wird.
#define DHTTYPE DHT11
//Initialisieren des Sensors mit dem Anschluss und dem Typ
DHT dht(DHTPIN, DHTTYPE);

Den UV Sensor haben wir am einzigen analogen Pin A0 des Wemos D1 Mini angeschlossen.

//UV Sensor am analogen Pin A0 angeschlossen
#define UV_SENSOR A0

Der BMP180 wird per I²C angeschlossen, diese Pins sind im System bekannt und somit müssen diese nicht benannt werden. Jedoch muss das Objekt aus der Bibliothek instanziiert werden damit wir die Daten lesen können.

//Die Instanzvariable eines BMP180 Sensors.
BMP180 barometer; 

Am Schluss werden noch die Konstanten für den UV-Sensor definiert.

const float inMin = 0.99;
const float inMax = 2.9;
const float outMin = 0.0;
const float outMax = 15.0;

const byte numberOfReadings = 8;

In der Funktion “setup” wird die serielle Kommunikation gestartet sowie die benötigten Pins der Sensoren auf INPUT gesetzt.

Zusätzlich starten wir auch die Kommunikation mit dem DHT11 Sensor und prüfen ob ein BMP180 Sensor verbunden ist. Wenn kein BMP180 Sensor verbunden ist, so wird eine Fehlermeldung auf der seriellen Schnittstelle ausgegeben.

void setup() {
  //beginn der seriellen Kommunikation mit 9600 Baud
  Serial.begin(9600);

  pinMode(DHTPIN, INPUT);
  pinMode(UV_SENSOR, INPUT);

  //DHT Kommunikation beginnen.
  dht.begin();

  Wire.begin(); //Adafruit Wire Bibliothek für die I2C Kommunikation mit dem BMP180 Sensor.
  barometer = BMP180(); //setzen einer Instanz des BMP180 Sensors.
  //Prüfen ob ein BMP180 Sensor gefunden wurde.
  if (!barometer.EnsureConnected()) {
    Serial.println("Es wurde kein BMP180 Sensor gefunden.");
  }
}

Die Daten der Sensoren werden in der Funktion “loop” alle 2 Sekunden gelesen.

void loop() {
  delay(2000);

Zunächst lesen wir die Daten des DHT11 Sensors.

  //lesen der Luftfeuchtigkeit
  double luftfeuchtigkeit = dht.readHumidity();
  //lesen der Temperatur in Grad Celsius
  double temperaturC = dht.readTemperature();
  //lesen der Temperatur in Grad Fahrenheit
  //mit dem Boolean Parameter wird "gesteuert" ob
  //die Temperatur in Fahrenheit oder Celsius ausgegeben wird.
  double temperaturF = dht.readTemperature(true);

Als Nächstes prüfen wir nun ob korrekte Daten empfangen wurden und ob der BMP180 Sensor verfügbar ist. Sollte dieses nicht so sein, so wird die Funktion “loop” an dieser Stelle durch ein “return” verlassen.

  //Prüfen ob die Werte erfolgreich gelesen wurden und der BMP180 Sensor verfügbar ist.
  if (isnan(luftfeuchtigkeit) || isnan(temperaturC) || isnan(temperaturF) || !barometer.IsConnected) {
    Serial.println("Fehler beim lesen von Daten.");
    return;
  }

Da die Daten des DHT11 Sensors vorliegen, geben wir diese auf der seriellen Schnittstelle aus.

Serial.print("Temperatur: ");
Serial.print(temperaturC);
Serial.print(" °C | ");
Serial.print(temperaturF);
Serial.println(" °F\t");

Serial.print("Luftfeuchtigkeit: ");
Serial.print(luftfeuchtigkeit);
Serial.println(" %\t");

Danach werden die Daten des verbundenen BMP180 Sensors gelesen und ebenfalls ausgegeben.

//Lesen des Wertes für den Luftdruck aus dem Sensor.
long currentPressure = barometer.GetPressure();
Serial.print("Luftdruck: ");
Serial.print(currentPressure / 100);
Serial.println(" hPa");

Zum Schluss wird nun noch der UV-Sensor ausgelesen.

Als Erstes wird der analoge Wert ausgelesen (Wertebereich 0 bis 1023) dazu werden X Werte gelesen und durch die Anzahl geteilt.

Aus diesem gelesenen Wert berechnen wir nun Spannung. Dazu wird die 3v3 Spannung benötig, ein Problem ist hier meist das nicht genau 3,3V anliegen, sondern meistens deutlich darunter daher ist dieser Wert ziemlich ungenau.

Mit der Funktion “mapfloat” setzen wir den Wert der Spannung in relation zum Wertebereich des Sensors.

int uvLevel = averageAnalogRead(UV_SENSOR);

float outputVoltage = 3.3 * uvLevel / 1024;
float uvIntensity = mapfloat(outputVoltage, inMin, inMax, outMin, outMax);

Funktion zum berechnen des Durchschnittswertes des Sensors.

int averageAnalogRead(int pinToRead) {
  unsigned int runningValue = 0;
  for (int x = 0 ; x < numberOfReadings ; x++) {
    runningValue += analogRead(pinToRead);
  }
  return (runningValue / numberOfReadings);
}

Funktion “mapfloat” aus dem Arduino Forum.

//Arduino map Funktion unterstützt kein Float daher wurde diese neu implementiert
//(eine Kopie aus dem Arduino Forum)
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;
}

das fertige Sketch für die Wemos D1 Mini – Wetterstation

//Bibliothek zur Kommunikation mit dem DHT11 Sensor
#include "DHT.h"
//OneWire Bibliothek für den BMP180 Sensor
#include <Wire.h>
//Bibliothek zur Kommunikation mit dem BMP180 Sensor
#include <BMP180.h>

//DHT11 Sensor am digitalen Pin D6 angeschlossen
#define DHTPIN 12
//Festlegen welcher Typ von DHT Sensor verwendet wird.
#define DHTTYPE DHT11
//Initialisieren des Sensors mit dem Anschluss und dem Typ
DHT dht(DHTPIN, DHTTYPE);


//UV Sensor am analogen Pin A0 angeschlossen
#define UV_SENSOR A0

//Die Instanzvariable eines BMP180 Sensors.
BMP180 barometer;

const float inMin = 0.99;
const float inMax = 2.9;
const float outMin = 0.0;
const float outMax = 15.0;

const byte numberOfReadings = 8;

void setup() {
  //beginn der seriellen Kommunikation mit 9600 Baud
  Serial.begin(9600);

  pinMode(DHTPIN, INPUT);
  pinMode(UV_SENSOR, INPUT);

  //DHT Kommunikation beginnen.
  dht.begin();

  Wire.begin(); //Adafruit Wire Bibliothek für die I2C Kommunikation mit dem BMP180 Sensor.
  barometer = BMP180(); //setzen einer Instanz des BMP180 Sensors.
  //Prüfen ob ein BMP180 Sensor gefunden wurde.
  if (!barometer.EnsureConnected()) {
    Serial.println("Es wurde kein BMP180 Sensor gefunden.");
  }
}

void loop() {
  delay(2000);

  //lesen der Luftfeuchtigkeit
  double luftfeuchtigkeit = dht.readHumidity();
  //lesen der Temperatur in Grad Celsius
  double temperaturC = dht.readTemperature();
  //lesen der Temperatur in Grad Fahrenheit
  //mit dem Boolean Parameter wird "gesteuert" ob
  //die Temperatur in Fahrenheit oder Celsius ausgegeben wird.
  double temperaturF = dht.readTemperature(true);

  //Prüfen ob die Werte erfolgreich gelesen wurden und der BMP180 Sensor verfügbar ist.
  if (isnan(luftfeuchtigkeit) || isnan(temperaturC) || isnan(temperaturF) || !barometer.IsConnected) {
    Serial.println("Fehler beim lesen von Daten.");
    return;
  }

  Serial.print("Temperatur: ");
  Serial.print(temperaturC);
  Serial.print(" °C | ");
  Serial.print(temperaturF);
  Serial.println(" °F\t");

  Serial.print("Luftfeuchtigkeit: ");
  Serial.print(luftfeuchtigkeit);
  Serial.println(" %\t");

  //Lesen des Wertes für den Luftdruck aus dem Sensor.
  long currentPressure = barometer.GetPressure();
  Serial.print("Luftdruck: ");
  Serial.print(currentPressure / 100);
  Serial.println(" hPa");

  int uvLevel = averageAnalogRead(UV_SENSOR);

  float outputVoltage = 3.3 * uvLevel / 1024;
  float uvIntensity = mapfloat(outputVoltage, inMin, inMax, outMin, outMax);

  Serial.print("UV Intensität: ");
  Serial.print(uvIntensity);
  Serial.println(" mW/cm²");

  Serial.println();
}

//Funktion zum lesen des Durchschnittswertes eines Sensors am analogen Pin
int averageAnalogRead(int pinToRead) {
  unsigned int runningValue = 0;
  for (int x = 0 ; x < numberOfReadings ; x++) {
    runningValue += analogRead(pinToRead);
  }
  return (runningValue / numberOfReadings);
}


//Arduino map Funktion unterstützt kein Float daher wurde diese neu implementiert
//(eine Kopie aus dem Arduino Forum)
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;
}

Ausgabe des Sketches auf der seriellen Schnittstelle:

Ausgabe der Sensorwerte auf der seriellen Schnittstelle
Ausgabe der Sensorwerte auf der seriellen Schnittstelle

Aufteilen des Sketches in Dateien

Damit das Programm in einem weiteren Beitrag wiederverwendet werden kann, möchte ich dieses in separate Dateien aufteilen. Dieses hat den Vorteil dass, das Programm deutlich lesbarer und Wartbarer wird. Des Weiteren können Änderungen leichter verfolgt werden.

Du kannst dir das fertige Programm auch als ZIP Datei herunterladen und somit diesen Schritt einfach überspringen.

erstellen einer zusätzlichen Datei in der Arduino IDE

Erstellen wir zunächst einen neuen Tab indem wir die Schaltfläche rechts in der IDE mit dem kleinen Pfeil nach unten betätigen.

In diesem neuen Kontextmenü wählen wir den Eintrag “Neuer Tab” (siehe Grafik) aus.

erzeugen eines neuen Tabs in der Arduino IDE
erzeugen eines neuen Tabs in der Arduino IDE

Es wird dann ein gelber Bereich unterhalb des Editors für den Quellcode angezeigt. In diesem geben wir nun den Dateinamen ein, zbsp. “dht11.h”.

vergeben des Dateinamens für den neuen Tab
vergeben des Dateinamens für den neuen Tab

Dieses wiederholen wir für die Sensoren und dem Wifi Modul und zusätzlich für unsere Konfigurationsdatei.

  • dht11.h
  • bmp180.h
  • uvSensor.h
  • wifi.h
  • config.h

DHT11 Sensor

Den Code für den DHT11 Sensor legen wir im Tab “dht11.h” ab.

//Bibliothek zur Kommunikation mit dem DHT11 Sensor
#include "DHT.h"

//DHT11 Sensor am digitalen Pin D6 angeschlossen
#define DHTPIN 12
//Festlegen welcher Typ von DHT Sensor verwendet wird.
#define DHTTYPE DHT11
//Initialisieren des Sensors mit dem Anschluss und dem Typ
DHT dht(DHTPIN, DHTTYPE);

void initDHTSensor() {
  pinMode(DHTPIN, INPUT);
  //DHT Kommunikation beginnen.
  dht.begin();
}

double getDHTSensor_Humidity() {
  double hum =  dht.readHumidity();
  if (isDebug) {
    Serial.println("Luftfeuchtigkeit: " + String(hum, DEC) + " %");
  }
  return hum;
}

double getDHTSensor_Temperature_Celsius() {
  double celsius = dht.readTemperature();
  if (isDebug) {
    Serial.println("Temperatur: " + String(celsius, DEC) + " °C");
  }
  return celsius;
}

double getDHTSensor_Temperature_Fahrenheit() {
  double fahrenheit = dht.readTemperature(true);
  if (isDebug) {
    Serial.println("Temperatur: " + String(fahrenheit, DEC) + " °F");
  }
  return fahrenheit;
}

BMP180

Der Code für den BMP180 Sensor legen wir im gleichnamigen Tab “bmp180.h” ab.

//OneWire Bibliothek für den BMP180 Sensor
#include <Wire.h>
//Bibliothek zur Kommunikation mit dem BMP180 Sensor
#include <BMP180.h>

//Die Instanzvariable eines BMP180 Sensors.
BMP180 barometer;

void initBMP180() {
  Wire.begin(); //Adafruit Wire Bibliothek für die I2C Kommunikation mit dem BMP180 Sensor.
  barometer = BMP180(); //setzen einer Instanz des BMP180 Sensors.
  //Prüfen ob ein BMP180 Sensor gefunden wurde.
  if (!barometer.EnsureConnected()) {
    Serial.println("Es wurde kein BMP180 Sensor gefunden.");
  }
}

long getBMP180Value() {
  long currentPressure = barometer.GetPressure();
  if (isDebug) {
    Serial.println("Luftdruck: " + String(currentPressure / 100, DEC) + " hPa");
  }
  return currentPressure;
}

UV-Sensor

Der Code und die Funktionen zum errechnen des UV – Wertes legen wir ebenfalls im Tab “uvSensor.h” ab.

//UV Sensor am analogen Pin A0 angeschlossen
#define UV_SENSOR A0

const float inMin = 0.99;
const float inMax = 2.9;
const float outMin = 0.0;
const float outMax = 15.0;

const byte numberOfReadings = 8;

//Funktion zum lesen des Durchschnittswertes eines Sensors am analogen Pin
int averageAnalogRead(int pinToRead) {
  unsigned int runningValue = 0;
  for (int x = 0 ; x < numberOfReadings ; x++) {
    runningValue += analogRead(pinToRead);
  }
  return (runningValue / numberOfReadings);
}


//Arduino map Funktion unterstützt kein Float daher wurde diese neu implementiert
//(eine Kopie aus dem Arduino Forum)
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;
}

void initUvSensor() {
  pinMode(UV_SENSOR, INPUT);
}

int getUVSensorValue() {
  int uvLevel = averageAnalogRead(UV_SENSOR);

  float outputVoltage = 3.3 * uvLevel / 1024;
  float uvIntensity = mapfloat(outputVoltage, inMin, inMax, outMin, outMax);

  Serial.print("UV Intensität: ");
  Serial.print(uvIntensity);
  Serial.println(" mW/cm²");

  Serial.println();
  return uvIntensity;
}

WiFi Modul

Die WiFi Funktionen legen wir im Tab “wifi.h” ab. Hier wird auch später die Funktion implementiert, um das Dashboard aufzubauen. Aber dazu später mehr.

#include <ESP8266WiFi.h>

WiFiServer server(80);

void initWifiModul() {
  Serial.println("Aufbau der Verbindung zu: " + String(ssid));
  
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("Mit " + String(ssid) + " erfolgreich verbunden!");
  server.begin();
  Serial.println("Server gestartet");
  Serial.print("Adresse : http://");
  Serial.println(WiFi.localIP());
}

Konfigurationsdatei

Für die spätere WiFi Verbindung benötigen wir noch zusätzlich eine Datei wo wir die Verbindungsdaten zum lokalen WLAN speichern wollen.

bool isDebug = true;

const char* ssid = "FRITZBox7590GI24";
const char* password = "xzy";

Projekt zum Download

Hier nun das vorbereitete Projekt zum einfachen Download.

Kommentar hinterlassen

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