Wetterstation mit Anenometer
Autoren: Specki & Eric in einem Studienprojekt am ifgi, April 2016
In dieser senseBox werden Umweltdaten gemessen und an die openSenseMap übermittelt, die dadurch als Basis für verschiedene Karten dienen kann. Die Station wird durch einen zusätzlichen Windmesser (Anenometer) ergänzt.
Materialien
Aus der senseBox:edu
- Genuino UNO
- HDC1000 (Temperatur&Luftfeuchtigkeitssensor)
- VEML6070 (UV-Lichtsensor)
- TSL45315 (Lichtsensor)
Zusätzliche Hardware
- Anemometer (Windsensor)
- Kupferplatine
- diverse Kabel
- Ethernet-Kabel
- Power-over-Ethernet Adapter
Setup
Hardwarekonfiguration
Auf das Genuino/Uno ist das WIZnet Ethernet Shield gesteckt um Netzwerk und Stromverbindung zu ermöglichen. Zusätzlich haben wir das Board um eine Kupferplatine erweitert um UV und Lichtsensoren horizontal ausgerichtet zu befestigen, damit korrekte Messdaten ermittelt werden können. Während sich die oben genannten Sensoren innerhalb der Wetterfest-Box befinden, sind Temperatursensor und Anemometer außerhalb der Box installiert um die Messdaten nicht zu verfälschen. Über ein Ethernet-Kabel werden die Messdaten in einem vorgegebenen Intervall an die openSenseMap übermittelt. Die Stromversorgung der gesamten Box wird über einen Power-over-Ethernet Adapter realisiert.
Improvisiertes Shield für den Arduino:
Das improvisierte Shield für den Arduino basiert auf einer einfachen einseitigen Streifenrasterplatine. Da fast alle Sensoren über I2C ausgelesen werden, wurden die für I2C benötigten Pins (GND,VCC,SCL,SDA) direkt nebeneinander gelegt. Dies ermöglicht es, mit einem einzelnen Kabel mit 3er-Stecker die auf den Sensoren direkt nebeneinanderliegenden Anschlüsse VCC,SCL,SDA direkt anzuschließen. Zusätzlich wird noch ein einzelnes Kabel für GND benötigt. Durch die Verwendung der 3er-Stecker sinkt die Anzahl der benötigten Kabel auf zwei, was hilft die gesamte Schaltung übersichtlich zu halten. Schaltplan:
In dem Schaltplans wurde auf Grund der Übersichtlichkeit auf die 3er-Stecker verzichtet (Kabelstränge würden dauerhaft überlappen -> Schaltung nicht mehr klar zu erkennen). Stattdessen sind die Anschlüsse zu den Sensoren durch 3 parrallele einzelne Kabelverbindungen dargestellt.
Das Anemometer gibt die Windgeschwindigkeit als analoges Stromsignal zwischen 0,4V und 2V über das Violett eingezeichnete Kabel wieder. Das Anemometer benötigt eine eigene Stromversorgung von 9-25V. Diese wird von einer zusätzlichen Batteriebox innerhalb der senesBox gewährleistet.
Wichtig in dem Schaltplan zu beachten ist zudem, dass der Minuspol der zusätzlichen Stromversorgung mit dem Minuspol des Arduino verbunden ist! Ist dies nicht der Fall, ist es für den Arduino unmöglich die Spannungsschwankungen auf dem Signalkabel des Anemometers zu erkennen!
Fertig zusammengebaute Box mit zusätzlicher integrierter Stromversorgung:
Softwaresketch
Der Softwaresketch basiert auf der von der openSenseMap bereitgestellten Schablone.
Neben dem eigentlichen Lesen der konkreten Sensorwerte werden zusätzlich verschiedene Umrechnungen direkt im Programmcode realisiert. Da das Anemometer die Windgeschwindigkeit über ein analoges Stromsignal wiedergibt (mehr Strom = höhere Windgeschwindigkeit) ist eine Umrechnung von Volt zu Windgeschwindigkeit(m/s) implementiert worden. Auch bei der UV-Intensität muss eine ähnliche Umrechnung stattfinden um das gewünschte Endergebnis zu erhalten (µW/cm²). Die restlichen Sensoren geben direkt nutzbare Werte aus und werden daher direkt hochgeladen.
Links zu zusätzlichen Bibliotheken die für einige der Sensoren/Shields benötigt werden
Programmcode:
#include <SPI.h>
#include <Ethernet.h>
/*
* Zusätzliche Sensorbibliotheken, -Variablen etc im Folgenden einfügen.
*/
#include <Wire.h>
#include <HDC100X.h>
//SenseBox ID
#define SENSEBOX_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
//Sensor IDs und MAC-Addressen wurden zensiert
#define SENSOR1_ID "XXXXXXXXXXXXXXXXXXXXXXXX" // Luftfeuchtigkeit
#define TEMPSENSOR_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
#define UVSENSOR_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
#define SENSOR2_ID "XXXXXXXXXXXXXXXXXXXXXXXX" // Licht
#define SENSOR3_ID "XXXXXXXXXXXXXXXXXXXXXXXX" // Wind (Anemometer)
Im folgenden werden Variablen für die internen Umrechnungen deklariert
// I2C Variablen Lichtsensor
#define I2C_ADDR_LUX (0x29)
#define REG_CONTROL_LUX 0x00
#define REG_CONFIG_LUX 0x01
#define REG_DATALOW_LUX 0x04
#define REG_DATAHIGH_LUX 0x05
#define REG_ID_LUX 0x0A
// I2C Variablen UV-Sensor
#define I2C_ADDR_UV (0x38)
//Integration Time
#define IT_1_2 0x0 //1/2T
#define IT_1 0x1 //1T
#define IT_2 0x2 //2T
#define IT_4 0x3 //4T
// Initialisierung des Temp/Humi Sensors
HDC100X hdc_temp_humi(0x43);
// Analogpin Windsensor
const int analogSensorPin = A3;
Netzwerkeinstellungen:
//Ethernet-Parameter
char server[] = "www.opensensemap.org";
byte mac[] = { XXXX, XXXX, XXXX, XXXX, XXXX, XXXX };
// Diese IP Adresse nutzen falls DHCP nicht möglich
IPAddress myIP(XXXX, XXXX, XXXX, XXXX);
EthernetClient client;
//Messparameter
int postInterval = 10000; //Uploadintervall in Millisekunden
long oldTime = 0;
void setup()
{
Serial.begin(9600);
Netzwerksetup wurde aus der von openSenseMap bereitgestellten Schablone übernommen:
Serial.print("Starting network...");
//Ethernet Verbindung mit DHCP ausführen..
if (Ethernet.begin(mac) == 0)
{
Serial.println("DHCP failed!");
//Falls DHCP fehltschlägt, mit manueller IP versuchen
Ethernet.begin(mac, myIP);
}
Serial.println("done!");
delay(1000);
Serial.println("Starting loop.");
// Analoger Windsensor Pin
pinMode(analogSensorPin , INPUT);
}
void loop()
{
//Upload der Daten mit konstanter Frequenz
if (millis() - oldTime >= postInterval)
{
oldTime = millis();
Die Sensoren werden der Reihenfolge nach ausgelesen. Lichtsensor wird erst über I2C gestartet und daraufhin die Belichtungszeit festgelegt. Anschließend werden 2 Bytes von dem Lichtsensor angefordert. Diese enthalten
// SENSOR 1: Lichtsensor
// Starten des Lichtsensors
Wire.begin();
Wire.beginTransmission(I2C_ADDR_LUX);
Wire.write(0x80 | REG_CONTROL_LUX);
Wire.write(0x03); //Power on
Wire.endTransmission();
// Festlegen der Belichtungszeit
Wire.beginTransmission(I2C_ADDR_LUX);
Wire.write(0x80 | REG_CONFIG_LUX);
Wire.write(0x01); //400 ms Belichtungszeit
Wire.endTransmission();
Der Sensor liefert zwei Bytes mit einem high/low Wert. Diese werden lokal gespeichert um die einfache Umrechnung zu ermöglichen.
Wire.beginTransmission(I2C_ADDR_LUX);
Wire.write(0x80 | REG_DATALOW_LUX);
Wire.endTransmission();
Wire.requestFrom(I2C_ADDR_LUX, 2); //2 Bytes anfordern
uint16_t low = Wire.read();
uint16_t high = Wire.read();
while (Wire.available()) {
Wire.read();
}
Umrechnung von high/low Bytes zu Lux wurde aus dem SenseBox-Wiki übernommen.
uint32_t lux; //Umrechnung für Lux vom Sensebox-Wiki
lux = (high << 8) | (low << 0);
lux = lux * 2; //Multiplikator für 400ms
// 131070 is Error value if Sensor is not connected! Fehlerbehebung!
if (lux != 131070) {
postFloatValue(lux, 1, "XXXXXXXXXXXXXXXXXXXXXXXX");
} else {
Serial.println("Light Sensor not connected/not working!");
}
// Ende von SENSOR 1
Temperatur/Luftfeuchtigkeitssensor wird gestartet. Falls der Temperatursensor inkorrekt angeschlossen oder defekt ist, liefert er einen inkorrekten Messwert von etwa -40. Da dieser Messwert klar als irregulär erkannt werden kann, da er im Einsatzgebiet dieser SenseBox nicht vorkommt(Mitteleuropäisches Klima), wird eine Fehlermeldung an den Seriellen Port geschickt und auf das Hochladen der Daten verzichtet.
// SENSOR 2: Temperature/Humidity Sensor
hdc_temp_humi.begin(HDC100X_TEMP_HUMI, HDC100X_14BIT, HDC100X_14BIT, DISABLE);
// Error value if Sensor is not connected is about -40!
if (hdc_temp_humi.getTemp() > -35) {
postFloatValue(hdc_temp_humi.getTemp(), 1, "XXXXXXXXXXXXXXXXXXXXXXXX");
} else {
Serial.println("Humidity/Temperature Sensor not connected/not working!");
}
Analog zu der Fehlererkennung bei dem Temperatursensor, werden auch bei dem Luftfeuchtigkeitssensor Daten die offensichtlich nicht der Realität entsprechen (Luftfeuchtigkeit von weniger als 0,5%) nicht an die openSenseMap übertragen.
// Error value if Sensor is not connected is about 0.4!
if (hdc_temp_humi.getHumi() > 0.5) {
postFloatValue(hdc_temp_humi.getHumi(), 1, "XXXXXXXXXXXXXXXXXXXXXXXX");
} else {
Serial.println("Humidity/Temperature Sensor not connected/not working!");
}
// Ende von SENSOR 2
Das Auslesen des UV-Sensors wurde aus dem Anwendungsbeispiel des Sensors von der offiziellen Herstellerseite übernommen. Weitere Informationen hier. Wieder wurde eine einfache Fehlererkennung implementiert (UV-Sensor gibt '3' aus wenn defekt).
// SENSOR 4 : UV-Sensor
// Umrechnung vom Hersteller übernommen
Wire.beginTransmission(I2C_ADDR_UV);
Wire.write((IT_1 << 2) | 0x02);
Wire.endTransmission();
byte msb = 0, lsb = 0;
uint16_t uv;
Wire.requestFrom(I2C_ADDR_UV + 1, 1); //MSB
delay(1);
if (Wire.available())
msb = Wire.read();
Wire.requestFrom(I2C_ADDR_UV + 0, 1); //LSB
delay(1);
if (Wire.available())
lsb = Wire.read();
uv = (msb << 8) | lsb;
if (uv != 3) { //Fehlerbehebung!
postFloatValue(uv * 5.625, 1 , "XXXXXXXXXXXXXXXXXXXXXXXX");
} else {
Serial.println("UV Sensor not connected/not working!");
}
// Ende von SENSOR 4
Die Umrechnung von der 0-1023 Skala der analogRead() Funktion des Arduino zu m/s Windgeschwindigkeit wurde übernommen. Genauere Erklärung zu der Umrechnung in den Inline-Kommentaren.
// SENSOR 5: Wind
/*
* Code taken from : http://www.hackerscapes.com/2014/11/anemometer/
*
* All credits belong to their respectful owners.
* Edited on 07.04.2016 by Specki
*/
int sensorValue = 0; //Variable stores the value direct from the analog pin
float sensorVoltage = 0; //Variable that stores the voltage (in Volts) from the anemometer being sent to the analog pin
float windSpeed = 0; // Wind speed in meters per second (m/s)
float voltageConversionConstant = .004882814; //This constant maps the value provided from the analog read function, which ranges from 0 to 1023, to actual voltage, which ranges from 0V to 5V
int sensorDelay = 1000; //Delay between sensor readings, measured in milliseconds (ms)
//Anemometer Technical Variables
//The following variables correspond to the anemometer sold by Adafruit, but could be modified to fit other anemometers.
float voltageMin = .4; // Mininum output voltage from anemometer in mV.
float windSpeedMin = 0; // Wind speed in meters/sec corresponding to minimum voltage
float voltageMax = 2.0; // Maximum output voltage from anemometer in mV.
float windSpeedMax = 32; // Wind speed in meters/sec corresponding to maximum voltage
sensorValue = analogRead(analogSensorPin); //Get a value between 0 and 1023 from the analog pin connected to the anemometer
sensorVoltage = sensorValue * voltageConversionConstant; //Convert sensor value to actual voltage
//Convert voltage value to wind speed using range of max and min voltages and wind speed for the anemometer
if (sensorVoltage <= voltageMin) {
windSpeed = 0; //Check if voltage is below minimum value. If so, set wind speed to zero.
} else {
windSpeed = (sensorVoltage - voltageMin) * windSpeedMax / (voltageMax - voltageMin); //For voltages above minimum value, use the linear relationship to calculate wind speed.
}
//Print windspeed to serial
postFloatValue(windSpeed, 1 , "XXXXXXXXXXXXXXXXXXXXXXXX");
// Ende von SENSOR 5
}
}
Routinen zum Upload der Messdaten wurde aus der von der openSenseMap bereitgestellten Schablone übernommen.
void postFloatValue(float measurement, int digits, String sensorId)
{
//Float zu String konvertieren
char obs[10];
dtostrf(measurement, 5, digits, obs);
//Json erstellen
String jsonValue = "{\"value\":";
jsonValue += obs;
jsonValue += "}";
//Mit OSeM Server verbinden und POST Operation durchführen
Serial.println("-------------------------------------");
Serial.print("Connectingto OSeM Server...");
if (client.connect(server, 8000))
{
Serial.println("connected!");
Serial.println("-------------------------------------");
//HTTP Header aufbauen
client.print("POST /boxes/"); client.print(SENSEBOX_ID); client.print("/"); client.print(sensorId); client.println(" HTTP/1.1");
client.println("Host: www.opensensemap.org");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.print("Content-Length: "); client.println(jsonValue.length());
client.println();
//Daten senden
client.println(jsonValue);
} else
{
Serial.println("failed!");
Serial.println("-------------------------------------");
}
//Antwort von Server im seriellen Monitor anzeigen
waitForServerResponse();
}
void waitForServerResponse()
{
//Ankommende Bytes ausgeben
boolean repeat = true;
do {
if (client.available())
{
char c = client.read();
Serial.print(c);
}
//Verbindung beenden
if (!client.connected())
{
Serial.println();
Serial.println("--------------");
Serial.println("Disconnecting.");
Serial.println("--------------");
client.stop();
repeat = false;
}
} while (repeat);
}
openSenseMap Registrierung
Die Station wurde auf der openSenseMap unter dem Namen "Wetterstation Specki & Eric " registriert. Es wurden insgesamt 5 Sensoren angegeben:
- Lichtintensität (in Lux)
- UV-Intensität(µW/cm²)
- Temperatur(°C)
- Windgeschwindigkeit (m/s)
- Luftfeuchtigkeit(%)
Die Box kann direkt unter der Adresse * Wetterstation Specki & Eric erreicht werden.
Stationsaufbau
Die Station wird am Michaelweg, 48149 Münster in einem Garten aufgestellt. Das Anemometer befindet sich in möglichst offener Lage, sodass die Messdaten möglichst wenig verfälscht werden. Auch der Temperatur/Feuchtigkeitssensor befindet sich außerhalb der Box, damit die Daten möglichst wenig verfälscht werden. Licht/UV-Sensor befinden sich innerhalb der Box.
Platzierung der Box: