Friday, February 6, 2015

Log Data to the Weather Underground

The next topic for my weather station system is how to send your data to a "Personal Weather Station" on the Weather Underground web site. This provides a nice interface for the world to see your current (and past) conditions.

The PWS Overview explains what personal weather stations are. It also has a buying guide. For comparison, the total cost for my system was somewhere around $150 (That's just a very rough guess.)

See PWS Upload Protocol for the complete documentation for uploading data for a PWS. To summarize, you need to send a properly formatted string to a web address (a simple HTTP GET to a PHP script.)

So here is a simplified example of the code that I am using.

/***********************************************************************
   Filename:   wunderground.c
   send current conditions to my Personal Weather Station on 
   The Weather Underground
   
   Uses libcurl to send the data via HTTP

   build with:
   gcc -o wutest wunderground.c -L/usr/local/lib -L/usr/local/ -lcurl -lwiringPi

  23-Jan-2015   Ted Hale  created this as an example for blog

************************************************************************/

/* system includes */
#include <stdio.h>
#include <stdlib.h>  
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <curl/curl.h>

#define TRUE 1
#define FALSE 0

// these are normally global varables that my weather system updates
// just put them here for this example
float outsideTemp;
float windSpeed;
float windGust;
float rainPeriod;
float humidity;
float barometric;

// my PWS ID and password
char *myStationID = "KVAWILLI99";
char *myStationPassword = "NOTMYPASSWORD";

// structure used by the libcurl write callback function
struct url_data {
    size_t size;
    char* data;
};
 
//=====================================================================
// write callback function needed by libCurl
size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data) {
    size_t index = data->size;
    size_t n = (size * nmemb);
    char* tmp;

    data->size += (size * nmemb);
    tmp = realloc(data->data, data->size + 1); /* +1 for null terminator */

    if(tmp) {
        data->data = tmp;
    } else {
        if(data->data) {
            free(data->data);
        }
        printf("skynet_post Failed to allocate memory.\n");
        return 0;
    }

    memcpy((data->data + index), ptr, n);
    data->data[data->size] = '\0';

    return size * nmemb;
}

//=====================================================================
// upload current conditions to Weather Underground
int UpdateWunderground()
{
 // URL format 
 char *myFmt = "http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?"
     //   1           2           3    4    5    6       7       8  
  "ID=%s&PASSWORD=%s&dateutc=%04d-%02d-%02d+%02d%%3A%02d%%3A%02d"
  //              9             10       11        12
  "&windspeedmph=%f&windgustmph=%f&tempf=%f&rainin=%f"
  //        13          14
  "&baromin=%f&humidity=%f&action=updateraw";

 /* 1 ID
  * 2 passwd
  * 3 yr
  * 4 mon
  * 5 day
  * 6 hr
  * 7 min
  * 8 sec
  * 9 winspeed
  * 10 gusts
  * 11 Outdoor temp
  * 12 rain
  * 13 baro 
  * 14 humidity
  */

 int   error = TRUE;
 time_t   now;
 struct tm  *dt;
 int   hour,minute,second,year,month,day;
 char   url[1024];
 
 CURL  *curl;
 CURLcode res;
 struct url_data response;
 
 time(&now);
 dt = gmtime(&now);
                         
 // build the URL string
 //                                      1              2
 snprintf(url, sizeof(url)-1, myFmt, myStationID, myStationPassword,
 //         3          4          5           6           7          8
  dt->tm_year, dt->tm_mon, dt->tm_mday, dt->tm_hour, dt->tm_min, dt->tm_sec,
 //    9           10         11        12             13        14
  windSpeed, windGust, outsideTemp, rainPeriod, barometric, humidity);

  // guarantee null termination of string
 url[sizeof(url)-1] = 0;
  
 curl = curl_easy_init();
 if (curl) {
  response.size = 0;
  response.data = malloc(4096); /* reasonable size initial buffer */ 
  response.data[0] = '\0';
  curl_easy_setopt(curl, CURLOPT_URL, url);
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
  res = curl_easy_perform(curl);
  if(res != CURLE_OK)
  {
   printf("curl_easy_perform() failed: %s\n",curl_easy_strerror(res));
   error = TRUE;
  } else {
   error = (strcmp(response.data,"Success") != 0);
  }
  curl_easy_cleanup(curl); 
  free (response.data);
 } else {
  printf("curl_easy_init failed\n");
  error = TRUE;
 }
 
 return error;
}

int main()
{
 // set some dummy data
 outsideTemp = 69.7;
 windSpeed = 2.3;
 windGust = 5.6;
 rainPeriod = 0.001;
 humidity = 75.0;
 barometric = 31.5;

 UpdateWunderground();
 
 return 0;
}

Please let me know if you use this code.  I would like to hear how it works for you.

No comments:

Post a Comment