Shed Weather: a Journey Into Questionable Purpose

I’ve been continuing to fiddle around with my Arduino over the last few weeks, trying out various sensors and screens. One breakout board on the online store I’ve been using caught my eye, the delightfully named BMP180 Barometric Pressure / Temperature / Altitude sensor. I’d very nearly taken the plunge a few months ago with one of these, so I thought it would be an interesting project to try and build something along the same lines: install an Arduino in the shed [which has a power supply] and somehow or other get the temperature back to the Raspberry Pi. Some quick googling led me to here. The author of that Instructable article has done a very nice job, which I’ve borrowed the principle of: using two Arduinos, and a 433MHz RF link between them to transmit the data. What I wanted to add into the mix was an iPhone app, which necessitates a server component. That factor is why I’m not going to list all the code here: having a hosting service comes with a monthly bill [and a lot of other capability], which I think will very much limit the broader applicability. But there are a few points that I encountered, which I think are worth going into some detail.

First, the shopping list:

  • 2 x Arduino Uno boards. [£22 each].
  • As well as the barometric pressure board [£8], also using an HTU21D humidity sensor.
  • Aforementioned RF Link kit: [£2].
  • 2 x wire coat hangers, more of which later. Not sure how much these cost, but they tend to come with free clothes.
  • 1 plastic takeaway food container for the shed based components to sit in.

There were some other incidentals in the mix such as breadboards and jumper wires; I also repurposed an old iPhone charger for the outdoor sensor.

The pattern I decided to take was pretty simple: I’d connect the sensors to the first Arduino which is outside [well, inside the shed] and send the results in a continuous loop [every second] over the RF link, to be picked up by the receiver. I’d then run some code on the Pi, picking the data off the serial port, which I’d then send to a database server.

Getting the RF Link to work, using the VirtualWire library, was pretty straightforward. The hard bit is getting it to work if the transmitters were more than a few centimetres apart – note the picture in the Instructables article’s final page, and how far apart the two components are [i.e., not very!].

I spent a lot of time on this. I really didn’t want to have to run a second power adaptor to crank up the DC input to 12V, which is the maximum that the RF transmitter can handle [obviously this would be a direct connection, not the toast-making approach of trying to run 12V through the Arduino]. I ended up making a Yagi antenna, which I have to admit was a term I’d never come across before yesterday. If you google for some combination of terms for home made antennas for 433Mhz transmitters, you will invariably come across the same image, which is half way down this bulletin board. I followed the instructions, using the wire coat hangers, and it works. I have one end of the ‘live’ parts of the antenna connecting to the transmitter, and the other connecting to ground. I also used a dirty little hack which I saw somewhere else: I connect both the 3.3V and the 5V outputs of the Arduino to the input of the transmitter – note that this on its own wasn’t enough to cover the distance I need with the V1 antenna [17cm of wire], something like around 25 feet.

Yagi Antenna [post feline damage]So I was chuffed with the result, as without it, the project would have been a right off. One unintended consequence: within an hour of putting the thing in the garden, the cat promptly sat on it and broke off one of the elements. It still works. I have the cable running under the door of the shed, and used a glue gun to try to make sure that all of the connections were waterproof.

One slight problem I encountered was that the signal wouldn’t penetrate through to the place I originally intended having the receiver and Raspberry Pi: the corner of the house is in the way, so I had to relocate the Pi to a spare bedroom. This meant that my lovely little TFT screen is now redundant.

I messed around with the code quite a bit, and it wasn’t without its complexity. I’ve written before about coding in Perl to read off the serial port, and the approach that I had been taking was to embed a call to the serialPy Python library. I’ve discarded this and now use the Device::SerialPort library which is actually pretty elegant. My original approach to this was to run the Perl as a process, and then check that the process was still running with a cron job. This was based on some prototyping when I was taking readings from the sensors on a directly connected Arduino. This is much more reliable, so I was just sending the data from the sensor at the intervals that I wanted – hourly. As I mentioned earlier, I am now sending from the external board every second. I’ve refactored the code to be called from cron, which runs once and then fires the data up to the database. Note that I currently assume that the script will complete within the hourly cycle. I might as well include the Perl actually, as it introduces the database component:

#!/usr/bin/perl

# post to Arrest-MySQL via curl looks something like:
# curl --data "tempC=77.4&time=2014-01-22 19:02:01" http://yoursite.com/Arrest-MySQL_installdir/tablename

use Device::SerialPort;
use DateTime;

my $port = Device::SerialPort->new("/dev/ttyACM0");
$port->databits(8);
$port->baudrate(9600);
$port->parity("none");
$port->stopbits(1);

$gotAllThree = 0;

$gotTemp = 0;
$gotHumidity = 0;
$gotPress = 0;

while($gotAllThree == 0) 
{
   $lastEpoch = time();
   my $char = $port -> lookfor();
   $char =~ s/[^!-~\s]//g;
   # print $char;
   if ($char =~ /T:/)
   {
      $dt2 = DateTime->now(time_zone=>'local');
      $ymd = $dt2->ymd;
      $timeString = $dt2->hms(':');
      $timestamp = $ymd . " " . $timeString ;
      $char =~ s/Received: T: //;
      print "In temp:\n";
      if ($gotTemp == 0)
      {
         print "T: curling\n";
         my $server_endpoint = "http://yoursite.com/Arrest-MySQL_installdir/tablename";
         system("curl --data \"tempC=$char&time=$timestamp\" $server_endpoint");
         $gotTemp  = 1;
      }
   }
   if ($char =~ /H:/)
   {
      # print "got humidity: $char\n";
      $dt2 = DateTime->now(time_zone=>'local');
      $ymd = $dt2->ymd;
      $timeString = $dt2->hms(':');
      $timestamp = $ymd . " " . $timeString ;
      $char =~ s/Received: H: //;
      print "In Humidity\n";  
      if ($gotHumidity == 0)
      {
         print "H: curling\n";
         my $server_endpoint = "http://yoursite.com/Arrest-MySQL_installdir/tablename";
         system("curl --data \"humidity=$char&time=$timestamp\" $server_endpoint");
         $gotHumidity = 1;
      }
   }
   if ($char =~ /P:/)
   {
      # print "got pressure: $char\n";
      $dt2 = DateTime->now(time_zone=>'local');
      $ymd = $dt2->ymd;
      $timeString = $dt2->hms(':');
      $timestamp = $ymd . " " . $timeString ;
      # print "pressure time stamp is $timestamp\n";
      $char =~ s/Received: P: //;
      # last char is a dot?
      $char =~ s/\..*//;
      # print "pressure is now $char\n";
      print "In Pressure\n";  
      if ($gotPress == 0)
      {
         print "P: curling\n";
         my $server_endpoint = "http://yoursite.com/Arrest-MySQL_installdir/tablename";
         system("curl --data \"millBars=$char&time=$timestamp\" $server_endpoint"); 
         $gotPress = 1;
      }
   }
   if (($gotPress == 1) && ($gotHumidity == 1) && ($gotTemp == 1))
   {
      print "we got at least one hit\n";
      $gotAllThree = 1;
   }

My original intention was to use an Amazon RDS instance, as this is free for a year which, if I’m honest, will be long after I’ll probably be bored with this set up and have pulled it apart. Using the whole AWS interface is kind of what I imagine having a quick go at piloting an aircraft carrier would be like: you tend to wonder what all of the buttons do. To put it another way, it’s very much geared up for an enterprise approach. All that said, I got it working [once I’d figured out how to disable the default and admirably paranoid firewall settings that prevent any external connections whatsoever]. Three days later I got my first bill, which was for a couple of bucks, for storage usage I think. From what I can see, the first year is free provided that you don’t actually use it.

I had a fallback option, which was the hosting service I use for this blog, and which allows me up to 3 mySQL installs, including the one that is used for WordPress. The only downside, and the reason it was second choice, is that there is no shell access: all of the database setup is via a web interface [phpMyAdmin].

I’d already realised from some early research that I needed to transform the data into JSON somehow or other. Googling ‘iOS SQL client’ quickly pointed me away from a native interface. What I finally settled on was a thing called Arrest MySQL. Note to self: the link I use for this is buried, but I need to add a .htaccess file to at least have a fig leaf of security. By default, simply posting to the interface will insert data into the table, if you know where to look.

So it’s working, and I am reliably informed that the outside [well, ‘shed’] temperature was 7.43 degrees Celcius, pressure was 988 Mb and humidity 68% the last time the cron job fired off. The iOS client is well under way too. It’s back to the coalface that is Core Plot for the primary functionality.

I’ve learned a lot from this, but end to end it’s something of a plate spinning exercise, however. The RF link obviously isn’t that reliable. I’ve also not really used a database before, and am having to figure out how to trim the data as I go, using events. It also makes sense to do other transformations [say turning the 24 hourly values into a daily average] server side as well.

But I think the reliability issue is what may make me strip it down and move onto the next Arduino-driven bauble that catches my interest :).