Zum Inhalt springen

Wetterstation

Was braucht man ?

  • Eine laufende openHAB installation mit Mosquitto (MQTT)
  • nodeMCU 32 (ESP-32)
  • AS3935
  • Diverse Sensoren (Temperatur, Luftdruck, Feinstaub, IR Temperatur)
  • Diverse Kabel
  • 1,5" OLED Display
  • Stromversorgung

 

Was kann das alles ?

  • Luftdruck
  • Luftfeuchte
  • Lufttemperatur
  • Feinstaub (PM 1,0  /  2,5  / 10,0)
  • Blitze
  • Gefühlte Temperatur (errechnet aus örtlicher: Lufttemperatur, Windgeschwindigkeit und Luftfeuchte)
  • Taupunkt (errechnet aus örtlicher Lufttemperatur und Luftfeuchte)
  • Bodentemperatur (20cm tiefe)
  • Himmelstemperatur
  • Bedeckungsgrad (Wolkendecke)
  • Örtliche Anzeige

 

Wenn man das später alles am laufen hat und sich damit ein paar Tage/Wochen beschäftigt, passieren viele lustige Sachen. Man guckt sich auf dem "smart TV" live an, wie der Wind draußen immer neue Höchstwerte erreicht, oder wundert sich warum der Feinstaubwert auf einmal dreimal höher als an Sylvester ist: Die Nachbarn haben gegrillt.

Generelles zu openHAB

Generell werde ich nicht darauf eingehen, wie man openHAB installiert, was man dafür braucht und was man damit machen kann. Ich werde lediglich am Ende kurz daruf eingehen, wie ich die Daten verarbeite und vorstellen wie das bei uns visualisiert wird.

Wetterstation - Das Gehäuse und der Standort

Um eine brauchbare, vergleichbare Messreihe zu bekommen ist der richtige Standort wichtig. Es gibt beim Deutschen Wetterdienst eine PDF Datei (Link erspare ich mir, die bauen so oft um) wie sowas auszusehen hat. Vergleichbarkeit der Messwerte ist immer das Hauptthema. Bedeutet:

  • Temperaturmessung in aller Regel in 2m höhe über kurz gemähter Wiese, möglichst ohne Einfluss von Schatten. Spielt auch für die Luftfeuchtigkeit eine Rolle.
  • Wind misst man eigentlich in 10 Höhe, an einem Standort ohne störende Einflüsse. Also Bäume, Häuser.....
  • Die UV-Messung muss so angebracht sein, dass im Prinzip über den Tag keine Schattenwürfe zu erwarten sind. Und natürlich auch nichts "draufspiegelt"
  • Regen ist das gleiche Thema, auch ohne "störende" Umwelteinflüsse....
  • Die IR Messung vom Himmel zeigt idealerweise senkrecht nach oben und hat freie Sicht
  • Die Messung der Bodentemperatur soll - oh wunder - an einem Ort ohne Störende Umwelteinflüsse (Schattenwurf) stattfinden.

Kann man also in einem normalen Garten nur sehr schwer bis gar nicht umsetzen. Die Sensoren für Temperatur, Luftfeuchte und Luftdruck kann man in ein Gehäuse bauen und auf 2m Höhe über Rasen anbringen. Wer eine Fassade hat, hat eventuell die Möglichkeit, den Windmesser so anzubringen das er über das Dach "guckt" und so möglichst 360° "rundsicht" hat. Den Regenmesser bekommt man evtl. auch noch so montiert, dass er nicht im "Regenschatten" einer Hausfassade steht. ACHTUNG! Die meisten Regenmesser haben eine Waage eingebaut, die die Regenmenge misst. Heißt: Das Ding muss so stabil wie möglich angebracht werden, sonst kommt es zu fehlmessungen. Bedeutet natürlich, Regenmesser und Windmesser an einem Mast wären schön, aber ohne große Umbaumaßnahmen (sehr stabiles Rohr) nicht machbar.

Als erstes Gehäuse habe ich eine Holzkiste gebaut. Sehr einfach, sehr preiswert. Das ganze wurde weiß lackiert, und mit einem Dach aus Plexiglas (Weiss) versehen. Das hat soweit gut funktioniert, hat aber leider den ersten Winter nicht gut überstanden. Die ein oder andere Leimstelle ist locker und der Lack müsste aufgefrischt werden. Das Plexiglas sah auch schon mal besser aus..... Darum: Wir bauen was anständiges. Die Gehäuse zum Strahlungsschutz sind normalweise rund, also war die eckige Kiste eh schon nicht die beste Lösung. Ich habe lange überlegt, wie man einfach ein Rundes Gehäuse bauen kann. Zur Auswahl standen:

  • Plexiglasgehäuse (Ringe bestellen, im Backofen tiefziehen)
  • Neue Holzkiste
  • GFK-Gehäuse bauen
  • Fertige Gehäuseteile zusammenschrauben

Gut, Plexiglas im Backofen tiefziehen geht, ist aber nachher sehr empfindlich gegenüber Bruch. Holzkiste hatte ich schon mal, GFK ist mir zu teuer und zu viel sauerei. Also bleibt die Frage, wie kommt man an fertige Gehäuseteile?

Genereller vorgehensweise

Comming soon 😉

Wiring

Coming Soon

Platinen Layout

Coming soon 😉

Der Code

#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <SparkFun_AS3935.h>
#include <Adafruit_NeoPixel.h>

String WiFi_SSID = "SSID";
String WiFi_PW = "WIFI PASSWORT";
const char* mqttServer = "SERVER IP";
const int mqttPort = 1883;
const char* mqttUser = "MQTT BENUTZER";
const char* mqttPassword = "MQTT PASSWORT";

unsigned long waitCount = 0;
uint8_t conn_stat = 0;
unsigned long lastStatus = 0;
unsigned long lastTask = 0;

const char* Status = "{\"Message\":\"ich laufe\"}";

long lastMsg = 0;
char msg[50];
int value = 0;

int flash =0;
int value_1;
long time_1 = 0;
int strip_helligkeitswert = 0;
int periode = 1000;

#define PIN_Strip_a 27 //Strip A - BLITZSENSOR
#define PIN_Strip_b 26 //Strip B - WETTERSTATION
#define NUMPIXEL_a 8
#define NUMPIXEL_b 8
#define AS3935_ADDR 0x03
#define INDOOR 0x12
#define OUTDOOR 0xE
// Interrupt pin for lightning detection
const int lightningInt = 4;
#define LIGHTNING_INT 0x08
#define DISTURBER_INT 0x04
#define NOISE_INT 0x01

int noiseFloor = 2;
int intVal = 0;

Adafruit_NeoPixel strip_a = Adafruit_NeoPixel(NUMPIXEL_a, PIN_Strip_a, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip_b = Adafruit_NeoPixel(NUMPIXEL_b, PIN_Strip_b, NEO_GRB + NEO_KHZ800);
WiFiClientSecure TCP;
WiFiClient espClient;
PubSubClient client(espClient);
SparkFun_AS3935 lightning(AS3935_ADDR);

uint32_t blue = strip_a.Color(0, 0, 255);
uint32_t orange = strip_a.Color(238, 154, 0);
uint32_t green = strip_a.Color(0, 255, 0);
uint32_t red = strip_a.Color(255, 0, 0);
uint32_t yellow = strip_a.Color(255, 255, 0);
uint32_t purple = strip_a.Color(160, 32, 240);
uint32_t aqua = strip_a.Color(135, 206, 250);
uint32_t navy = strip_a.Color(0, 0, 205);
uint32_t gray = strip_a.Color(143, 143, 143);
uint32_t off = strip_a.Color(0, 0, 0);

void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
pinMode(lightningInt, INPUT);
strip_a.begin();
strip_b.begin();
delay(50);
strip_a.fill(orange, 0, 8);
strip_b.fill(orange, 0, 8);
strip_a.setBrightness(5);
strip_b.setBrightness(5);
delay(100);
strip_a.show();
strip_b.show();
delay(500);
client.setCallback(callback);
Wire.begin();
lightning.begin();
lightning.setIndoorOutdoor(OUTDOOR);
}

void callback(char* topic, byte* message, unsigned int length) {
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;

for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();

if ((String(topic) == "ESP321/input")&&(flash == 0 )) {
Serial.print("Changing output to ");
if(messageTemp == "1"){
Serial.println("1 - navy");
strip_b.fill(navy, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "2"){
Serial.println("2 - blue");
strip_b.fill(blue, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "3"){
Serial.println("3- aqua");
strip_b.fill(aqua, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "4"){
Serial.println("4 - gray");
strip_b.fill(gray, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "5"){
Serial.println("5 - green");
strip_b.fill(green, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "6"){
Serial.println("6 - yellow");
strip_b.fill(yellow, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "7"){
Serial.println("7 - orange");
strip_b.fill(orange, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "8"){
Serial.println("8 - red");
strip_b.fill(red, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}
else if(messageTemp == "9"){
Serial.println("9 - purple");
strip_b.fill(purple, 0, 8);
strip_b.setBrightness(255);
strip_b.show();
}

}
}

void loop() {
// start of non-blocking connection setup section
if ((WiFi.status() != WL_CONNECTED) && (conn_stat != 1)) { conn_stat = 0; }
if ((WiFi.status() == WL_CONNECTED) && !client.connected() && (conn_stat != 3)) { conn_stat = 2; }
if ((WiFi.status() == WL_CONNECTED) && client.connected() && (conn_stat != 5)) { conn_stat = 4;}
switch (conn_stat) {
case 0: // MQTT and WiFi down: start WiFi
Serial.println("MQTT and WiFi down: start WiFi");
WiFi.begin(WiFi_SSID.c_str(), WiFi_PW.c_str());
conn_stat = 1;
break;
case 1: // WiFi starting, do nothing here
Serial.println("WiFi starting, wait : "+ String(waitCount));
waitCount++;
break;
case 2: // WiFi up, MQTT down: start MQTT
Serial.println("WiFi up, MQTT down: start MQTT");
client.setServer(mqttServer, mqttPort);
client.connect("ESP32Client", mqttUser, mqttPassword );
conn_stat = 3;
waitCount = 0;
break;
case 3: // WiFi up, MQTT starting, do nothing here
Serial.println("WiFi up, MQTT starting, wait since: "+ String(waitCount));
waitCount++;
break;
case 4: // WiFi up, MQTT up: finish MQTT configuration
Serial.println("WiFi up, MQTT up: finish MQTT configuration");
client.publish("ESP321/Status", "MQTT Verbunden");
client.subscribe("ESP321/input");
flash = 0;
strip_a.fill(green, 0, 4);
strip_a.show();
strip_a.fill(blue, 4, 4);
strip_a.setBrightness(50);
strip_a.show();
conn_stat = 5;
break;
}
// end of non-blocking connection setup section

// start section with tasks where WiFi/MQTT is required
if (conn_stat == 5) {
if (millis() - lastStatus > 300000) {
Serial.println(Status);
client.publish("ESP321/Status", "MQTT Alive");
client.publish("ESP321/Disturber", "-");
client.publish("ESP321/Noise", "-");
client.publish("ESP321/Blitzerkennung", "-");
client.publish("ESP321/Blitzentfernung", "-");
strip_a.fill(off, 0, 8);
strip_a.setBrightness(30);
strip_a.show();
lastStatus = millis();
flash = 0;

}

if((digitalRead(lightningInt) == HIGH) &&(flash == 0 )){
intVal = lightning.readInterruptReg();
if(intVal == NOISE_INT){
client.publish("ESP321/Noise", "Rauschen empfangen");
Serial.println("Rauschen!");
strip_a.fill(purple, 0, 8);
strip_a.setBrightness(100);
strip_a.show();
lastStatus = millis();
}
else if((intVal == DISTURBER_INT)&&(flash == 0 )){
client.publish("ESP321/Disturber", "Störung empfangen");
Serial.println("Störung!");
strip_a.fill(red, 0, 8);
strip_a.setBrightness(100);
strip_a.show();
lastStatus = millis();
}
else if(intVal == LIGHTNING_INT){
client.publish("ESP321/Blitzerkennung", "Blitz erkannt");
Serial.println("Blitz erkannt");
byte distance = lightning.distanceToStorm();
char s [20];
sprintf (s, "%d", lightning.distanceToStorm());
client.publish("ESP321/Blitzentfernung", (const char*) s);
strip_a.fill(blue, 0, 8);
strip_b.fill(blue, 0, 8);
flash =1;
lastStatus = millis();
}
}

if (flash == 1){
time_1 = millis();
value_1 = 128+127*sin(2*PI/periode*time_1);
strip_helligkeitswert = value_1;
strip_a.setBrightness(strip_helligkeitswert);
strip_b.setBrightness(strip_helligkeitswert);
strip_a.show();
strip_b.show();
}
}
client.loop();
}


Damit das ganz funktioniert, benötigt man einen "Sketch", also die Programmierung für den ESP32.

Ich habe dazu die Arudino IDE benutzt, da auch hier die Installation bzw. vorgehensweise immer etwas variiert, erspare ich mir hier etwas aufzuschreiben das dann in 2 Wochen wieder nicht funktioniert.

Generelle Vorgehensweise:

  • Arduino IDE Installieren
  • Im Boardmanager das Expressif ESP32 hinzufügen (bitte hier die Anleitung der Expressif Seite beachten!)
  • Unter "Werkzeuge" -->  "Bibliotheken verwalten" alle Bibliotheken installieren die notwendig sind. Sollte da eine fehlen ist das erstmal nicht tragisch, die IDE meckert dann beim kompilieren und man installiert einfach nach
  • In Windows muss noch ein COM Port eingerichtet werden damit die IDE z.B. an COM3 dann auch den ESP findet

Wenn man das alles hat, kann man den Sketch dann einfach kompilieren und übertragen.

WICHTIG:

Im Sketch müssen noch ein paar Dinge ergänzt werden:

  • WiFi SSID
  • WiFi Passwort
  • MQTT Zugangsdaten usw.