This commit is contained in:
Dustin Brunner 2022-03-18 14:12:51 +01:00
parent b3a40a8c5a
commit 5c319a26a1
18 changed files with 1543 additions and 1 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

File diff suppressed because it is too large Load Diff

73
ESP8266/README.md Normal file
View File

@ -0,0 +1,73 @@
# ESP8266 MQTT Wetterstation
Wie man eine Wetterstation basierend auf einem ESP8266 baut...
Der ESP verbindet sich per WLAN mit einem MQTT-Server und sendet die Messwerte als JSON (alle 10 Minuten), welche dann in Node-Red empfangen und verarbeitet werden. Zusätzlich werden die Daten in eine Influx-DB Datenbank geschrieben und können in Grafana visualisiert werden.
Diese Wetterstation kann als Erweiterung der Bresser-Wetterstation gesehen werden. Es werden Funktionen hinzugefügt, welche über die Bresser-Wetterstation nicht gemessen und empfangen werden können.
# Die Sensoren
| Sensor | Protocol | Values |
| --------- |---------- | ------ |
| BME 280 | I2C | Temperatur (nicht genutzt da nicht exakt), Luftfeuchtigkeit (nicht genutzt da von Bresser-Station gemesssen), Luftdruck |
| 2x DS18B20 | OneWire Bus | Temperatur (2m und 0m Höhe) |
| BH1750 | I2C | Helligkeit |
| Rain sensor | Analog in | Regen ja/nein |
| VEML6070 | I2C | UV-Intensität (daraus kann der UV-Index berechnet werden) |
<br>
# Hardware
Es wird ein WeMos D1 Mini Board verwendet. Das PCB-Layout passt (nur) für dieses Board.
## Schaltplan
[<img src="Wetterstation_Schaltplan.png" width="80%">](Wetterstation_Schaltplan.png)
## Hauptplatine
[<img src="Wetterstation_Leiterplatte.png" width="40%">](Wetterstation_Leiterplatte.png)
## 3D gedruckte Teile
Im Internet habe ich einen sog. Stevenson-Screen gefunden, welcher als Sonnenschutz-Gehäuse für den BME280 und die DS18B20 Sensoren dient.
Für die beiden Lichtsensoren habe ich jeweils ein Gehäuse erstellt, welche z.B. mit einem Stück Plexiglas geschlossen oder für noch besseren Feuchtigkeitsschutz mit transparenten Epoxidharz ausgegossen werden können.
Die Modelle können aus der GitHub repository heruntergeladen werden. Das Lichtsensor Gehäuse ist auch auf Tinkercad verfügbar: https://www.tinkercad.com/things/6c97AcGgvdE
<br>
# Software
## ESP8266
- Es wird bei jeder Messung ein Mittelwert aus 3 Messungen gebildet
- Der Luftdruck auf NN-Niveau wird berechnet. (Im Programmcode muss die Aufstellhöhe der Station angepast werden, damit dieser Wert korrekt berechnet werden kann)
- Es sind einige Debugging-Funktionen implementiert: ESP Reset, Ping/Pong, WIFI Signalstärke
- Debug LED
bootup: ein
nach Initialisierung: blinkt 2x
error, MQTT nicht verbunden: dauer ein
- Damit die DS18B20 Sensoren funktionieren müssen die Sensoradressen im Code angepasst werden. Diese Adressen können mit Beispielcode `oneWireSearch.ino` der DallasTemperature Bibiliothek in Erfahrung gebracht werden.
- Alle benötigten Bibiliotheken sind im Code verlinkt und können von dort heruntergeladen werden.
Achtung: Damit die Datenübertragung mit JSON funktioniert muss die zulässige Paketgröße `MQTT_MAX_PACKET_SIZE` in der `PubSubClient.h` Datei auf `1024` erhöht werden. Diese Datei ist unter Windows zu finden unter: `C:\Users\*YOUR_USERNAME*\Documents\Arduino\libraries\pubsubclient-master\src\PubSubClient.h`
## Datenverarbeitung: Node-Red
Die Auswertung der Messwerte erfolgt in Node-Red.
Ich habe einen Flow erstellt, welcher die Werte im Node-Red Dashboard grafisch darstellt sowie in eine Influx-DB Datenbank schreibt. Somit ist eine weitere Auswertung mit Grafana möglich. In Node-Red wird ebenfalls die Berechnung des UV-Risikofaktors (UV-Index) durchgeführt. Die Messwerte werden in globale Variablen geschieben, sodass sie innerhalb von Node-Red weiterverwendet werden können (z.B. für das [MQTT Weather Display](https://github.com/dustinbrun/MQTT-Weather-display))
[Screenshot des Node-Red Flows](Node-Red/node-red-flow.PNG)
Der Flow kann aus der Github Repository heruntergeladen werden.
# Quellen
- https://github.com/jp112sdl/HB-UNI-Sen-WEA
- Rain sensor: https://www.heise.de/developer/artikel/Darf-es-etwas-mehr-sein-Anschluss-von-Umweltsensoren-3339616.html
- MQTT: https://randomnerdtutorials.com/esp8266-and-node-red-with-mqtt/
- BME280, debug functions: https://github.com/adlerweb/ESP8266-BME280-Multi/blob/master/src/main.cpp
- Stevenson Screen: https://www.thingiverse.com/thing:2755149
- Weitere Quellen (Bibiliotheken etc.) sind im Code verlinkt
<br><br>
<p xmlns:dct="http://purl.org/dc/terms/" xmlns:cc="http://creativecommons.org/ns#" class="license-text">This work by <span property="cc:attributionName">Dustin Brunner</span> is licensed under <a rel="license" href="https://creativecommons.org/licenses/by/4.0">CC BY 4.0<img style="height:15px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" /><img style="height:15px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" /></a></p>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons Lizenzvertrag" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />Dieses Werk von <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Dustin Brunner</span> ist lizenziert unter einer <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Namensnennung 4.0 International Lizenz</a>.

View File

@ -0,0 +1,159 @@
/*
WARNING:
If you are not receiving the Json Messages you probably have to increase the
MQTT_MAX_PACKET_SIZE in the PubSubClient.h file to at least 1024
(Located on Windows at C:\Users\*YOUR_USERNAME*\Documents\Arduino\libraries\pubsubclient-master\src\PubSubClient.h)
*/
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h> //https://github.com/adafruit/Adafruit_BME280_Library
#include <BH1750.h> //https://github.com/claws/BH1750
#include <ESP8266WiFi.h>
#include <PubSubClient.h> //https://github.com/knolleary/pubsubclient
#include <OneWire.h>
#include <DallasTemperature.h> //https://github.com/milesburton/Arduino-Temperature-Control-Library
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
#include "Adafruit_VEML6070.h" // https://github.com/adafruit/Adafruit_VEML6070
const char *ssid = "----WIFI_SSID_HERE----";
const char *password = "----WIFI_PASSWORD_HERE----";
//MQTT Server
const char *mqtt_server = "----MQTT_SERVER_IP_HERE----";
const char *mqtt_user = "----MQTT_USERNAME_HERE----";
const char *mqtt_pass = "----MQTT_PASSWORD_HERE----";
uint8_t ds18b20_2m[8] = {0x28, 0xB4, 0x2A, 0xA7, 0x4D, 0x20, 0x01, 0x11}; //You need to adapt these addresses
uint8_t ds18b20_0m[8] = {0x28, 0xA9, 0x2F, 0x1C, 0x0B, 0x00, 0x00, 0xA8};
const int hoehe = 437; //Aufstellhoehe in m
//Pins
const int dataled = D8;
const int onewire = D3; //DS18B20
const int regensensorpin = A0;
const int regensensorpower = D5;
#define TEMPERATURE_PRECISION 11 //9-12 Bit for DS18B20
/* 9-12 Bit
9 - 0.5 Steps
10 - 0.25 Steps
11 - 0.125 Steps <- Best for this Case
12 - 0.0625 Steps
*/
Adafruit_BME280 bme;
Adafruit_VEML6070 uvMeter = Adafruit_VEML6070();
BH1750 lightMeter;
WiFiClient espClient;
PubSubClient client(espClient);
OneWire oneWire(onewire);
DallasTemperature ds18b20(&oneWire);
StaticJsonBuffer<1024> jsonBuffer;
//Variablen
float temp = 0;
float bodentemp = 0;
float druck_sensorwert = 0;
float druck = 0;
float feuchte = 0;
float lux = 0;
int uv = 0;
float regensensorwert = 0;
void setup() {
Serial.begin(115200);
Serial.println("MQTT Wetterstation");
pinMode(dataled, OUTPUT);
pinMode(regensensorpin, INPUT_PULLUP);
pinMode(regensensorpower, OUTPUT);
digitalWrite(regensensorpower, LOW);
ledon();
delay(500);
ds18b20.begin();
ds18b20.setResolution(ds18b20_2m, TEMPERATURE_PRECISION);
ds18b20.setResolution(ds18b20_0m, TEMPERATURE_PRECISION);
// SDA, SCL
Wire.begin(D2, D1);
if (! bme.begin(0x76))
{
Serial.println("Could not find BME280 sensor");
//while (1) {}
}
else
{
Serial.println("BME280 initialized.");
}
if (! lightMeter.begin())
{
Serial.println("Could not find BH1750 sensor");
//while (1) {}
}
else
{
Serial.println("BH1750 initialized.");
}
uvMeter.begin(VEML6070_4_T);
/*
possible integration times: -> adapt the convert_to_risk_level-function accordingly!
VEML6070_HALF_T ~62.5ms
VEML6070_1_T ~125ms
VEML6070_2_T ~250ms
VEML6070_4_T ~500ms
*/
uv = uvMeter.readUV();
if (uv == -1 || uv == 65535)
{
Serial.println("Could not find VEML6070 sensor");
//while (1) {}
}
else
{
Serial.println("VEML6070 initialized.");
}
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
ledblink();
Serial.println("-----Initialisierung beendet-----");
}
void loop() {
if (!client.connected())
reconnect();
client.loop();
delay(100);
}

View File

@ -0,0 +1,24 @@
void ledon() {
for (int fadeValue = 0 ; fadeValue <= 255; fadeValue += 5) {
analogWrite(dataled, fadeValue);
delay(7);
}
}
void ledoff() {
for (int fadeValue = 255 ; fadeValue >= 0; fadeValue -= 5) {
analogWrite(dataled, fadeValue);
delay(7);
}
}
void ledblink() {
ledon();
delay(200);
ledoff();
delay(200);
ledon();
delay(200);
ledoff();
delay(200);
}

View File

@ -0,0 +1,104 @@
/*
Json format:
{
"temp_2m": 22.45,
"temp_0m": 12.45,
"druck": 1234.56,
"feuchte": 45.4,
"helligkeit": 12345.56,
"regensensor": "256",
"uvsensor" : "1234"
}
*/
void sendmqtt() {
JsonObject& json = jsonBuffer.createObject();
json["temp_2m"] = temp;
json["temp_0m"] = bodentemp;
json["druck"] = druck;
json["feuchte"] = feuchte;
json["helligkeit"] = lux;
json["regensensor"] = regensensorwert;
json["uvsensor"] = uv;
char output[1025];
json.printTo(output, sizeof(output));
client.publish("wetter_außen/get", output);
client.publish("wetter_außen/update", "1");
jsonBuffer.clear();
}
void reconnect() {
while (!client.connected()) {
Serial.print("MQTT Verbindungsversuch...");
if (client.connect("ESP8266_Wetterstation_außen", mqtt_user, mqtt_pass)) {
Serial.println("connected");
ledoff();
client.subscribe("wetter_außen/debug/#");
client.subscribe("wetter_außen/set/#");
}
else {
Serial.print("fehlgeschlagen, rc=");
Serial.print(client.state());
Serial.println("Erneuter Versuch in 5 Sekunden");
ledon();
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print(F("Message arrived ["));
Serial.print(topic);
Serial.print(F("] "));
char message[length + 1];
for (unsigned int i = 0; i < length; i++) {
message[i] = (char)payload[i];
}
message[length] = '\0';
Serial.println(message);
String topicStr = topic;
if (topicStr.startsWith("wetter_außen/set"))
{
readsensors();
sendmqtt();
return;
}
if (topicStr.startsWith("wetter_außen/debug/reset")) {
Serial.println("MQTT RESET!");
Serial.flush();
ESP.restart();
}
if (topicStr.startsWith("wetter_außen/debug/ping")) {
Serial.println("PING");
client.publish("wetter_außen/debug/pong", message, false);
return;
}
if (topicStr.startsWith("wetter_außen/debug/wifi/ping")) {
//@author Marvin Roger - https://github.com/marvinroger/homie-esp8266/blob/ad876b2cd0aaddc7bc30f1c76bfc22cd815730d9/src/Homie/Utils/Helpers.cpp#L12
int32_t rssi;
rssi = WiFi.RSSI();
uint8_t quality;
if (rssi <= -100) {
quality = 0;
} else if (rssi >= -50) {
quality = 100;
} else {
quality = 2 * (rssi + 100);
}
char qualityString[8];
dtostrf(quality, 1, 1, qualityString);
client.publish("wetter_außen/debug/wifi/pong", qualityString);
return;
}
}

View File

@ -0,0 +1,51 @@
void readsensors() {
ledon();
digitalWrite(regensensorpower, HIGH);
delay(50);
temp = 0;
bodentemp = 0;
druck_sensorwert = 0;
feuchte = 0;
lux = 0;
regensensorwert = 0;
uv = 0;
for (int i = 0; i < 3; ++i) {
//BME280
//temp += bme.readTemperature();
druck_sensorwert += bme.readPressure();
feuchte += bme.readHumidity();
//BH1750
lux += lightMeter.readLightLevel();
//DS18B20
ds18b20.requestTemperatures();
bodentemp += ds18b20.getTempC(ds18b20_0m);
temp += ds18b20.getTempC(ds18b20_2m);
//VEML6070
uv += uvMeter.readUV(); //measurement takes as long as the integration time is set
//Regensensor
regensensorwert += analogRead(regensensorpin);
delay(100);
}
temp = temp / 3;
druck_sensorwert = druck_sensorwert / 3;
feuchte = feuchte / 3;
lux = lux / 3;
bodentemp = bodentemp / 3;
uv = uv / 3;
regensensorwert = regensensorwert / 3;
digitalWrite(regensensorpower, LOW);
//Rechnen
//druck = (druck_sensorwert / 100) + (hoehe / 8.5);
druck = druck_sensorwert / 100;
druck = bme.seaLevelForAltitude(hoehe, druck);
ledoff();
}

Binary file not shown.

BIN
ESP8266/Wetterstation.fzz Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@ -5,7 +5,8 @@ Dieses Projekt besteht aus mehreren Teilen:
## 1. [Grundlagen: VEML 6070 Sensor, UV-Index Berechnung](VEML_6070/README.md)
## 2. [Bresser Wetterstation MQTT-Anbindung mit RTL-433](rtl_433_Bresser/README.md)
## [Zusatzinfos: verwendete Berechnungsformeln](Berechnungsfunktionen.md)
## 3. [Erweiterung der Bresser Wetterstation: ESP8266-Wettersensoren](ESP8266/README.md)
## 4. [Zusatzinfos: verwendete Berechnungsformeln](Berechnungsfunktionen.md)