The missus got a weather station for her birthday. I mounted it on a convenient sturdy post at the end of the garden, and it started gathering data.

Weather Station

(That fence is on a bit of a slope, and the post is wonky, but thanks to some carefully engineered wedges the weather station is level.)

Now, there's not much point gathering data if you don't upload it somewhere. It's a pretty heavy duty unit (a Davis Vantage Pro2), so I expected it to be easy to interface to a PC. Surprisingly, it doesn't come with that ability out of the box - you have to buy a little dongle and some software for 290 NZD, or, if you want it to upload its results automatically without a PC, 500 NZD. That seemed on the outrageous side to me, so I went and googled for cheap alternatives.

It didn't take long to find this blog post from someone who'd worked out how to interface to the unit without the dongle. It uses a 3.3v serial connection, which you can get pretty cheaply - but that's still not cheap enough for me, and still requires a PC always on to upload the data to the web.

Luckily, I had an old Linksys WRT54G sitting around. It's a classic hacker-friendly device, very easy to install linux on, and, most usefully, has a connector inside that handles serial at 3.3v. It's been sitting in my study acting as a dumb switch since it was superseded by the AirPort. Time to dust it off and give it some gainful employment.

I installed OpenWRT on it, drilled a hole in its case, soldered some wires in, and hooked it up to the Davis console. Fired up minicom, typed some commands, and - hey presto! - it worked. I spent some time happily turning the console's backlight on and off. This was too easy!


Jony Ive eat your heart out.

Well yes. As it turned out, it was too easy. Getting from this stage to having something which would automatically update a website with the results turned out to be quite a slog. Still, I did it so that you don't have to!

My original notion was to install the wview daemon on the router. This handles all sorts of weather stations, serves up its own web pages, and can forward the results to a variety of other weather sites. But to build wview, I first had to build the OpenWRT toolchain, and stuff the firmware full of the relevant dependencies. After a couple of brickings (including one that I could only recover by jamming a fork in the router), I realised that I simply wasn't going to get something the size of wview crammed into the 4MB of flash available to me.

Next I looked to the various Perl scripts that people have written for communicating with their weather stations. But it turned out that I couldn't even fit Perl into the firmware. It was clear that I had to look at the problem from the other side - see what was available on the router, then try to use that. As it turned out, Lua was already included, so that's what I had to use.

Armed with the documentation for the Davis serial protocol and the upload protocol for wunderground (which we'd decided on as a Good Site to host the data), I set about writing my own solution.

The next problem turned out to be communicating over the serial connection. It had been straightforward using minicom, but accessing /dev/ttyS1 from within Lua gave very inconsistent results. It turns out that by default, Unix still assumes that a serial port exists so that dumb terminals can connect to it. When it sees data coming in, it tries to respond to it on that assumption. So the Davis console would start sending its data, and the router would immediately start responding "OH HAI HOW ARE YOU TODAY", which would cause the console to cancel in confusion.

I built a new version of the firmware with stty enabled, and that let me apply these intuitive settings:

> stty -F /dev/ttyS1 -a
speed 19200 baud;stty: /dev/ttyS1
 line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

The final hurdle was that Lua doesn't seem to offer a non-blocking read or a way to spawn new threads, and I was worried that the script could too easily get hung up waiting for data from the console. My solution was a little clunky, but should be robust; I have two separate scripts - one periodically sending a request for more data, and the other constantly reading the serial port looking for anything it recognises as a valid packet.

The request script is incredibly simple - it sends a carriage return to wake up the console, waits a second, then requests a single LOOP packet:

echo > /dev/ttyS1
sleep 1s
echo LOOP 1 > /dev/ttyS1

and an entry in /etc/crontab to run it every 5 minutes:

*/5 * * * * /root/ > /dev/null

The receiving script has a bit more work to do. It looks for the start of a loop packet ("LOO..."), pulls in enough data for a whole packet, then checks that it has a valid CRC. If so, it picks out the relevant data and sends it on to wunderground. It should err on the side of quietly dropping data if there's a problem, rather than sending bad data to the web.

davisrcv.lua (If you use it, you'll have to edit the script to add your own station id and password)

All that's left is to kick it off on startup, by adding the following to /etc/rc.local:

stty -F /dev/ttyS1 0:0:cbe:0:3:1c:7f:15:1:0:0:0:11:13:1a:0:12:f:17:16:4:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
lua /root/davisrcv.lua &

And here are the results!