After a long time without any sort of attention, the weathoscope has had a bit of an overhaul. A few months ago I rewrote the web UI, mostly just to use jQuery and flot rather than the Google charts API. The BMP085 pressure sensor breakout board I was playing with back in March has been integrated. Here it is on the breadboard:
And here it is wrapped in some heat shrink tubing for protection. There's a square hole cut in the tubing with a scalpel to expose the actual sensor. (The white stuff is the remnants of a piece of sticky foam pad; I forgot to take a picture before sticking it down...)
Although the BMP085 is a pressure sensor it also provides a digital temperature output - as this is factory calibrated it has replaced the readings I was previously getting using a LM335Z. The LM335Z is still connected and logged, I just don't use the values in the front end.
The anemometer is the device I first made back in December 2010. It's only recently come into service with some cups taken from a basic weather station kit - it originally had some home made blades but these were just rubbish; they didn't really turn easily and fell apart in the end.
Inside the blue box, at the other end of the axle is a cog with a single hole, an infrared LED and light dependant resistor. The LDR is connected to one of the arduino's interrupt pins and gets triggered when the hole is between the LED and the LDR. I.e. once per revolution. Using the radius of the cups and the count of the revolutions over a known time period we can calculate the wind speed.
Software
The software stack comprises of the arduino code that deals with the individual sensors, then outputs a set of key value pairs on the uart (once per minute). This is read by a perl script running on the host PC that parses and inserts the readings into a postgres database. A recent amendment makes this script pushes the data up to OpenWeatherMap too.
On the front end web server there's a small PHP script that acts as a simple RPC wrapper around the DB. It'll take one of the date ranges (day, week, month etc) and output a JSON-ified object ready for feeding into flot. Finally there's a bit of jQuery combined with flot to draw the graphs. APC is used to cache the results from the DB to avoid hitting the DB too often.
The latest code needs a bit of a clean up before I can release it; hopefully get it posted in the next week or so.
Plans
Current plans include extending the system with a humidity sensor, replace the light sensor that has corroded away and maybe once the 3D printer arrives, build a tipping bucket rain gauge.
Web UI is here. To see the other weathoscope related posts click the 'weathoscope' tag at the bottom of the page (or click here).
Just a breadboard arduino and a bmp085 pressure sensor - nothing spectacular just connected via a level shifter to the I2C pins but it did work first time :)
And here's a screen grab of the output from the test code I found here.
I've just pushed the latest server side code changes up to github. The biggest change is that the persistent storage has been moved to a postgres database instance to allow efficient retrieval based on date ranges. The database schema is just one table with two columns - a timestamp and the temperature.
The background perl script - weathoscope_logger_db.pl - now uses the DBI perl module and simply inserts the values into the database as they are read from the arduino.
On the web front end, the php script - index.php - runs a small SQL query to extract the latest captured temperature :
SELECT temp FROM log ORDER BY ts DESC LIMIT 1
It also outputs a couple of image tags linking to a third php script - temp_chart_db.php - which handles the dirty job of taking the data returned from the DB and formatting it into something the google charts api can work with. It can take an optional parameter that changes the date range from 1 day to 1 week. I plan to make longer ranged graphs available as soon as I have the data (within the bounds of reasonable query response times anyway).
There been some strange spikes on the graphs over the past week - I suspect the probe need repositioning further away from the house and the on-device smoothing probably needs it's number of samples increasing.
Below is a picture of the weathoscope mounted on the inside of the shed. You can see the USB cable on the right and the black/yellow cable of the temperature probe on the left. The probe cable runs out through the gap beween the wall and the roof, the probe itself hanging in the air.
I'm hoping that hanging there will be both out of direct sunlight and out of the rain.
Here's the first version of the weathoscope. This initial version only supports temperature but I'll add sensors as they and time become available. It's now installed in the shed with the LM335Z sticking outside sampling the air in the back garden.
And with the lid on:
The code running on it (below) dumps the current values of the sensors on a single line consisting of key/value pairs, to the serial port every 60 seconds.
#include "stdlib.h"
#include "math.h"
#include "wiring.h"
#include "WProgram.h"
#include "Wire.h"
#include <Smoothing.h>
#include <Timer.h>
//status LED
const int LEDPIN = 13;
//rotorary encoder photocell - wind speed
/* NOT USED
const int PHOTOPIN = 0;
volatile int state = LOW;
volatile int counter = 0;
*/
//LM335Z - temperature
const int LM335RATE = 1000; // 1 second
const int LM335PIN = 0;
Timer LM335Timer;
Smoothed LM335Smooth;
int LM335toDegreesC(int raw, int fudge){
//Algorithm to convert the LM335 output signal from the ADC to degrees C.
return (((raw * 500L) / 1023L) - 273L) + fudge;
}
//dump data timer
Timer dumpTimer;
const int DUMPRATE = 60000; //60 seconds
void setup(){
Serial.begin(9600);
Serial.println("setup");
digitalWrite(LEDPIN, LOW);
/* NOT USED
pinMode(PHOTOPIN, INPUT);
attachInterrupt(PHOTOPIN, windSpeedISR, FALLING);
*/
newSmoothed(&LM335Smooth, 10);
newTimer(&LM335Timer, LM335RATE);
newTimer(&dumpTimer, DUMPRATE);
delay(1000);
}
void windSpeedISR()
{
/*
state = !state;
counter++;
*/
}
int smoothedLM335 = 0;
void loop(){
if( checkTimer(&LM335Timer) ){
int rawLM335 = analogRead(LM335PIN);
smoothedLM335 = smoothReading(&LM335Smooth, rawLM335);
}
if( checkTimer(&dumpTimer) ){
digitalWrite(LEDPIN, HIGH);
int degreesC = LM335toDegreesC(smoothedLM335, -4);
Serial.print("degreesC:");
Serial.print(degreesC);
Serial.print('/'); // divider
Serial.println();
digitalWrite(LEDPIN, LOW);
}
}
On the web server a perl script reads the line from the USB serial port and writes out two files. First it writes the latest line to weathoscope.out - this file can then be easily picked up by a PHP script for displaying the live data. Secondly it parses it into it's key/value pairs building a simple CSV line to be appended to weathoscope.csv. The CSV file can later be used for historical analysis and graphing. Both these files are available on the public part of the web server for potential re-use.
#!/usr/bin/perl
use Device::SerialPort;
use DateTime;
use Data::Dumper;
$LOGDIR = "/var/www/htdocs";
#$LOGDIR = "/tmp";
$LOGFILE = "weathoscope.out";
$CSVFILE = "weathoscope.csv";
$PORT = "/dev/ttyU0";
#$PORT = "/dev/ttyUSB1";
my $port = Device::SerialPort->new($PORT);
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);
open(LOG,">${LOGDIR}/${LOGFILE}") || die "can't open log file\n";
open(CSV,">>${LOGDIR}/${CSVFILE}") || die "can't open csv file\n";
while (1) {
my $data = $port->lookfor();
if ($data) {
#print "$data\n";
#write latest to LOG
truncate(LOG, 0);
seek(LOG, 0, 0);
syswrite LOG, $data;
#now decode for the CSV
my $dt = DateTime->now();
#looking for these
my $degreesC = "";
$data =~ s/\r|\n//g;
my @keyvals = split(/\//, $data);
foreach(@keyvals){
my @keyval = split(/:/, $_);
#degreesC
if(@keyval[0] =~ /degreesC/){
$degreesC = @keyval[1];
}
#blahblah
#if(@keyval[0] =~ /blahblah/){
# $blahblah = @keyval[1];
#}
}
#write csv
my $csv = "$dt, $degreesC\n";
syswrite CSV, $csv;
} else {
sleep(1);
}
}
The lines outputed from the arduino are in the following format, which you can see just need to be split by a forward slash to get an array of key/value pairs and then each of these to be split by a colon to get the key and value.
key1:value1/key2:value2/key3:value3
The nicely presented data can be found here. The PHP script opens the .out file and double-splits the line, parsing the value(s) it needs. It outputs a 'standard' sodnpoo.com blog XML file that automatically gets transformed and styled.
I'm planning on adding a humidity sensor and my homemade wind speed meter next. Although I have plans for a laser rainfall meter and barometer eventually.
I've started building an arduino powered weather monitoring station. The plan is to have sensors for the various different aspects (wind speed, temperature etc) so I can publish the live data and keep a log that can be used for later analysis.
For temperature measurement I'm using the LM335Z which can cover a range of -40 to +100 degrees centigrade. It needs one analog pin which after some conversion gives you the temperature in Kelvin.
Below you can see I'm only using two of the 335's legs - v+ and v-. V+ is pulled high on one side via a 1K resistor and on the other is connected to an arduino analog pin. V- is connected to ground.
I use the function below where raw is the raw value returned from analogRead() and fudge is how much to offset the result by after calibration. (I used ice in a bag.) The '-273' is there to convert from Kelvin to degrees centigrade.
int LM335toDegreesC(int raw, int fudge){
return (((raw * 500L) / 1023L) - 273L) + fudge;
}
To measure wind speed I needed to be able to count the rotations of some sort of propeller in a given time period. This is the device I came up with to measure the rotations:
The device uses an infrared LED and photocell taken from a broken PS2 ball mouse. The output from the photocell is connected to an arduino interrupt pin.
The vertical axle and cog are taken from a broken small toy helicopter. The bottom of the cog has been covered with black tape except for a single window that exposes the photocell to the IR from the LED once per rotation.
Below is a close up where you can see the clear LED at the bottom, the black photocell at the top and the window in the cog.
I still need to make the propeller - which will most likely be two cups on the end of short arms connected to the axle head - and to acquire a suitable housing for it that can be easily mounted.