commit 9403ac5bffbfe9a82376b5564ead577bb310b8b2 Author: Dustin Brunner Date: Sat Jan 22 17:41:55 2022 +0100 Teil 1 diff --git a/Berechnungsfunktionen.md b/Berechnungsfunktionen.md new file mode 100644 index 0000000..20d841c --- /dev/null +++ b/Berechnungsfunktionen.md @@ -0,0 +1,257 @@ +# Wetterstation Berechnungen +Hier sind einige nützliche Berechnungsfunktionen und Formeln zu finden, welch ich in meiner Wetterstation verwende. Die Berechnungsfunktionen sind in Javascript zur Verwendung in Node-Red geschieben, können jedoch einfach auch für andere Plattformen angepasst werden (z.B. Arduino). + +Die Quellen sind jeweils verlinkt. + +# Taupunkt berechnen +Quelle: https://myscope.net/taupunkttemperatur/ + +Die Berechnung des Taupunktes erfolgt aus den Messwerten Temperatur (°C) und Luftfeuchtigkeit (%). + +``` +var calcdewpoint = function(celsius, humidity) { + + var a, b; + if (celsius >= 0) { + a = 7.5; + b = 237.3; + } else if (celsius < 0) { + a = 7.6; + b = 240.7; + } + + // Sättigungsdampfdruck (hPa) + var sdd = 6.1078 * Math.pow(10, (a * celsius) / (b + celsius)); + + // Dampfdruck (hPa) + var dd = sdd * (humidity / 100); + + // v-Parameter + var v = Math.log10(dd / 6.1078); + + // Taupunkttemperatur (°C) + var td = (b * v) / (a - v); + + //Runden 1 Nachkommastelle + td = Math.round(td * 10) / 10; + + return td; +} + +msg.payload = calcdewpoint(12.45, 80); + +return msg; +``` +# Gefühlte Temperatur berechnen +Die Gefühlte Temperatur setzt sich aus zwei Berechnungsarten zusammen. Windchill (Temperatur kleiner als 10°C und Windgeschwindigkeit größer als 4.8km/h) und Hitzeindex (Temperatur größer als 26.7°C und Relative Luftfeuchte größer als 40%). + +``` + +//Quelle: https://myscope.net/windchill-gefuehlte-temperatur-berechnen/ +// https://de.wikipedia.org/wiki/Windchill +var calcwindchill = function(celsius, windspeed) { + var windchill = 13.12 + 0.6215 * celsius - 11.37 * Math.pow(windspeed, 0.16) + 0.3965 * celsius * Math.pow(windspeed, 0.16); + return windchill; +} + +//Quelle: https://myscope.net/hitzeindex-gefuehle-temperatur/ +// https://de.wikipedia.org/wiki/Hitzeindex +var calcheatindex = function(celsius, humidity) { + return (-8.784695 + 1.61139411 * celsius + 2.338549 * humidity - 0.14611605 * celsius * humidity - 0.012308094 * celsius * celsius - 0.016424828 * humidity * humidity + 0.002211732 * celsius * celsius * humidity + 0.00072546 * celsius * humidity * humidity - 0.000003582 * celsius * celsius * humidity * humidity); +} + +var Temperatur_2m = parseFloat(12.34); +var Luftfeuchte_rel = parseFloat(80); +var windkmh = parseFloat(2.45); + +if (Temperatur_2m <= 10 && windkmh >= 4.8) +{ + msg.payload = calcwindchill(Temperatur_2m, windkmh); + msg.topic = "123"; +} +else if (Temperatur_2m >= 26.7 && Luftfeuchte_rel >= 40) +{ + msg.payload = calcheatindex(Temperatur_2m, Luftfeuchte_rel); + msg.topic = "246"; +} +else //Keine der beiden Formeln ist definiert +{ + //msg.payload = -1; + msg.payload = Temperatur_2m; +} + +//Runden 1 Nachkommastelle +msg.payload = Math.round(msg.payload * 10) / 10; + +return msg; +``` + +# Absolute Luftfeuchtigkeit +Eine Umrechnung von Relativer (in %) in Absolute Luftfeuchtigkeit (in g/m^3). + +``` +// Relative to absolute humidity +// Based on https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/ +var absoluteHumidity = function(temperature, humidity) { + return (13.2471*Math.pow(2.7182818,17.67*temperature/(temperature+243.5))*humidity/(273.15+temperature)); +} + +var Temperatur_2m = parseFloat(12.34); +var Luftfeuchte_rel = parseFloat(45); + +msg.payload = absoluteHumidity(Temperatur_2m, Luftfeuchte_rel); + +//Runden 1 Nachkommastelle +msg.payload = Math.round(msg.payload * 10) / 10; + +return msg; +``` + +# Windgeschwindigkeit Meter pro Sekunde in Kilometer pro Stunde +Eine Umrechnung der Windgeschwindigkeit vom m/s in km/h sowie runden des Ergebnisses auf eine Nachkommastellen. +``` +msg.payload = Math.round(msg.payload * 3.6 * 10) / 10; + +return msg; +``` + +# Windgeschwindigkeit von Kilometer pro Stunde in Windstärke/Beaufortskala umrechnen +Referenz: https://wetterkanal.kachelmannwetter.com/woher-kommt-die-beaufortskala/ +Die Berechnungen beziehen sich auf die Einheit m/s. + +``` +var windmax = parseFloat(msg.payload) / 3.6; + +var windstarkewort = {}; +windstarkewort.topic = "Windstärke_Wort"; + +var windstarkebft = {}; +windstarkebft.topic = "Windstärke_bft"; + +if (windmax < 0.3) { + windstarkebft.payload = 0; + windstarkewort.payload = "Windstille"; + } + else if (windmax >= 0.3 && windmax < 1.6) { + windstarkebft.payload = 1; + windstarkewort.payload = "leiser Zug"; + } + else if (windmax >= 1.6 && windmax < 3.4 ) { + windstarkebft.payload = 2; + windstarkewort.payload = "leichte Brise"; + } + else if (windmax >= 3.4 && windmax < 5.5) { + windstarkebft.payload = 3; + windstarkewort.payload = "schwache Brise"; + } + else if (windmax >= 5.5 && windmax < 8.0 ) { + windstarkebft.payload = 4; + windstarkewort.payload = "mäßige Brise"; + } + else if (windmax >= 8.0 && windmax < 10.8 ) { + windstarkebft.payload = 5; + windstarkewort.payload = "frische Brise"; + } + else if (windmax >= 10.8 && windmax < 13.9 ) { + windstarkebft.payload = 6; + windstarkewort.payload = "starker Wind"; + } + else if (windmax >= 13.9 && windmax < 17.2 ) { + windstarkebft.payload = 7; + windstarkewort.payload = "steifer Wind"; + } + else if (windmax >= 17.2 && windmax < 20.8 ) { + windstarkebft.payload = 8; + windstarkewort.payload = "stürmischer Wind"; + } + else if (windmax >= 20.8 && windmax < 24.5 ) { + windstarkebft.payload = 9; + windstarkewort.payload = "Sturm"; + } + else if (windmax >= 24.5 && windmax < 28.5 ) { + windstarkebft.payload = 10; + windstarkewort.payload = "schwerer Sturm"; + } + else if (windmax >= 28.5 && windmax < 32.7 ) { + windstarkebft.payload = 11; + windstarkewort.payload = "orkanartiger Sturm"; + } + else if (windmax >= 32.7) { + windstarkebft.payload = 12; + windstarkewort.payload = "Sturm"; + } + + +return [windstarkewort, windstarkebft]; + +``` + +# Windrichtung "Grad in Wort" +Eine Umrechnung einer Windrichtung in Grad (0° entpsricht Nord) in Himmelsrichtungen (z.B. NO, S usw.). + +``` +var windrichtung = parseInt(msg.payload); + +var windrichtungwort = {}; +windrichtungwort.topic = "Windrichtung_Wort"; + + +if (windrichtung >= 348 && windrichtung < 12) { + windrichtungwort.payload = "N"; + } + else if (windrichtung >= 12 && windrichtung < 35) { + windrichtungwort.payload = "NNO"; + } + else if (windrichtung >= 35 && windrichtung < 57) { + windrichtungwort.payload = "NO"; + } + else if (windrichtung >= 57 && windrichtung < 80) { + windrichtungwort.payload = "NOO"; + } + else if (windrichtung >= 80 && windrichtung < 102) { + windrichtungwort.payload = "O"; + } + else if (windrichtung >= 102 && windrichtung < 125) { + windrichtungwort.payload = "SOO"; + } + else if (windrichtung >= 125 && windrichtung < 147) { + windrichtungwort.payload = "SO"; + } + else if (windrichtung >= 147 && windrichtung < 170) { + windrichtungwort.payload = "SSO"; + } + else if (windrichtung >= 170 && windrichtung < 192) { + windrichtungwort.payload = "S"; + } + else if (windrichtung >= 192 && windrichtung < 215) { + windrichtungwort.payload = "SSW"; + } + else if (windrichtung >= 215 && windrichtung < 237) { + windrichtungwort.payload = "SW"; + } + else if (windrichtung >= 237 && windrichtung < 260) { + windrichtungwort.payload = "SWW"; + } + else if (windrichtung >= 260 && windrichtung < 282) { + windrichtungwort.payload = "W"; + } + else if (windrichtung >= 282 && windrichtung < 305) { + windrichtungwort.payload = "NWW"; + } + else if (windrichtung >= 305 && windrichtung < 327) { + windrichtungwort.payload = "NW"; + } + else if (windrichtung >= 327 && windrichtung < 348) { + windrichtungwort.payload = "NNW"; + } + else { + windrichtungwort.payload = "???"; + } + +return windrichtungwort; +``` + +

+

This work by Dustin Brunner is licensed under CC BY 4.0

+ +Creative Commons Lizenzvertrag
Dieses Werk von Dustin Brunner ist lizenziert unter einer Creative Commons Namensnennung 4.0 International Lizenz. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fcef0a8 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Wetterstation +MQTT-Wetterstation mit Datenauswertung und -Darstellung in Node-Red sowie Aufzeichung durch Influx-DB. + +Dieses Projekt besteht aus mehreren Teilen: + +## [Grundlagen: VEML 6070 Sensor, UV-Index Berechnung](VEML_6070/README.md) +## [Zusatzinfos: verwendete Berechnungsformeln](Berechnungsfunktionen.md) + + + + + + +

+

This work by Dustin Brunner is licensed under CC BY 4.0

+ +Creative Commons Lizenzvertrag
Dieses Werk von Dustin Brunner ist lizenziert unter einer Creative Commons Namensnennung 4.0 International Lizenz. diff --git a/VEML_6070/README.md b/VEML_6070/README.md new file mode 100644 index 0000000..406221e --- /dev/null +++ b/VEML_6070/README.md @@ -0,0 +1,53 @@ +# VEML6070 UV Sensor + +[](VEML_6070.png) + +The VEML6070 is a UV sensor with I2C-Interface. +It measures the UV light level but **NOT** the UV-Index. However, it is possible to convert the sensor readings to an equivalent UV-risk level/UV-Index. + +> Unlike the Si1145 (http://adafru.it/1777), this sensor will not give you UV Index readings. However, the Si1145 does UV Index approximations based on light level not true UV sensing. The VEML6070 in contrast does have a real light sensor in the UV spectrum. + +## Using the Sensor with Arduino +The sensor can be easily connected to the I2C pins of the Arduino (for Arduino UNO + Nano these are the pins A4 and A5). + +To read the measurements the `Adafruit_VEML6070`-Library can be used. +It can be installed directly via the Arduino Library Manager. Alternatively, it can be downloaded from GitHub: https://github.com/adafruit/Adafruit_VEML6070 + + +## Convert measurements to UV-Risk level +The conversion is implemented in the equivalent Adafruit library for Circuit Python (https://github.com/adafruit/Adafruit_CircuitPython_VEML6070). However it is not implemented in the Arduino version. + +I adapted the Circuit Python code and created an extended example sketch with risk-level-conversion. + +``` +String convert_to_risk_level(int reading) +{ + int integration_time = 4; //available for Integration-Time 1, 2, 4 + // MUST be adjusted according to the set integration time + reading = reading / integration_time; + + String risk_level; + + if(reading <= 560) + risk_level = "LOW (UV 0-2)"; + else if(reading > 560 && reading <= 1120) + risk_level = "Moderate (UV 3-5)"; + else if(reading > 1120 && reading <= 1494) + risk_level = "High (UV 6-7)"; + else if(reading > 1494 && reading <= 2054) + risk_level = "Very High (UV 8-10)"; + else if(reading > 2054 && reading <= 9999) + risk_level = "Extreme (UV >10)"; + else + risk_level = "ERROR"; + + return risk_level; +} + + +``` + +## You can the full example sketch [here](./vemltest_extended/vemltest_extended.ino). + +
+References/Sources: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-veml6070-uv-light-sensor-breakout.pdf \ No newline at end of file diff --git a/VEML_6070/VEML_6070.png b/VEML_6070/VEML_6070.png new file mode 100644 index 0000000..7ba0026 Binary files /dev/null and b/VEML_6070/VEML_6070.png differ diff --git a/VEML_6070/vemltest_extended/vemltest_extended.ino b/VEML_6070/vemltest_extended/vemltest_extended.ino new file mode 100644 index 0000000..33c5c07 --- /dev/null +++ b/VEML_6070/vemltest_extended/vemltest_extended.ino @@ -0,0 +1,82 @@ +#include +#include "Adafruit_VEML6070.h" + +Adafruit_VEML6070 uv = Adafruit_VEML6070(); + +void setup() { + Serial.begin(9600); + Serial.println("VEML6070 Test"); + uv.begin(VEML6070_2_T); // pass in the integration time constant + /* + 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 + + */ +} + + +//Source: https://github.com/adafruit/Adafruit_CircuitPython_VEML6070/blob/main/adafruit_veml6070.py +String convert_to_risk_level(int reading) +{ + int integration_time = 4; //available for Integration-Time 1, 2, 4 + // MUST be adjusted according to the set integration time + reading = reading / integration_time; + + String risk_level; + + if(reading <= 560) + risk_level = "LOW (UV 0-2)"; + else if(reading > 560 && reading <= 1120) + risk_level = "Moderate (UV 3-5)"; + else if(reading > 1120 && reading <= 1494) + risk_level = "High (UV 6-7)"; + else if(reading > 1494 && reading <= 2054) + risk_level = "Very High (UV 8-10)"; + else if(reading > 2054 && reading <= 9999) + risk_level = "Extreme (UV >10)"; + else + risk_level = "ERROR"; + + return risk_level; +} + +/* +// German equivalent +String convert_to_risk_level(int reading) +{ + int integration_time = 4; //available for Integration-Time 1, 2, 4 + // MUST be adjusted according to the set integration time + reading = reading / integration_time; + + String risk_level; + + if(reading <= 560) + risk_level = "Niedrig (UV 0-2)"; + else if(reading > 560 && reading <= 1120) + risk_level = "Mittel (UV 3-5)"; + else if(reading > 1120 && reading <= 1494) + risk_level = "Hoch (UV 6-7)"; + else if(reading > 1494 && reading <= 2054) + risk_level = "Sehr Hoch (UV 8-10)"; + else if(reading > 2054 && reading <= 9999) + risk_level = "Extrem (UV >10)"; + else + risk_level = "ERROR"; + + return risk_level; +} +*/ + +void loop() { + int reading = uv.readUV(); + + Serial.print("UV light level: "); + Serial.print(reading); + Serial.print(" Risk level is: "); + Serial.println(convert_to_risk_level(reading)); + + delay(1000); +}