PHP: Met Office Weather Upload (WOW)

Overview

This article discusses how to upload collected data from an automatic weather station (AWS) and enable it to be sent to the UK Met Office Weather Observation Website (WOW).

 

Why Do This?

The reason for doing this is that there are no working scripts published on the Internet to post to WOW.

With this in mind, it is hoped this article will give you a script you can use to allow your AWS unit to send data to WOW.

Physical Architecture

The physical architecture of how to connect a weather station to an NSLU2 is described in this article so won’t be repeated here.

Assuming the pre-requisites are in place, we can look at the PHP script.

Software Architecture

The software/script elements are based on the use of PHP script, a shell script and a cron job. The cron job runs the shell script every ten minutes which in turn runs the PHP script. This is the same kind of cron job and shell script as used in this article so won’t be covered again.

However, we are using a slightly different weather station this time ( the Lacrosse Ws2350), but the same logic and architecture applies as the Ultimeter. Note the Ws2350 has now been superceeded by the Ws2355.

Ok, lets take a look at the WOW PHP code that the shell script calls.

PHP Script

The entire PHP script is shown below (we’ll decompose it at the bottom). But if you just cut and paste it in, make sure you:

  1. Put the WOW ID in or your site number in the “FormatData ()” function (replace 999999 with your ID).
  2. Put the authentication key in the “FormatData ()” function (replace 000000 with your key).

Or the data sent to WOW will be rejected.

The script assumes you have output in a local file from a WS2350 automatic weather station, output from the open2300 utility.

  [ | ]
 if ($argc <= 1){
 echo "\r\nNot enough parameters.\r\nUsage: php   [ | ]\r\n";
 exit(1);
 }

 $localfilename = $argv[0];
 if ($argc == 3){
 $option = $argv[1];
 } else {
 $option ="";
 }

 if ($option == "debug") {
 echo "\nArgc=".$argc;
 if ($argv[0]!=""){ echo "\nArgv[0]=".$argv[0]; }
 if ($argv[1]!=""){ echo "\nArgv[1]=".$argv[1]; }
 if ($argv[2]!=""){ echo "\nArgv[2]=".$argv[2]; }
 if ($argv[3]!=""){ echo "\nArgv[3]=".$argv[3]; }
 }

 $handle = fopen($localfilename, "r+");

 if ($handle) {

 //now get any results
 $data = stream_get_line ($handle, 452, "\n");
 fclose($handle);

 // lets split this out ...
 $gWx = explode( " ", $data );

 // We expect in the file a standard ws2350 output using the open2300 toolset .. which looks like ...
 // 20110605100008 2011-Jun-05 10:00:08 79.9 62.8 52.0 44 68 9.6 67.5 ENE 56.5 0.00 0.00 21.90 1012.200 Falling Rainy

 if ($option == "debug") {
 print_r( $gWx );
 }

 // Now as an MetOffice string
 $METO = FormatData($gWx);
 if ( $METO == "" ) {
 echo "\r\nProblem building the URL. See log file.\r\n";
 exit (1);
 }

 //Print out what we got from the file and what URL we've created
 echo "\nSrc=".$data."\nOut=".$METO."\r\n";

 // Send METO data to outside world
 if ($option == "meto") {
 echo "\r\nSending to Met Office\n";
 SendData( $METO );
 }
 } else {
 echo "\r\nUnable to open the local file for weather data";
 }
// End of main body ----------------------------------------------------
//----------------------------------------------------------------------
// This function will post to METO-land and send a report.

function SendData( $data )
{
 global $gWxData;

 $fp = fopen($data, "r");
 if (!$fp) {
 echo "\r\nError of some kind ... check the logs.\r\n";
 } else {
 echo "\r\nSuccessful post to met office WOW =".$output."\r\n";
 fclose($fp);
 }
}
//----------------------------------------------------------------------

/* This function will build a string up from the data collected
Returns an empty string if an error somewhere

http://wow.metoffice.gov.uk/automaticreading?
siteid=XXXXXX
&siteAuthenticationKey=XXXXXX
&dateutc=2011-02-02%2010%3A32%3A55
&winddir=230
&windspeedmph=12
&windgustmph=12
&windgustdir=25
&humidity=90
&dewptf=68.2
&tempf=70
&rainin=0
&dailyrainin=5
&baromin=29.1
&soiltempf=25
&soilmoisture=25
&visibility=25

*/
function FormatData ($WxData)
{
 global $option;

 if ($option == "debug") {
 echo "\nTimestamp=".$WxData[0];
 echo "\nDate=".$WxData[1];
 echo "\nTime=".$WxData[2];
 echo "\nTemp_indoor=".$WxData[3];
 echo "\nTemp_outdoor=".$WxData[4];
 echo "\nDewpoint=".$WxData[5];
 echo "\nRel_humidity_indoor=".$WxData[6];
 echo "\nRel_humidity_outdoor=".$WxData[7];
 echo "\nWind_speed=".$WxData[8];
 echo "\nWind_direction-degrees=".$WxData[9];
 echo "\nWind_direction_text=".$WxData[10];
 echo "\nWind_chill=".$WxData[11];
 echo "\nRain_1h=".$WxData[12];
 echo "\nRain_24h=".$WxData[13];
 echo "\nRain_total=".$WxData[14];
 echo "\nRel_pressure=".$WxData[15];
 echo "\nTendency=".$WxData[16];
 echo "\nForecast=".$WxData[17]."\n";
 }

 // set out output string to empty
 $data ="";

 // start in out timezone so we get the daylioght savings element for checking how old stuff is
 date_default_timezone_set('Europe/London');

 // First things is to check if the data supplied is more than two hours old. If so don't send anything
 $now = strtotime("now"); // ok that's a UNIX time for right now
 // 2011-Jun-05 10:30:07 is $WxData[1] and [2]
 $then = strtotime($WxData[1]." ".$WxData[2]);

 if ($option == "debug" ) {
 echo "\r\n Now=".$now;
 echo "\r\nThen=".$then;
 echo "\r\nDifference=".abs($now-$then)." seconds";
 }

 // check that the report passed is not more than 1 hour old
 if (abs($now-$then) > 3600) { // we've got old data more than 1 hours old so stop here and return nothing
 echo "\r\nFile report too old. Out by ".abs($now-$then)." seconds";
 return "";
 }

 // By now we've checked that the local system time (assumed to be the same as the AWS/2300 timezone / time)
 //  and that the reported time on the filesystem is within an hour of the time of the host .. all is good

 // Now we work in UTC time becuase that's what the Met Office wants
 date_default_timezone_set('UTC');

 //Set our base URL to send to
 $data = "http://wow.metoffice.gov.uk/automaticreading";
 // Add the site ID
 $data = $data . "?siteid=999999"; // your site ID or test site ID (204990)
 // site authentication key
 $data = $data . "&siteAuthenticationKey=000000"; // your site key

 // start to build the time up
 $time = substr( $WxData[0],0,4) . "-" . substr( $WxData[0],4,2) . "-" . substr( $WxData[0],6,2);
 // note we put %20 for the space between date and time
 $time = $time . "%20" . date("H") . "%3A" . date("i") . "%3A" . date("s");

 // 031530 = substr(WxData[0],6,6)
 // &dateutc=2011-02-02+10%3A32%3A55
 $data = $data . "&dateutc=".$time; // time of report of this script running - we have checked against the file report

 $dir = $WxData[9];
 $speed = $WxData[8];

 // Add the two figures onto the output string   
 $data = $data . "&winddir=".round($dir);
 $data = $data . "&windspeedmph=".round($speed);

 // We have not got these so set to empty
 $data = $data . "&windgustmph=";
 $data = $data . "&windgustdir=";

 $data = $data . "&humidity=".$WxData[7];
 $data = $data . "&dewptf=".$WxData[5];
 $data = $data . "&tempf=".$WxData[4];
 $data = $data . "&rainin=".$WxData[12]; // last rain in hour
 $data = $data . "&dailyrainin=".$WxData[13]; // last rain in 24

 // pressure in inches is 0.02953 to the millibar
 // see: http://weatherfaqs.org.uk/node/72
 $pressin = $WxData[15]*0.02953;
 $data = $data . "&baromin=".$pressin;

 // We have not got these so set to empty
 $data = $data . "&soiltempf=";
 $data = $data . "&soilmositure=";
 $data = $data . "&visibility=";

 return $data;
}
//----------------------------------------------------------------------
?>

Right lets take a look at the script.

  • Firstly, we assume the shell script has gathered the data to send to WOW from another application (open2300 in this case). If you can pull your data from a text file or directly from the AWS itself, then this script assumes it’s ready to go in a flat text file.
  • Ok, the first few PHP lines check the parameters passed. We need a data file to look at and whether to “debug” or send to the metoffice. Note that if the command line parameter “debug” is used, we don’t send it to WOW and we print a lot of extra stuff out. This is useful if something’s not right. If the parameter instead is “meto”, we will try and send the parsed data to WOW.
  • After that we set a global variable called $gWx. We use this to send the information to WOW – more on that string and the FormatData() function later.
  • Then we open a file (handle) for the input file we use. This is the data from the AWS in a fixed format, with spaces between each data item. If the file open fails, we simply exit the PHP script – you may want to add something here to alert you.
  • The comments in the script tell you what’s going on, but providing all has gone well, we get all the weather data back with the PHP “$data = stream_get_line ($handle, 452, “\n”);”
  • That grabs 452 bytes or unless a “\n” is read in. That should be all the data we need.
  • We then close the file handle so we can then process it and finally send it to WOW.
  • The first thing after getting the data from the weather station is to carve it up into useful bits. This is done with the syntax “explode (” “, $data);”
  • Providing we have split it up ok (and we assume it’s worked for now as there is no error checking), we build a WOW URL string (discussed in a bit), and then send it to the standard output (screen).
  • We then check to see if we had a parameter passed to the PHP script. If a string was passed and was “meto”, then we will try to send the weather data to the Internet. If not, then we’ll do nothing – obviously you can add something here if you want to send it somewhere else.
  • If we were passed “meto”, send a message to the standard output followed by a call to the function “SendData( $gWx );” This last function we’ll also cover in a bit.
  • Now providing at all worked, by now, the weather station data should be with WOW on the Internet.

Overview of Functions

The following describe a few of the important functions.

function SendData( $data )

This is the key PHP function that sends the formatted data to WOW. Very simply, it opens a file handle to the WOW main server using the full URL passed into it. If all is well, it should be successful. We should do some extra checking here (e.g. if we get a 404) – but you can add it in.

function FormatData ()

This function builds a string that it returns to the caller based on the WOW format rules. The function gives an overview as a comment at the start, so you get an idea what it should look like.

Each WOW URL parameter is built up into the return string $data. Ok, let’s look at the rest of the function.

The start is some debugging code if “debug” mode was called.

Then we set the PHP timezone to local (London/Europe in this case). This is becuase we need to verify the time of the submission is fairly close to the system time of the host (NSLU2). If more than an hour old, don’t send it to WOW as it must have stopped or there is another problem.

If all is well time-wise, reset the local time to UTC as WOW likes UTC. If you forget this you could end up trying to send a report one hour into the future and WOW doesn’t like this.

First of all, add your site ID and authentication key – these are specific to you. Then add the time and date. Then add wind speed and direction. Note if your AWS supports more parameters, add these parameters in based on the rules set on the support pages of WOW. Included here are some empty parameters as the AWS used here doesn’t support them. Then add rain, humidity and temperatures. Lastly add the barometric pressure in inches. Note that at the time of writing, WOW doesn’t allow millibar uploads – so a conversion is done here from millibar to inches of mercury. With the last parameter done, return the fully built URL to the function caller.

And that’s it. The script should just run as shown above, but note there isn’t a lot of error handling as the script has been found to be reliable without change.

If you want to test this script out with no weather station, follow the following steps. Firstly, put this line of text into a flat file:

20110605100008 2011-Jun-05 10:00:08 79.9 62.8 52.0 44 68 9.6 67.5 ENE 56.5 0.00 0.00 21.90 1012.200 Falling Rainy

Secondly, paste the code below into another file. Thirdly, run them together as:

php [your-script-name] [your-weather-data-file] debug

And hopefully you’ll get some output showing the script working.

Suggestions for Generic Linux Usage

It is possible to use the above on any linux system that has PHP, a cron daemon and both a serial port to the weather station and online access to the Internet. Try the script out and see how you go (but make sure you set the WOW ID and authentication key).

Links and Useful Pointers

The following gives locations of useful information, used during the development of this application.