Da das Wetter im Augenblick grauenerregend ist suchte ich nach einem neuen Projekt für die regnerischen Tage (wenn ich keine Lust aufs Spiegelschleifen habe). Inspiriert von Matthias Bopps Wolkensensor überlegte ich mir, ob es möglich wäre etwas zu bauen um den Wolkensensor über den Laptop abzulesen.
Die Hardware:
Ich hatte noch einen NSLU2 mit Linux drauf von einem früheren Experiment rumliegen. Aber wie den Sensor anschließen?

Die Lösung ist der Arduino. Mit diesem kleinen Computer auf Atmel AVR ATmega Basis kann man sehr leicht analoge Sensoren auslesen.

Als Sensoren werden zwei 22k (mein Händler hatte gerade keinen anderen da - 10k müssten auch gehen) NTCs, die als Spannungsteiler geschaltet sind, verwendet. Die 5V Spannung wird vom Arduino an einem Pin zur Verfügung gestellt.

Ich überlegte erst, ob noch ein Widerstand als Sicherung eingebaut werden müsste. Dem Datenblatt der NTCs habe ich entnommen, das die NTCs selbst bei 60°C noch 8k Widerstand haben. Damit werden die Ports des Arduino auf keinen Fall überlastet. Der "Mittelabgriff" des Spannungsteilers wird einem Analog-Eingang des Arduino zugeführt.
In dieser Beschaltung kann man mit den NTCs keine Absoluttemperaturen bestimmen - mir geht es nur um die Temperaturdifferenz. Dieser Sensor ist sehr empfindlich: wenn man sich einem NTC mit dem Finger auf ca. 5mm nähert bekommt man schon einen deutlichen Ausschlag.
Hardwaremäßig sieht das Ganze dann so aus:

Zusätzlich habe ich noch einen Luftdrucksensor (Freescale MPXA4100A6U) und einen Temperatur- und Feuchte-Sensor (Sensirion SHT71) angeschlossen. Eine Library um den SHT71 über seine digitale Schnittstelle auszulesen gibt es hier: http://github.com/practicalarduino/SHT1x. Der Arduino liefert alle Meßwerte als CSV an den NSLU2 zur Auswertung.
Die Software:
Die ausgelesenen Werte werden in eine RRD Datenbank geschrieben um eine schöne Historie der Messergebnisse zu haben.
Um die RRD-Datenbank anzulegen habe ich folgendes Kommando verwendet:
Um die Grafiken zu erzeugen verwende ich folgende Einträge in der crontab:
/usr/bin/nice -n 19 /usr/bin/rrdtool graph /var/www/cloudtemp2d.png --start "now-1day" -a PNG -l -20 -u 20 -r -Y -t "Bedeckungsgrad (2 Tage)" -X 0 -w 800 -h 300 DEF:cloudtemp=/root/weather.rrd:tempdiff:AVERAGE DEF:cloudtempmin=/root/weather.rrd:tempdiff:MIN DEF:cloudtempmax=/root/weather.rrd:tempdiff:MAX VDEF:cloudtempacta=cloudtemp,LAST VDEF:cloudtempmina=cloudtempmin,MINIMUM VDEF:cloudtempmaxa=cloudtempmax,MAXIMUM LINE1:cloudtemp#FF0000:"Bedeckungsgrad" GPRINT:cloudtempmina:"min %6.2lf" GPRINT:cloudtempacta:"act %6.2lf" GPRINT:cloudtempmaxa:"max %6.2lf" 2>&1 > /dev/null
/usr/bin/nice -n 19 /usr/bin/rrdtool graph /var/www/temperature2d.png --start -172800 -a PNG -Y --vertical-label "[C]" -t "Temperatur (2 Tage)" -X 0 -w 800 -h 300 DEF:temp=/root/weather.rrd:temperature:AVERAGE DEF:tempmin=/root/weather.rrd:temperature:MIN DEF:tempmax=/root/weather.rrd:temperature:MAX VDEF:tempacta=temp,LAST VDEF:tempmina=tempmin,MINIMUM VDEF:tempmaxa=tempmax,MAXIMUM LINE1:temp#FF0000:"Temperatur" GPRINT:tempmina:"min %6.2lf" GPRINT:tempacta:"act %6.2lf" GPRINT:tempmaxa:"max %6.2lf" 2>&1 > /dev/null
/usr/bin/nice -n 19 /usr/bin/rrdtool graph /var/www/humidity2d.png --start -172800 -a PNG -Y --vertical-label "[%]" -t "rel. Feuchte (2 Tage)" -X 0 -w 800 -h 300 DEF:humidity=/root/weather.rrd:humidity:AVERAGE DEF:humiditymin=/root/weather.rrd:humidity:MIN DEF:humiditymax=/root/weather.rrd:humidity:MAX VDEF:humidityacta=humidity,LAST VDEF:humiditymina=humiditymin,MINIMUM VDEF:humiditymaxa=humiditymax,MAXIMUM LINE1:humidity#FF0000:"rel. Feuchte" GPRINT:humiditymina:"min %6.2lf" GPRINT:humidityacta:"act %6.2lf" GPRINT:humiditymaxa:"max %6.2lf" 2>&1 > /dev/null
/usr/bin/nice -n 19 /usr/bin/rrdtool graph /var/www/pressure2d.png --start "now-4days" -a PNG -l 970 -u 1050 -r -Y --vertical-label "[hPa]" -t "Luftdruck (4 Tage)" -X 0 -w 800 -h 300 DEF:pressure=/root/weather.rrd:pressure:AVERAGE DEF:pressuremin=/root/weather.rrd:pressure:MIN DEF:pressurepmax=/root/weather.rrd:pressure:MAX VDEF:pressureacta=pressure,LAST VDEF:pressuremina=pressure,MINIMUM VDEF:pressuremaxa=pressure,MAXIMUM LINE1:pressure#FF0000:"Luftdruck (hPa)" GPRINT:pressuremina:"min %6.2lf" GPRINT:pressureacta:"act %6.2lf" GPRINT:pressuremaxa:"max %6.2lf" 2>&1 > /dev/null
Der Code für den Arduino:
#include <SHT1x.h>
#define TEMP_PIN 0
#define PRESSURE_PIN 1
#define LED_PIN 13
#define dataPin 10
#define clockPin 11
SHT1x sht1x(dataPin, clockPin);
// Höhenkorrektur =EXP(-138/7990) 138 Höhe Aschaffenburg
const float pressure_correction = 0.9828767094;
const int numReadings = 20;
int calValue;
int readings[numReadings];
int readingsPressure[numReadings];
float tempReadings[numReadings];
float humidReadings[numReadings];
int index = 0;
int total = 0;
int totalPressure = 0;
float totalTemp = 0.0;
float totalHumid = 0.0;
float average = 0.0;
float averagePressure = 0.0;
float averageTemp = 0.0;
float averageHumid = 0.0;
//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void setup() {
Serial.begin(9600);
// zero out array
for (int thisReading=0; thisReading<numReadings;++thisReading) {
readings[thisReading] = 0;
readingsPressure[thisReading] = 0;
tempReadings[thisReading] = 0.0;
humidReadings[thisReading] = 0.0;
}
// read calibration value from EEPROM
calValue=EEPROMReadInt(0);
digitalWrite(LED_PIN,HIGH);
}
void loop() {
total = total - readings[index];
totalPressure = totalPressure - readingsPressure[index];
totalTemp = totalTemp - tempReadings[index];
totalHumid = totalHumid - humidReadings[index];
digitalWrite(LED_PIN,LOW);
readings[index] = analogRead(TEMP_PIN);
readingsPressure[index] = analogRead(PRESSURE_PIN);
tempReadings[index]=sht1x.readTemperatureC();
humidReadings[index]=sht1x.readHumidity();
digitalWrite(LED_PIN,HIGH);
total = total + readings[index];
totalPressure = totalPressure + readingsPressure[index];
totalTemp = totalTemp + tempReadings[index];
totalHumid = totalHumid + humidReadings[index];
index++;
if (index >= numReadings) {
index=0;
}
// Float erzwingen
average = (0.0+total) / (0.0+numReadings);
averagePressure = (0.0+totalPressure) / (0.0+numReadings);
averageTemp = totalTemp / numReadings;
averageHumid = totalHumid / numReadings;
if (Serial.available()>0) {
int c=Serial.read();
if (c=='c') {
// calibrate
calValue=total/numReadings;
EEPROMWriteInt(0,calValue);
}
Serial.print(calValue-average);
Serial.print(";");
// Formel stammt aus dem Datenblatt des Drucksensors
float pressure=((averagePressure/1024.0)+0.1518)/(0.001059);
pressure/=pressure_correction;
Serial.print(pressure);
Serial.print(";");
Serial.print(averageTemp);
Serial.print(";");
Serial.println(averageHumid);
}
delay(10);
}
Der Ruby-Code, der die Updates in die RRD-Datenbank schreibt:
require "serialport"
require "RRD"
def dewpoint(temp,humidity)
a=(241.2*Math.log(humidity/100.0)+((4222.03716*temp)/241.2+temp))
b=(17.5042-Math.log(humidity/100.0)-((17.5043*temp)/241.2+temp))
a/b
end
def frostpoint(temp,humidity)
a=(272.186*Math.log(humidity/100.0)+((6107.85384*temp)/(272.186+temp)))
b=(22.4433-Math.log(humidity/100.0)-(22.4433*temp)/(272.186+temp))
a/b
end
sp=SerialPort.new "/dev/ttyUSB0", 9600, 8, 1, SerialPort::NONE
sp.read_timeout=2000
# Ignore first readings
SKIP=10
skipcount=0
while true
sp.putc(' ')
sleep(1)
val=sp.gets
next if !val
val.chomp!
values=val.split(';')
t=Time.now.to_i
if skipcount>SKIP
RRD.update("/root/weather.rrd","#{t}:#{values[0]}:#{values[1]}:#{values[2]}:#{values[3]}")
end
sleep(30)
skipcount+=1
end



Post new comment