Our full technical support staff does not monitor this forum. If you need assistance from a member of our staff, please submit your question from the Ask a Question page.


Log in or register to post/reply in the forum.

CR10X Upload to Weather Underground (etc)


wpns Feb 2, 2009 01:41 AM

I've got some Perl code to upload from a CR10X to Weather Underground using an NL100 and a Linux box if anyone is interested. It's a little ugly, but the price is right. 8*)

Perl is pretty standard, so it ought to work on Windows or OSX if anyone wants to make the appropriate modifications.

I'm running Centos, not that it should matter a lot. I've got a crontab entry that looks like:

* * * * * /usr/bin/perl /home/Wanda/wanda.pl >> /home/Wanda/wanda.log

(Wanda The Weather Woman is the COM300 voice modem that reads out the weather report when you call the attached phone).

This code also uploads to CWOP, keeps track of max/min on interesting values, sends a daily report and "interesting phenomena" Emails, etc.

To use this code, you'll have to fiddle a bit, as your setup won't be the same as mine, feel free to ask here if you have any questions about how various bits of this code work, I've been adding and refining quite a bit along the way.

Weather Underground page I'm updating is at:
http://www.wunderground.com/cgi-bin/findweather/getForecast?query=pic


Enjoy!


#!/usr/bin/perl -w
#
# wanda.pl - get weather data from Wanda and upload to Weather Underground
#
# rev 5.1 fix CSI Float conversion for real, copy from Watson 02/01/09 WPNS

use strict;
use Socket;
use XML::Simple;
use Data::Dumper;
use Mail::Sendmail;
use LWP;
use Net::FTP;

require "/home/Wanda/SimpleGet.pl";

my $RevString = "Wanda.pl_rev_5.1_02/01/09_WPNS";


use constant WakeUpCount => 2; # how many times to send CR to wake up
use constant WakeUpDelay => 1; # how many seconds to wait per wakeup count
use constant ReplyDelay => 1; # how many seconds to wait after transmitting
use constant FALSE => 0; # dunno why Perl doesn't define these
use constant TRUE => !FALSE;

$| = 1; # turn on autoflush for sane output

my ($sec,$min,$hour,$mday,$mon,$year,
$wday,$yday,$isdst) = localtime time;

printf("\n-----------------\n%02i:%02i %s\n",$hour,$min,$RevString);

#print $LWP::VERSION;

# read memory from XML file
my $Wanda_Mem = XMLin();
#print Dumper($Wanda_Mem);
$Wanda_Mem->{RevString}=$RevString;

# initialize host and port
my $port = 8097;
my $server = "";
my $packed_ip = gethostbyname("wanda.geekho.com");
if (defined $packed_ip)
{
$server = inet_ntoa($packed_ip);
printf("Wanda IP: %s ",$server);
}
else {die "Wanda DNS Lookup Failed!\n";}

my $watson_port = 8094;
my $watson_server = "";
my $watson_packed_ip = gethostbyname("watson.geekho.com");
if (defined $watson_packed_ip)
{
$watson_server = inet_ntoa($watson_packed_ip);
printf("Watson IP: %s ",$watson_server);
}
else {die "Watson DNS Lookup Failed!\n";}

my $cwop_port = 14580;
#my $cwop_port = 23;
my $cwop_server = "";
my $cwop_packed_ip = gethostbyname("cwop.aprs.net");
if (defined $cwop_packed_ip)
{
$cwop_server = inet_ntoa($cwop_packed_ip);
printf("CWOP IP: %s\n",$cwop_server);
}
else {die "CWOP DNS Lookup Failed!\n";}

my $line;
my $loop;

printf ("Socket ");
# create the socket, connect to the port
socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2])
or die "Can't create a socket $!\n";
printf ("created ");
connect( SOCKET, sockaddr_in($port, inet_aton($server)))
or die "Can't connect to port $port! \n";
printf ("opened ");
setsockopt (SOCKET, SOL_SOCKET, SO_REUSEADDR, 1);
printf ("set! ");

printf ("Transmitting");
for ($loop=0;$loop<WakeUpCount;$loop++)
{
send (SOCKET, "\r",0);
printf(".");
sleep (1);
}
printf("Done! ");

printf ("Rx delay");
for ($loop=0;$loop<(WakeUpDelay*WakeUpCount);$loop++)
{
printf(".");
sleep (1);
}
printf ("Done!\n");
recv (SOCKET,$line,512,0);
printf ("%i bytes:$line\n",length($line));

if (length($line) != (3*WakeUpCount))
{
printf("Bad WakeUp!\n");
die "Bad wakeup reponse!\n";
}

# send unlock
printf("Unlock...");
send (SOCKET, "1234L\r",0);
sleep(ReplyDelay);
recv (SOCKET,$line,512,0);
printf ("$line\n");

# select FS A
sleep(ReplyDelay);
printf("FSA...");
send (SOCKET, "1A\r",0);
sleep(ReplyDelay);
recv (SOCKET,$line,512,0);
printf ("$line\n");

# send "J" command, to select output for "K"
printf("Select...");
send (SOCKET, "2413J\r",0);
sleep(ReplyDelay);
recv (SOCKET,$line,512,0);
printf ("$line\n");

# send "J" configuration
printf("Configure...");
send (SOCKET, "\x00\x40\x00\x03\x05\x06\x07\x09\x0a\x0d\x2e\x33\x01\x29\x00",0);
sleep(ReplyDelay);
recv (SOCKET,$line,512,0);
#printf ("$line\n");
printf("Got %i bytes: ",length($line));
for ($loop=0;$loop<length($line);$loop++)
{
printf("%02x ",ord(substr($line,$loop,1)));
}
printf("\n");

# dump using "K"
printf("Dump...");
send (SOCKET, "K\r",0);
sleep(ReplyDelay);
recv (SOCKET,$line,512,0);
#printf ("$line\n");
printf("Got %i bytes: ",length($line));
for ($loop=0;$loop<length($line);$loop++)
{
printf("%02x ",ord(substr($line,$loop,1)));
}
printf("\n");

if (length($line) != 57)
{
printf("Error - line length not 57!\n");
die "Dump Line Length!\n";
}
if (substr($line,0,3) ne "K\r\n")
{
printf("Error - header incorrect!\n");
die "K Header!\n";
}
if (substr($line,53,2) ne "\x7f\x00")
{
printf("Error - wrong terminator!\n");
die "K Terminator!\n";
}

printf("-------------------------\n");

# pull out the time stamp
my $minutes = ord(substr($line,3,1))*256 + ord(substr($line,4,1));

# split them out into pieces we care about
my $SlrW = CSIFPF($line,9);
my $AirTF = CSIFPF($line,13);
my $RH = CSIFPF($line,17);
my $BAR_inHg = CSIFPF($line,21);
my $WS_mph = CSIFPF($line,25);
my $WindDir = CSIFPF($line,29);
my $DewPoint = CSIFPF($line,33);
my $Rain_Day = CSIFPF($line,37);
# now ROUND to hundredths of an inch
$Rain_Day = (int(($Rain_Day*100)+0.5)/100);
my $WS_Max = CSIFPF($line,41);
my $Vcc = CSIFPF($line,45);
my $Day = int(CSIFPF($line,49));
#my $Day = $yday + 1;

# and print them out for the operator
printf ("Time %02i:%02i\t",$minutes/60,$minutes%60);
printf ("Temp: %3.0f\t",$AirTF);
printf ("RH: %3.0f\t\t",$RH);
printf ("Dewpoint: %3.0f\n",$DewPoint);
printf ("WSpeed: %3.0f\t",$WS_mph);
printf ("WDir: %3.0f\t",$WindDir);
printf ("WSmax: %3.0f\t",$WS_Max);
printf ("Rain: %5.2f\n",$Rain_Day);
printf ("Solar: %4.0f\t",$SlrW);
printf ("Baro: %5.2f\t",$BAR_inHg);
printf ("Vcc: %5.2f\t",$Vcc);
printf ("Day: %i\n",$Day);

printf("-------------------------\n");

# end communications
sleep(ReplyDelay);
printf("End...");
send (SOCKET, "E\r",0);
recv (SOCKET,$line,512,0);
printf ("$line");

close SOCKET or die "close: $!";

# Power Fail stuff
if (($Vcc > 12.85) && ($Wanda_Mem->{PowerFailFlag}==TRUE))
{
my $MailPowerOKString = "Wanda Detects AC Return!\n";
$MailPowerOKString .= sprintf("%5.2f Volts at %02i:%02i\n",$Vcc,$minutes/60,$minutes%60);

my %mailpowerok = (
To => 'person@domain.com',
From => 'dummy@geekho.com',
Subject => "Power return!",
Message => $MailPowerOKString,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mailpowerok) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailPowerOKString);

$Wanda_Mem->{PowerFailFlag} = FALSE; # and set flag for next time
}
elsif (($Vcc < 12.80) && ($Wanda_Mem->{PowerFailFlag}==FALSE))
{
my $MailPowerFailString = "Wanda Detects AC Loss!\n";
$MailPowerFailString .= sprintf("%5.2f Volts at %02i:%02i\n",$Vcc,$minutes/60,$minutes%60);

my %mailpowerfail = (
To => 'user@domain.com',
From => 'dummy@geekho.com',
Subject => "Power fail!",
Message => $MailPowerFailString,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mailpowerfail) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailPowerFailString);

$Wanda_Mem->{PowerFailFlag} = TRUE; # and set flag for next time
}

# Battery Critical stuff
if (($Vcc > 12.00) && ($Wanda_Mem->{BatteryCriticalFlag}==TRUE))
{
my $MailBatteryOKString = "Wanda Detects Battery OK!\n";
$MailBatteryOKString .= sprintf("%5.2f Volts at %02i:%02i\n",$Vcc,$minutes/60,$minutes%60);

my %mailbatteryok = (
To => 'user@domain.com',
From => 'dummy@geekho.com',
Subject => "Battery OK!",
Message => $MailBatteryOKString,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mailbatteryok) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailBatteryOKString);

$Wanda_Mem->{BatteryCriticalFlag} = FALSE; # and set flag for next time
}
elsif (($Vcc < 11.50) && ($Wanda_Mem->{BatteryCriticalFlag}==FALSE))
{
my $MailBatteryCriticalString = "Wanda Detects Battery Critical!\n";
$MailBatteryCriticalString .= sprintf("%5.2f Volts at %02i:%02i\n",$Vcc,$minutes/60,$minutes%60);

my %mailbatterycritical = (
To => 'user@domain.com',
From => 'dummy@geekho.com',
Subject => "Battery Critical!",
Message => $MailBatteryCriticalString,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mailbatterycritical) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailBatteryCriticalString);

$Wanda_Mem->{BatteryCriticalFlag} = TRUE; # and set flag for next time
}

if ($Wanda_Mem->{WindDecade} < int($WS_Max/10))
{
my $MailWind = "Wind Speed Increases!\n";
$MailWind .= sprintf("%5.2f Miles Per Hour at %02i:%02i\n",$WS_Max,$minutes/60,$minutes%60);


my %mailwindnotify = (
# To => 'user@domain.com',
To => 'MailingList@mail.domain.com',
From => 'dummy@geekho.com',
Subject => "Wind Speed!",
Message => $MailWind,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mailwindnotify) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailWind);

$Wanda_Mem->{WindDecade} = int($WS_Max/10); # and remember that you sent that decade
}

# Swap these two (and see below) for reporting all rain or just inches
if ($Wanda_Mem->{RainReport} < int($Rain_Day))
#if ($Wanda_Mem->{RainReport} < $Rain_Day)
{
my $MailRain = "Another Inch of Rain!\n";
$MailRain .= sprintf("%5.2f inches total at %02i:%02i\n",$Rain_Day,$minutes/60,$minutes%60);


my %mailrainnotify = (
# To => 'user@domain.com',
To => 'MailingList@mail.domain.com',
From => 'dummy@geekho.com',
Subject => "Rainfall!",
Message => $MailRain,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mailrainnotify) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailRain);

#$Wanda_Mem->{RainReport} = $Rain_Day; # and remember that you sent that number
$Wanda_Mem->{RainReport} = int($Rain_Day); # and remember that you sent that inch
}

# calculate min/max stuff
if ($Wanda_Mem->{TMax} < $AirTF)
{$Wanda_Mem->{TMax} = $AirTF}
if ($Wanda_Mem->{TMin} > $AirTF)
{$Wanda_Mem->{TMin} = $AirTF}

if ($Wanda_Mem->{RHMax} < $RH)
{$Wanda_Mem->{RHMax} = $RH}
if ($Wanda_Mem->{RHMin} > $RH)
{$Wanda_Mem->{RHMin} = $RH}

if ($Wanda_Mem->{BaroMax} < $BAR_inHg)
{$Wanda_Mem->{BaroMax} = $BAR_inHg}
if ($Wanda_Mem->{BaroMin} > $BAR_inHg)
{$Wanda_Mem->{BaroMin} = $BAR_inHg}

if ($Wanda_Mem->{SolarMax} < $SlrW)
{$Wanda_Mem->{SolarMax} = $SlrW}

if ($Wanda_Mem->{WSMax} < $WS_Max)
{$Wanda_Mem->{WSMax} = $WS_Max}

if ($Wanda_Mem->{Rain} < $Rain_Day)
{$Wanda_Mem->{Rain} = $Rain_Day}

$Wanda_Mem->{Counter}++;

#print Dumper($Wanda_Mem);

XMLout($Wanda_Mem,AttrIndent=>1,OutputFile=>"/home/Wanda/wanda.xml");

# Now send me some mail
#if ($minutes == 0)
# if midnight passed (we've got giant count from yesterday)
#if ($Wanda_Mem->{Counter} > (10+$minutes*1.1))
if ($Wanda_Mem->{Day} != $Day)
{

my $MailString = "Weather for yesterday,";
$MailString .= sprintf(" Julian %i:\n\n",$Wanda_Mem->{Day});
$MailString .= sprintf("\tMax Temperature: %3.0f Degrees F\n",$Wanda_Mem->{TMax});
$MailString .= sprintf("\tMin Temperature: %3.0f\n\n",$Wanda_Mem->{TMin});
$MailString .= sprintf("\tMax Humidity: %3.0f Percent\n",$Wanda_Mem->{RHMax});
$MailString .= sprintf("\tMin Humidity: %3.0f\n\n",$Wanda_Mem->{RHMin});
$MailString .= sprintf("\tMax Barometer: %5.2f Inches of Mercury\n",$Wanda_Mem->{BaroMax});
$MailString .= sprintf("\tMin Barometer: %5.2f\n\n",$Wanda_Mem->{BaroMin});
$MailString .= sprintf("\tMax Solar Power: %4.0f Watts per Square Meter\n\n",$Wanda_Mem->{SolarMax});
$MailString .= sprintf("\tPeak Windspeed: %3.0f Miles Per Hour\n\n",$Wanda_Mem->{WSMax});
$MailString .= sprintf("\tRainfall : %4.2f Inches\n\n",$Wanda_Mem->{Rain});
$MailString .= sprintf("%s %i/1440 samples, Vcc: %5.2f\n",$RevString,$Wanda_Mem->{Counter},$Vcc);

my %mail = (
# To => 'user@domain.com',
To => 'MailingList@mail.domain.com',
From => 'dummy@geekho.com',
Subject => "Daily Weather Summary",
Message => $MailString,
# smtp => 'mail.domain.com',
smtp => '192.168.1.90',
);

sendmail(%mail) or die $Mail::Sendmail::error;

print "OK. Log says:\n", $Mail::Sendmail::log;

printf("%s",$MailString);

# now reset the totals for the next day
$Wanda_Mem->{TMax} = 0.0;
$Wanda_Mem->{TMin} = 999.9;
$Wanda_Mem->{RHMax} = 0.0;
$Wanda_Mem->{RHMin} = 9999.9;
$Wanda_Mem->{BaroMax} = 0.0;
$Wanda_Mem->{BaroMin} = 9999.9;
$Wanda_Mem->{SolarMax} = 0.0;
$Wanda_Mem->{WSMax} = 0.0;
$Wanda_Mem->{Rain} = 0.0;
$Wanda_Mem->{Counter} = 0;
$Wanda_Mem->{Day} = $Day; # Remember we did this day
$Wanda_Mem->{WindDecade} = 3; #set threshold for wind speed reporting for next day
$Wanda_Mem->{RainReport} = 0; # zero rain report

XMLout($Wanda_Mem,AttrIndent=>1,OutputFile=>"/home/Wanda/wanda.xml");

}

# now create the HTTP GET string:

my $GetString = "http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php";
$GetString .= "?ID=IPINECAY2&PASSWORD=secret";
$GetString .= "&action=updateraw";
$GetString .= sprintf("&softwaretype=%s",$RevString);
$GetString .= sprintf("&dateutc=%04i-%02i-%02i+%02i:%02i",$year+1900,$mon,$mday,$minutes/60,$minutes%60);
$GetString .= sprintf("&tempf=%03.0f",$AirTF);
$GetString .= sprintf("&humidity=%03.0f",$RH);
$GetString .= sprintf("&dewptf=%03.0f",$DewPoint);
$GetString .= sprintf("&windspeedmph=%03.0f",$WS_mph);
$GetString .= sprintf("&winddir=%03.0f",$WindDir);
$GetString .= sprintf("&windgustmph=%03.0f",$WS_Max);
$GetString .= sprintf("&dailyrainin=%05.2f",$Rain_Day);
$GetString .= sprintf("&solarradiation=%04.0f",$SlrW);
$GetString .= sprintf("&baromin=%05.2f",$BAR_inHg);

# show it to me, and send it to WxUnderground
printf("WxUG: %i characters:$GetString\n",length($GetString));
getprint("$GetString");

#=================================================================
# now the stuff from cwop, merged in 06/23/08 WPNS
# now create the output string. Note (re)using getString
# from documentation at http://www.wxqa.com/faq.html
# note that some of these numbers require a bit of work before final
# release, as they aren't quite kosher...
#printf("mins ends in %s\n",substr($min,(length($min)-1),1));
if (substr($min,(length($min)-1),1) ne "8")
{
# printf("%02i:%02i - No CWOP\n",$hour,$min);
die "No CWOP at $hour:$min\n";
}

$GetString = "DW9999"; # your CW Number
$GetString .= ">APRS:"; # boilerplate
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime time; # now we want GMT
$GetString .= sprintf("@%02i%02i%02iz",$mday,$hour,$min); # you are ignoring my time anyway
$GetString .= "2152.12N/07205.42W"; # your location here, ddmm.hh, DDDmm.hh
$GetString .= sprintf("_%03.0f",$WindDir); # wind direction
$GetString .= sprintf("/%03.0f",$WS_mph); # wind speed (instantaneous, not average!)
#$GetString .= sprintf("g%03.0f",$WS_Max); # Wind Gust (Daily max, not 5-minute!)
$GetString .= sprintf("g..."); # Wind Gust
$GetString .= sprintf("t%03.0f",$AirTF); # temp degrees F
$GetString .= sprintf("P%03.0f",($Rain_Day*100)); # what if this overflows? I've had 4.32, so it shouldn't, but...
if ($RH == 100)
{$GetString .= sprintf("h00");}
else
{$GetString .= sprintf("h%02.0f",$RH);}
# from http://www.csgnetwork.com/barcorrecthcalc.html
$GetString .= sprintf("b%05.0f",(($BAR_inHg+0.021983866084310615)*33.8637526*10)); # inches of mercury (plus 20-foot pressure altitude) converted to millibar TENTHS
$GetString .= sprintf("eCampbellSci_%s\r\n",$RevString); # identifier

# show it to me
printf("%i characters: $GetString",length($GetString));

# now open connection to APRS server
printf ("CWOP Socket ");
# create the socket, connect to the port
socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2])
or die "Can't create a socket $!\n";
printf ("created ");
connect(SOCKET, sockaddr_in($cwop_port, inet_aton($cwop_server)))
or die "Can't connect to port $port! \n";
printf ("opened ");
setsockopt (SOCKET, SOL_SOCKET, SO_REUSEADDR, 1);
printf ("set!\n");

# show me the prompt
recv (SOCKET,$line,512,0);
printf ("%i bytes:$line\n",length($line));

# send login
printf("Login...");
send (SOCKET, "user DW9999 pass -1 vers $RevString\r\n",0);
recv (SOCKET,$line,512,0);
printf ("$line\n");

# send string
printf("Send...");
send (SOCKET, "$GetString",0);
#recv (SOCKET,$line,512,0);
#printf ("$line\n");

sleep(3);
printf("Done!\n");

close SOCKET or die "close: $!";

#-----------------------------------
# convert 4 bytes in Campbell Scientific, Inc. Floating Point Format to a float
sub CSIFPF{
my ($linein,$index) = @_;
# printf("%02x %02x %02x %02x\n",ord(substr($linein,$index,1)),ord(substr($linein,$index+1,1)),ord(substr($linein,$index+2,1)),ord(substr($linein,$index+3,1)));
my $sign =( ord(substr($linein,$index,1)) & 0x80);
# printf("Sign %i\n",$sign);
my $exponent = ((ord(substr($linein,$index,1)) & 0x7f) - 64);
# printf("Exponent %i\n",$exponent);
my $mantissa = ((ord(substr($linein,$index+1,1)))*65536) + ((ord(substr($linein,$index+2,1)))*256) + (ord(substr($linein,$index+3,1)));
# printf("Mantissa %i\n",$mantissa);

if ($sign == 0)
{
return ($mantissa/(2**24))*(2**$exponent)
}
else
{
return (0-($mantissa/(2**24))*(2**$exponent))
}
}

* Last updated by: wpns on 2/1/2009 @ 6:43 PM *


wpns Feb 11, 2010 02:11 PM

I'm up to version 7.2 of the above software, so ping me (reply here) if there's any interest, and I'll release the latest update.


Dana Feb 12, 2010 05:30 PM

I have a "dumb question" (please don't make me read the Perl script... OK, I browsed it but...) -- you are uploading the Current Conditions only, and not the METAR and Aviation data?

:) Dana W.


ChipsNSalsa Feb 12, 2010 05:40 PM

Wow! I think the method I described in the following forum thread is easier for most non-programmers to get their head around.

http://www.campbellsci.com/forum/messages.cfm?threadid=BC6B01A7-078A-156D-C34DA61A57C3E63E


wpns Feb 13, 2010 12:16 AM

Dana,

Not sure what METAR is, or where I'd get/derive aviation data, but yeah, current conditions only, and I cheat a bit on wind gust and substitute peak wind speed for the day.

Glen,

Yeah, given LoggerNet already running on a WinDoze machine, I suspect you're right.

Enjoy.

Log in or register to post/reply in the forum.