From 34fb2f9bb6432b6dffe2f79fe6c4e31801565aad Mon Sep 17 00:00:00 2001 From: Dirk Koopman Date: Fri, 11 Jul 2014 23:41:37 +0100 Subject: [PATCH] successfully decoding loop pkts with Mojo --- a.pl | 545 +++++++++++++++++++++++++++++++++++++++++++++++++++ dirk-ae.pl | 562 +++++++++++++++++++++++++++++++++++++++++++++++++++++ dumpraw.pl | 133 +++++++++++++ loop.pl | 252 ++++++++++++++++++++++++ 4 files changed, 1492 insertions(+) create mode 100755 a.pl create mode 100755 dirk-ae.pl create mode 100755 dumpraw.pl create mode 100755 loop.pl diff --git a/a.pl b/a.pl new file mode 100755 index 0000000..51a503c --- /dev/null +++ b/a.pl @@ -0,0 +1,545 @@ +#!/usr/bin/perl + +use DBI; +use strict; +use Device::SerialPort; +use Time::HiRes; + +my $devname = "/dev/davis"; +my $dbsort = "SQLite"; +my $db="weather.db"; +my $host=""; +my $userid=""; +my $passwd=""; +my $connectionInfo="dbi:$dbsort:$db"; +$connectionInfo .= ":$host" if $host; + +# make connection to database +my %attr = ( PrintError => 0, RaiseError => 0 ); +my $dbh = DBI->connect($connectionInfo,$userid,$passwd,\%attr) or die "Couldn't connect to database: " . DBI->errstr; + + +my ($count, $result); +my %hsh; +my @crc_table=(); +my %bar_trend=(); +load_crc_table(); + +my $ob = Device::SerialPort->new ($devname) || die; + +$ob->user_msg(1); # misc. warnings +$ob->error_msg(1); # hardware and data errors + +$ob->baudrate(19200); +$ob->parity("none"); +#$ob->parity_enable(1); # for any parity except "none" +$ob->databits(8); +$ob->stopbits(1); +$ob->handshake('none'); +$ob->read_const_time(15000); # ultimate timeout (15 seconds) +$ob->write_settings||die"setting failed"; + + +my $awake=0; +my $attempts=0; +$ob->write("\n"); # initial wake + +while ($awake==0) { + $ob->write("\n"); # wake for real + $ob->read_interval(600); # wait for a max of 600ms + ($count, $result) = $ob->read(10); # read up to 10 chars + if ($result eq "\n\r") { + print "awoke on attempt $attempts :)\n"; + $awake=1; + } else { + print "wake error on attempt $attempts :(\n"; + } + $attempts++; + dienice("failed to wake device - tried $attempts times") unless $attempts<6; +} + +$ob->write("LAMPS 0\n"); +$ob->read_interval(300); # wait for a max of 300ms +($count, $result) = $ob->read(8); # read up to 8 chars +$result=~s/(\r|\n)//g; +if ($result eq "OK") { + print "lamps on :)\n"; +} else { + dienice("lamp error '$result'"); +} + + + +my $ref=gettime(); +$ref->[5]+=1900; +print "$ref->[2]:$ref->[1]:$ref->[0] $ref->[3]/$ref->[4]/$ref->[5]\n"; + +#settime(); + +#do_dmpaft(); + +while (1) { + get_loop(); # if you do anything after here - you need to wake up the device again + print "** do something else\n"; + sleep 5; + $ob->write("\r"); # wake + $ob->read_interval(500); # wait for a max of 300ms + my ($count, $result) = $ob->read(4096); # read any crap up to 4096 chars +} + +#print "count=$count $result\n"; +#for(my $i=0; $i<$count; $i++) +#{ printf("%02d ",ord(substr($result,$i,1))); } + +#$ob->write("WRD\x12\x4d\n"); + +undef $ob; +exit 0; + +sub dienice +{ + my $err=shift; + print "$err\n"; + undef $ob; + exit 9; +} + +sub parse_loop_blck +{ + my $blk = shift; + my $loo = substr $blk,0,3; + unless ( $loo eq 'LOO') { + warn("Block invalid loo -> $loo\n"); return ""; + } + my $t; + + #$hsh{'next_rec'} = unpack("s", substr $blk,5,2); + + $hsh{'Barometric_Trend'} = unpack("C", substr $blk,3,1); + $hsh{'Barometric_Trend_txt'} = $bar_trend{$hsh{'Barometric_Trend'}}; + $t = unpack("s", substr $blk,7,2) / 1000; + # $hsh{'Barometric_Press_hg'} = $t; + $hsh{'Barometric_Press_mb'} = sprintf("%.2f",$t*33.8637526); + + + $t = unpack("s", substr $blk,9,2) / 10; + # $hsh{'Air_Temp_Inside_f'} = $t; + $hsh{'Air_Temp_Inside_c'} = sprintf("%.1f",($t - 32) * 5/9); + my $tf = unpack("s", substr $blk,12,2) / 10; + # $hsh{'Air_Temp_Outside_f'} = $tf; + $hsh{'Air_Temp_Outside_c'} = sprintf("%.1f",($tf - 32) * 5/9); + + $hsh{'Wind_Speed_mph'} = unpack("C", substr $blk,14,1); + # $hsh{'Wind_Speed_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_mph'}*0.44704); + $hsh{'Wind_Speed_10min_Avg_mph'} = unpack("C", substr $blk,15,1); + # $hsh{'Wind_Speed_10min_Avg_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_10min_Avg_mph'}*0.44704); + $hsh{'Wind_Dir'} = unpack("s", substr $blk,16,2); + + + $hsh{'Humidity_Outside'} = unpack("C", substr $blk,33,1); + $hsh{'Humidity_Inside'} = unpack("C", substr $blk,11,1); + $hsh{'Dew_Point'} = dew_point($tf, $hsh{'Humidity_Outside'}); + + # $hsh{'UV'} = unpack("C", substr $blk,43,1); + # $hsh{'Solar'} = unpack("s", substr $blk,44,2); # watt/m**2 + + $hsh{'Rain_Rate'} = (unpack("s", substr $blk,41,2) / 100) * 25.4; # Inches per hr converted to mm + $hsh{'Rain_Storm'} = (unpack("s", substr $blk,46,2) / 100) * 25.4; # Inches per storm + #$hsh{'Storm_Date'} = unpack("s", substr $blk,48,2); # Need to parse data (not sure what this is) + $hsh{'Rain_Day'} = (unpack("s", substr $blk,50,2)/100) * 25.4; + $hsh{'Rain_Month'} = (unpack("s", substr $blk,52,2)/100) * 25.4; + $hsh{'Rain_Year'} = (unpack("s", substr $blk,54,2)/100) * 25.4; + + $hsh{'ET_Day'} = unpack("s", substr $blk,56,2)/1000; + $hsh{'ET_Month'} = unpack("s", substr $blk,58,2)/100; + $hsh{'ET_Year'} = unpack("s", substr $blk,60,2)/100; + + #$hsh{'Alarms_Inside'} = unpack("b8", substr $blk,70,1); + #$hsh{'Alarms_Rain'} = unpack("b8", substr $blk,70,1); + #$hsh{'Alarms_Outside'} = unpack("b8", substr $blk,70,1); + + $hsh{'Batt_Transmitter'} = unpack("C", substr $blk,86,1); # * 0.005859375 + $hsh{'Batt_Console'} = unpack("s", substr $blk,87,2) * 0.005859375; + + $hsh{'Forecast_Icon'} = unpack("C", substr $blk,89,1); + $hsh{'Forecast_Rule'} = unpack("C", substr $blk,90,1); + + $hsh{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) ); + $hsh{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/; + $hsh{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) ); + $hsh{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/; + + #my $nl = ord substr $blk,95,1; + #my $cr = ord substr $blk,96,1; + + my $crc = unpack "%n", substr($blk,97,2); + my $crc_calc = CRC_CCITT($blk); + + if ($crc_calc==0) { + return 0; + } + else { + print "CRC check failed for LOOP data!\n"; + return 1; + } + #delete @hsh{'crc', 'crc_calc', 'next_rec'}; + #delete($hsh{crc})||die"cant delete crc"; + #delete($hsh{crc_calc})||die"cant delete crc_calc"; + #delete($hsh{next_rec})||die"cant delete next_rec"; + +} + +sub dew_point +{ + my $temp = shift @_; + my $rh = shift @_; + + # Using the simplified approximation for dew point + # Accurate to 1 degree C for humidities > 50 % + # http://en.wikipedia.org/wiki/Dew_point + + my $dew_point = $temp - ( (100 - $rh)/5 ); + + return $dew_point; +} + +sub CRC_CCITT +{ + # Expects packed data... + my $data_str = shift @_; + + my $crc = 0; + my @lst = split //, $data_str; + foreach my $data (@lst) { + my $data = unpack("c",$data); + + my $crc_prev = $crc; + my $index = $crc >> 8 ^ $data; + my $lhs = $crc_table[$index]; + #print "lhs=$lhs, crc=$crc\n"; + my $rhs = ($crc << 8) & 0xFFFF; + $crc = $lhs ^ $rhs; + + + } + + return $crc; +} + +sub load_crc_table +{ + + @crc_table = ( + 0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0 + ); + + $bar_trend{-60} = "Falling Rapidly"; + $bar_trend{196} = "Falling Rapidly"; + $bar_trend{-20} = "Falling Slowly"; + $bar_trend{236} = "Falling Slowly"; + $bar_trend{0} = "Steady"; + $bar_trend{20} = "Rising Slowly"; + $bar_trend{60} = "Rising Rapidly"; +} + +sub gettime +{ + $ob->write("GETTIME\n"); + $ob->read_interval(200); + my($cnt_in, $str) = $ob->read(9); + if ($cnt_in==0) { + dienice("read error cnt_in=$cnt_in, str='$str'"); + } + + my $ck = CRC_CCITT(substr($str,1,9)); + if ( $ck ) { + warn "checksum error"; return 0; + } + my @rsp_lst = split //, $str; + shift @rsp_lst; + @rsp_lst = map ord, @rsp_lst; + return \@rsp_lst; +} + +sub settime +{ + my $s_time = [ localtime() ]; + $s_time->[4] += 1; + + $ob->write("SETTIME\n"); + $ob->read_interval(300); + my ($cnt_in, $str) = $ob->read(1); + my $ack = ord $str; + if ( $ack != 6 ) { + warn "SETTIME not set ack $ack !"; return 0; + } + my ($sec, $min, $hour, $day, $mon, $yr) = @{$s_time}; + $str = join "", map chr, ($sec, $min, $hour, $day, $mon, $yr); + my $ck = CRC_CCITT($str); + $str = $str . pack("n",$ck); + $ob->write($str); + ($cnt_in, $str) = $ob->read(1); + if ( ord($str) != 6 ) { + warn "SETTIME not set!"; return 0; + } + sleep 3; # The console seems to need to some time here... +} + +sub get_loop +{ + print "** get loop at ", scalar localtime, "\n"; + my $loops=1; + $ob->write("LOOP $loops\n"); + $ob->read_interval(0); + ($count, $result) = $ob->read(1); + for (my $i=0; $i<$loops; $i+=1) { + my $rc=loop_dump(); + if ($rc!=0) { + last; + } + } + return; + +} + +sub loop_dump +{ + $ob->read_interval(0); + ($count, $result) = $ob->read(99); + if ($count != 99) { + print "loop error - got $count bytes, expected 99\n"; + return 1; + } + print "LOOP data received\n"; + + if (ord(substr($result,0,1))==6) { + $result=substr($result,1); + } + + my $rc=parse_loop_blck($result); + if ($rc!=0) { + return; + } # bad crc so goto next + foreach my $key (sort keys %hsh) { + print "$key = $hsh{$key}\n"; + } + + my $stmt = 'INSERT INTO current (Date_Time,' . join(',', keys %hsh) . ') VALUES (NOW(),' . join(',', ('?') x keys %hsh) . ')'; + + $dbh->do( $stmt, undef, values %hsh); + + %hsh=(); + return 0; +} + + +sub do_dmpaft +{ + #my $self = shift @_; + open(DMP,">dump.txt"); + my $vDateStamp = shift @_; + my $vTimeStamp = shift @_; + + # If not date/time stamp then assume 0 which will down load the entire archive + unless ( $vDateStamp ) { + $vDateStamp = 0; + } + unless ( $vTimeStamp ) { + $vTimeStamp = 0; + } + + #my $port_obj = $self->{'port_obj'}; + + my $datetime = pack("ss",$vDateStamp, $vTimeStamp); + + my $crc = CRC_CCITT($datetime); + my $cmd = pack("ssn",$vDateStamp,$vTimeStamp,$crc); + + #----------------------- + #my $str = unpack("H*", $cmd); + #$str =~ s/(\w{2})/$1 /g; + # Documentation is wrong! The example should be <0xC6><0x06><0xA2><0x03> in section X + #print "cmd : $str \n";exit; + #----------------------- + + #sleep 2; # Needed after loop + #$self->wake_up(); + + # Ok let's start the communication sequence.... + my $cnt_out = $ob->write("DMPAFT\n"); + unless ($cnt_out) { + warn "write failed\n"; + } + ; + + $ob->read_interval(300); + my ($cnt_in, $str) = $ob->read(1); + + my $ack = ord $str; + unless ($ack == 6) { + warn "Ack not received on DMPAFT command: $ack"; exit -1; + } + + $cnt_out = $ob->write($cmd); + unless ($cnt_out) { + warn "write failed\n"; + } + ; + ($cnt_in, $str) = $ob->read(7); + + $ack = ord substr($str,0,1); + + my $ls = unpack("H20",substr($str,1,4) ); + $ls =~ s/(\w{2})/$1 /g; + + my $pages = unpack("s",substr($str,1,2) ); + my $rec_start = unpack("s",substr($str,3,2) ); + + $crc = CRC_CCITT(substr($str,1,6) ); + + print "Pages = $pages : rec = $rec_start Datestamp $vDateStamp $crc\n"; + + $cnt_out = $ob->write( pack("h", 0x06) ); + + #if ($pages == 513 ) { return -1 } + + my @arc_rec_lst; + foreach my $page (1..$pages) { + my $page_sz = 267; + $ob->read_interval(0); + my ($cnt_in, $str) = $ob->read($page_sz); #,3 + printf("len=%s\n",length($str)); + if ($cnt_in!=$page_sz) { + dienice("hmm, dmpaft only got $cnt_in bytes. was expecting $page_sz"); + } + print "Page $page\n"; + #print DMP $str,"\n"; + #print "ACK receipt of page $page\n"; + #$ob->write( pack("h", 0x06) ); + #next; + my $calc_crc = CRC_CCITT($str); + my $crc = unpack "%n", substr($str,265,2); + print "page crc=$crc, calc_crc=$calc_crc\n"; + my $rec_sz = 52; + my $date_prev = 0; + my %hsh; + + foreach my $rec ( 0..4 ) { + if ( ($page == 1) && ($rec < $rec_start ) ) { + next; + } # Find the right starting point... + + my $start_ptr = 1 + ($rec * $rec_sz ); + my $rec_str = substr($str, $start_ptr ,52); + + #print "$start_ptr \t > " . unpack( "h*", $rec_str) . "\n"; + + my $date = substr($rec_str,0,2); + my $date_curr = unpack "s", $date; + + # Check if we have wrapped... + if ( $date_curr < $date_prev ) { + last; + } + $date_prev = $date_curr; + + $hsh{'date_stamp'} = $date_curr; + $hsh{'time_stamp'} = unpack "s", substr($rec_str,2,2); + + $hsh{'day'} = unpack( "c", $date & pack("c",0x1F) ); + $hsh{'month'} = ( $hsh{'date_stamp'} >> 5) & 0xF; + $hsh{'year'} = ( $hsh{'date_stamp'} >> 9) + 2000; + + $hsh{'hour'} = sprintf("%02d", int ( $hsh{'time_stamp'} / 100 )); + + $hsh{'min'} = $hsh{'time_stamp'} - ($hsh{'hour'} * 100); + $hsh{'min'} = sprintf("%02d", $hsh{'min'}); + + $hsh{'time_stamp_fmt'} = "$hsh{'hour'}:$hsh{'min'}:00"; + $hsh{'date_stamp_fmt'} = "$hsh{'year'}_$hsh{'month'}_$hsh{'day'}"; + + #$hsh{'unixtime'} = timelocal(0,$hsh{min}, $hsh{hour}, $hsh{day}, $hsh{month}-1, $hsh{year}-1900); + + $hsh{'Air_Temp'} = unpack("s", substr($rec_str,4,2)) / 10; + $hsh{'Air_Temp_Hi'} = unpack("s", substr($rec_str,6,2)) / 10; + $hsh{'Air_Temp_Lo'} = unpack("s", substr($rec_str,8,2)) / 10; + $hsh{'Rain_Clicks'} = unpack("s", substr($rec_str,10,2)); + $hsh{'Rain_Rate'} = unpack("s", substr($rec_str,12,2)) / 100; # Inches per hour + $hsh{'Barometric_Press'} = unpack("s", substr $rec_str,14,2) / 1000; + $hsh{'Solar'} = unpack("s", substr $rec_str,16,2); # watt/m**2 + $hsh{'Wind_Samples'} = unpack("s", substr $rec_str,18,2); + $hsh{'Air_Temp_Inside'} = unpack("s", substr $rec_str,20,2) / 10; + + $hsh{'Relative_Humidity_Inside'} = unpack("C", substr $rec_str,22,1); + $hsh{'Relative_Humidity'} = unpack("C", substr $rec_str,23,1); + + $hsh{'Wind_Speed'} = unpack("C", substr($rec_str,24,1)); + $hsh{'Wind_Gust_Max'} = unpack("C", substr($rec_str,25,1)); + $hsh{'Wind_Dir_Max'} = unpack("C", substr($rec_str,26,1)); + $hsh{'Wind_Dir'} = unpack("C", substr($rec_str,27,1)); + + $hsh{'UV'} = unpack("C", substr($rec_str,28,1)) / 10; + $hsh{'ET'} = unpack("C", substr($rec_str,29,1)) / 1000; + + $hsh{'Solar_Max'} = unpack("s", substr($rec_str,30,2)); + $hsh{'UV_Max'} = unpack("C", substr($rec_str,32,1)); + + $hsh{'Forecast_Rule'} = unpack("C", substr($rec_str,33,1)); + + # $hsh{'Dew_Point'} = _dew_point($hsh{'Air_Temp'},$hsh{'Relative_Humidity'}); + + # Miscellaneous others omitted for now + + print "date> $hsh{'Air_Temp'} $hsh{'time_stamp'} $hsh{'time_stamp_fmt'} $hsh{'date_stamp'} $hsh{'date_stamp_fmt'}\n"; + #print Dumper \%hsh; + + push @arc_rec_lst, {%hsh}; + } + + #$in = ; # Testing step through facility + #if ($in =~ /q/i ) { $port_obj->write( pack("h", 0x1B) ); last; } + #else { $port_obj->write( pack("h", 0x06) ); } + print "ACK receipt of page\n"; + $ob->write( pack("h", 0x06) ); + + } + close DMP; +} + +package Device::SerialPort; + +sub read_interval +{ + +} diff --git a/dirk-ae.pl b/dirk-ae.pl new file mode 100755 index 0000000..7ad9d5b --- /dev/null +++ b/dirk-ae.pl @@ -0,0 +1,562 @@ +#!/usr/bin/perl + +use DBI; +use strict; +use Device::SerialPort; +use Time::HiRes; +use AnyEvent; + +my $devname = "/dev/davis"; +my $dbsort = "SQLite"; +my $db="weather.db"; +my $host=""; +my $userid=""; +my $passwd=""; +my $connectionInfo="dbi:$dbsort:$db"; +$connectionInfo .= ":$host" if $host; + +# make connection to database +my %attr = ( PrintError => 0, RaiseError => 0 ); +my $dbh = DBI->connect($connectionInfo,$userid,$passwd,\%attr) or die "Couldn't connect to database: " . DBI->errstr; + + +my ($count, $result); +my %hsh; +my @crc_table=(); +my %bar_trend=(); +load_crc_table(); + +my $ob = Device::SerialPort->new ($devname) || die; + +$ob->user_msg(1); # misc. warnings +$ob->error_msg(1); # hardware and data errors + +$ob->baudrate(19200); +$ob->parity("none"); +#$ob->parity_enable(1); # for any parity except "none" +$ob->databits(8); +$ob->stopbits(1); +$ob->handshake('none'); +$ob->read_const_time(15000); # ultimate timeout (15 seconds) +$ob->write_settings||die"setting failed"; + +my $cv = AE::cv; + +my $hdl = new AnyEvent::Handle( + fh => $ob->{HANDLE}, + on_error => sub { + my ($hdl, $fatal, $msg) = @_; + AE::log error => $msg; + $hdl->destroy; + $cv->send; + }, + ); + +$hdl->push_write("\n"); # kick the serial port +my $awake=0; +my $attempts=0; +#$ob->write("\n"); # initial wake + +while ($awake==0) { + + $hdl->push_write("\n"); # wake for real + $hdl->push_read(regex => qr<\n\r>, timeout => sub ) + $ob->read_interval(600); # wait for a max of 600ms + ($count, $result) = $ob->read(10); # read up to 10 chars + if ($result eq "\n\r") { + print "awoke on attempt $attempts :)\n"; + $awake=1; + } + else { + print "wake error on attempt $attempts :(\n"; + } + $attempts++; + dienice("failed to wake device - tried $attempts times") unless $attempts<6; +} + +$ob->write("LAMPS 0\n"); +$ob->read_interval(300); # wait for a max of 300ms +($count, $result) = $ob->read(8); # read up to 8 chars +$result=~s/(\r|\n)//g; +if ($result eq "OK") { + print "lamps on :)\n"; +} +else { + dienice("lamp error '$result'"); +} + + + +my $ref=gettime(); +$ref->[5]+=1900; +print "$ref->[2]:$ref->[1]:$ref->[0] $ref->[3]/$ref->[4]/$ref->[5]\n"; + +#settime(); + +#do_dmpaft(); + +while (1) { + get_loop(); # if you do anything after here - you need to wake up the device again + print "** do something else\n"; + sleep 5; + $ob->write("\r"); # wake + $ob->read_interval(500); # wait for a max of 300ms + my ($count, $result) = $ob->read(4096); # read any crap up to 4096 chars +} + +#print "count=$count $result\n"; +#for(my $i=0; $i<$count; $i++) +#{ printf("%02d ",ord(substr($result,$i,1))); } + +#$ob->write("WRD\x12\x4d\n"); + +undef $ob; +exit 0; + +sub dienice +{ + my $err=shift; + print "$err\n"; + undef $ob; + exit 9; +} + +sub parse_loop_blck +{ + my $blk = shift; + my $loo = substr $blk,0,3; + unless ( $loo eq 'LOO') { + warn("Block invalid loo -> $loo\n"); return ""; + } + my $t; + + #$hsh{'next_rec'} = unpack("s", substr $blk,5,2); + + $hsh{'Barometric_Trend'} = unpack("C", substr $blk,3,1); + $hsh{'Barometric_Trend_txt'} = $bar_trend{$hsh{'Barometric_Trend'}}; + $t = unpack("s", substr $blk,7,2) / 1000; +# $hsh{'Barometric_Press_hg'} = $t; + $hsh{'Barometric_Press_mb'} = sprintf("%.2f",$t*33.8637526); + + + $t = unpack("s", substr $blk,9,2) / 10; +# $hsh{'Air_Temp_Inside_f'} = $t; + $hsh{'Air_Temp_Inside_c'} = sprintf("%.1f",($t - 32) * 5/9); + my $tf = unpack("s", substr $blk,12,2) / 10; +# $hsh{'Air_Temp_Outside_f'} = $tf; + $hsh{'Air_Temp_Outside_c'} = sprintf("%.1f",($tf - 32) * 5/9); + + $hsh{'Wind_Speed_mph'} = unpack("C", substr $blk,14,1); +# $hsh{'Wind_Speed_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_mph'}*0.44704); + $hsh{'Wind_Speed_10min_Avg_mph'} = unpack("C", substr $blk,15,1); +# $hsh{'Wind_Speed_10min_Avg_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_10min_Avg_mph'}*0.44704); + $hsh{'Wind_Dir'} = unpack("s", substr $blk,16,2); + + + $hsh{'Humidity_Outside'} = unpack("C", substr $blk,33,1); + $hsh{'Humidity_Inside'} = unpack("C", substr $blk,11,1); + $hsh{'Dew_Point'} = dew_point($tf, $hsh{'Humidity_Outside'}); + +# $hsh{'UV'} = unpack("C", substr $blk,43,1); +# $hsh{'Solar'} = unpack("s", substr $blk,44,2); # watt/m**2 + + $hsh{'Rain_Rate'} = (unpack("s", substr $blk,41,2) / 100) * 25.4; # Inches per hr converted to mm + $hsh{'Rain_Storm'} = (unpack("s", substr $blk,46,2) / 100) * 25.4; # Inches per storm + #$hsh{'Storm_Date'} = unpack("s", substr $blk,48,2); # Need to parse data (not sure what this is) + $hsh{'Rain_Day'} = (unpack("s", substr $blk,50,2)/100) * 25.4; + $hsh{'Rain_Month'} = (unpack("s", substr $blk,52,2)/100) * 25.4; + $hsh{'Rain_Year'} = (unpack("s", substr $blk,54,2)/100) * 25.4; + + $hsh{'ET_Day'} = unpack("s", substr $blk,56,2)/1000; + $hsh{'ET_Month'} = unpack("s", substr $blk,58,2)/100; + $hsh{'ET_Year'} = unpack("s", substr $blk,60,2)/100; + + #$hsh{'Alarms_Inside'} = unpack("b8", substr $blk,70,1); + #$hsh{'Alarms_Rain'} = unpack("b8", substr $blk,70,1); + #$hsh{'Alarms_Outside'} = unpack("b8", substr $blk,70,1); + + $hsh{'Batt_Transmitter'} = unpack("C", substr $blk,86,1); # * 0.005859375 + $hsh{'Batt_Console'} = unpack("s", substr $blk,87,2) * 0.005859375; + + $hsh{'Forecast_Icon'} = unpack("C", substr $blk,89,1); + $hsh{'Forecast_Rule'} = unpack("C", substr $blk,90,1); + + $hsh{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) ); + $hsh{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/; + $hsh{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) ); + $hsh{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/; + + #my $nl = ord substr $blk,95,1; + #my $cr = ord substr $blk,96,1; + + my $crc = unpack "%n", substr($blk,97,2); + my $crc_calc = CRC_CCITT($blk); + + if ($crc_calc==0) { + return 0; + } + else { + print "CRC check failed for LOOP data!\n"; + return 1; + } + #delete @hsh{'crc', 'crc_calc', 'next_rec'}; + #delete($hsh{crc})||die"cant delete crc"; + #delete($hsh{crc_calc})||die"cant delete crc_calc"; + #delete($hsh{next_rec})||die"cant delete next_rec"; + +} + +sub dew_point +{ + my $temp = shift @_; + my $rh = shift @_; + + # Using the simplified approximation for dew point + # Accurate to 1 degree C for humidities > 50 % + # http://en.wikipedia.org/wiki/Dew_point + + my $dew_point = $temp - ( (100 - $rh)/5 ); + + return $dew_point; +} + +sub CRC_CCITT +{ + # Expects packed data... + my $data_str = shift @_; + + my $crc = 0; + my @lst = split //, $data_str; + foreach my $data (@lst) { + my $data = unpack("c",$data); + + my $crc_prev = $crc; + my $index = $crc >> 8 ^ $data; + my $lhs = $crc_table[$index]; + #print "lhs=$lhs, crc=$crc\n"; + my $rhs = ($crc << 8) & 0xFFFF; + $crc = $lhs ^ $rhs; + + + } + + return $crc; +} + +sub load_crc_table +{ + + @crc_table = ( + 0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0 + ); + + $bar_trend{-60} = "Falling Rapidly"; + $bar_trend{196} = "Falling Rapidly"; + $bar_trend{-20} = "Falling Slowly"; + $bar_trend{236} = "Falling Slowly"; + $bar_trend{0} = "Steady"; + $bar_trend{20} = "Rising Slowly"; + $bar_trend{60} = "Rising Rapidly"; +} + +sub gettime +{ + $ob->write("GETTIME\n"); + $ob->read_interval(200); + my($cnt_in, $str) = $ob->read(9); + if ($cnt_in==0) { + dienice("read error cnt_in=$cnt_in, str='$str'"); + } + + my $ck = CRC_CCITT(substr($str,1,9)); + if ( $ck ) { + warn "checksum error"; return 0; + } + my @rsp_lst = split //, $str; + shift @rsp_lst; + @rsp_lst = map ord, @rsp_lst; + return \@rsp_lst; +} + +sub settime +{ + my $s_time = [ localtime() ]; + $s_time->[4] += 1; + + $ob->write("SETTIME\n"); + $ob->read_interval(300); + my ($cnt_in, $str) = $ob->read(1); + my $ack = ord $str; + if ( $ack != 6 ) { + warn "SETTIME not set ack $ack !"; return 0; + } + my ($sec, $min, $hour, $day, $mon, $yr) = @{$s_time}; + $str = join "", map chr, ($sec, $min, $hour, $day, $mon, $yr); + my $ck = CRC_CCITT($str); + $str = $str . pack("n",$ck); + $ob->write($str); + ($cnt_in, $str) = $ob->read(1); + if ( ord($str) != 6 ) { + warn "SETTIME not set!"; return 0; + } + sleep 3; # The console seems to need to some time here... +} + +sub get_loop +{ + print "** get loop at ", scalar localtime, "\n"; + my $loops=1; + $ob->write("LOOP $loops\n"); + $ob->read_interval(0); + ($count, $result) = $ob->read(1); + for (my $i=0; $i<$loops; $i+=1) { + my $rc=loop_dump(); + if ($rc!=0) { + last; + } + } + return; + +} + +sub loop_dump +{ + $ob->read_interval(0); + ($count, $result) = $ob->read(99); + if ($count != 99) { + print "loop error - got $count bytes, expected 99\n"; + return 1; + } + print "LOOP data received\n"; + + if (ord(substr($result,0,1))==6) { + $result=substr($result,1); + } + + my $rc=parse_loop_blck($result); + if ($rc!=0) { + return; + } # bad crc so goto next + foreach my $key (sort keys %hsh) { + print "$key = $hsh{$key}\n"; + } + + my $stmt = 'INSERT INTO current (Date_Time,' . join(',', keys %hsh) . ') VALUES (NOW(),' . join(',', ('?') x keys %hsh) . ')'; + + $dbh->do( $stmt, undef, values %hsh); + + %hsh=(); + return 0; +} + + +sub do_dmpaft +{ + #my $self = shift @_; + open(DMP,">dump.txt"); + my $vDateStamp = shift @_; + my $vTimeStamp = shift @_; + + # If not date/time stamp then assume 0 which will down load the entire archive + unless ( $vDateStamp ) { + $vDateStamp = 0; + } + unless ( $vTimeStamp ) { + $vTimeStamp = 0; + } + + #my $port_obj = $self->{'port_obj'}; + + my $datetime = pack("ss",$vDateStamp, $vTimeStamp); + + my $crc = CRC_CCITT($datetime); + my $cmd = pack("ssn",$vDateStamp,$vTimeStamp,$crc); + + #----------------------- + #my $str = unpack("H*", $cmd); + #$str =~ s/(\w{2})/$1 /g; + # Documentation is wrong! The example should be <0xC6><0x06><0xA2><0x03> in section X + #print "cmd : $str \n";exit; + #----------------------- + + #sleep 2; # Needed after loop + #$self->wake_up(); + + # Ok let's start the communication sequence.... + my $cnt_out = $ob->write("DMPAFT\n"); + unless ($cnt_out) { + warn "write failed\n"; + } + ; + + $ob->read_interval(300); + my ($cnt_in, $str) = $ob->read(1); + + my $ack = ord $str; + unless ($ack == 6) { + warn "Ack not received on DMPAFT command: $ack"; exit -1; + } + + $cnt_out = $ob->write($cmd); + unless ($cnt_out) { + warn "write failed\n"; + } + ; + ($cnt_in, $str) = $ob->read(7); + + $ack = ord substr($str,0,1); + + my $ls = unpack("H20",substr($str,1,4) ); + $ls =~ s/(\w{2})/$1 /g; + + my $pages = unpack("s",substr($str,1,2) ); + my $rec_start = unpack("s",substr($str,3,2) ); + + $crc = CRC_CCITT(substr($str,1,6) ); + + print "Pages = $pages : rec = $rec_start Datestamp $vDateStamp $crc\n"; + + $cnt_out = $ob->write( pack("h", 0x06) ); + + #if ($pages == 513 ) { return -1 } + + my @arc_rec_lst; + foreach my $page (1..$pages) { + my $page_sz = 267; + $ob->read_interval(0); + my ($cnt_in, $str) = $ob->read($page_sz); #,3 + printf("len=%s\n",length($str)); + if ($cnt_in!=$page_sz) { + dienice("hmm, dmpaft only got $cnt_in bytes. was expecting $page_sz"); + } + print "Page $page\n"; + #print DMP $str,"\n"; + #print "ACK receipt of page $page\n"; + #$ob->write( pack("h", 0x06) ); + #next; + my $calc_crc = CRC_CCITT($str); + my $crc = unpack "%n", substr($str,265,2); + print "page crc=$crc, calc_crc=$calc_crc\n"; + my $rec_sz = 52; + my $date_prev = 0; + my %hsh; + + foreach my $rec ( 0..4 ) { + if ( ($page == 1) && ($rec < $rec_start ) ) { + next; + } # Find the right starting point... + + my $start_ptr = 1 + ($rec * $rec_sz ); + my $rec_str = substr($str, $start_ptr ,52); + + #print "$start_ptr \t > " . unpack( "h*", $rec_str) . "\n"; + + my $date = substr($rec_str,0,2); + my $date_curr = unpack "s", $date; + + # Check if we have wrapped... + if ( $date_curr < $date_prev ) { + last; + } + $date_prev = $date_curr; + + $hsh{'date_stamp'} = $date_curr; + $hsh{'time_stamp'} = unpack "s", substr($rec_str,2,2); + + $hsh{'day'} = unpack( "c", $date & pack("c",0x1F) ); + $hsh{'month'} = ( $hsh{'date_stamp'} >> 5) & 0xF; + $hsh{'year'} = ( $hsh{'date_stamp'} >> 9) + 2000; + + $hsh{'hour'} = sprintf("%02d", int ( $hsh{'time_stamp'} / 100 )); + + $hsh{'min'} = $hsh{'time_stamp'} - ($hsh{'hour'} * 100); + $hsh{'min'} = sprintf("%02d", $hsh{'min'}); + + $hsh{'time_stamp_fmt'} = "$hsh{'hour'}:$hsh{'min'}:00"; + $hsh{'date_stamp_fmt'} = "$hsh{'year'}_$hsh{'month'}_$hsh{'day'}"; + + #$hsh{'unixtime'} = timelocal(0,$hsh{min}, $hsh{hour}, $hsh{day}, $hsh{month}-1, $hsh{year}-1900); + + $hsh{'Air_Temp'} = unpack("s", substr($rec_str,4,2)) / 10; + $hsh{'Air_Temp_Hi'} = unpack("s", substr($rec_str,6,2)) / 10; + $hsh{'Air_Temp_Lo'} = unpack("s", substr($rec_str,8,2)) / 10; + $hsh{'Rain_Clicks'} = unpack("s", substr($rec_str,10,2)); + $hsh{'Rain_Rate'} = unpack("s", substr($rec_str,12,2)) / 100; # Inches per hour + $hsh{'Barometric_Press'} = unpack("s", substr $rec_str,14,2) / 1000; + $hsh{'Solar'} = unpack("s", substr $rec_str,16,2); # watt/m**2 + $hsh{'Wind_Samples'} = unpack("s", substr $rec_str,18,2); + $hsh{'Air_Temp_Inside'} = unpack("s", substr $rec_str,20,2) / 10; + + $hsh{'Relative_Humidity_Inside'} = unpack("C", substr $rec_str,22,1); + $hsh{'Relative_Humidity'} = unpack("C", substr $rec_str,23,1); + + $hsh{'Wind_Speed'} = unpack("C", substr($rec_str,24,1)); + $hsh{'Wind_Gust_Max'} = unpack("C", substr($rec_str,25,1)); + $hsh{'Wind_Dir_Max'} = unpack("C", substr($rec_str,26,1)); + $hsh{'Wind_Dir'} = unpack("C", substr($rec_str,27,1)); + + $hsh{'UV'} = unpack("C", substr($rec_str,28,1)) / 10; + $hsh{'ET'} = unpack("C", substr($rec_str,29,1)) / 1000; + + $hsh{'Solar_Max'} = unpack("s", substr($rec_str,30,2)); + $hsh{'UV_Max'} = unpack("C", substr($rec_str,32,1)); + + $hsh{'Forecast_Rule'} = unpack("C", substr($rec_str,33,1)); + +# $hsh{'Dew_Point'} = _dew_point($hsh{'Air_Temp'},$hsh{'Relative_Humidity'}); + + # Miscellaneous others omitted for now + + print "date> $hsh{'Air_Temp'} $hsh{'time_stamp'} $hsh{'time_stamp_fmt'} $hsh{'date_stamp'} $hsh{'date_stamp_fmt'}\n"; + #print Dumper \%hsh; + + push @arc_rec_lst, {%hsh}; + } + + #$in = ; # Testing step through facility + #if ($in =~ /q/i ) { $port_obj->write( pack("h", 0x1B) ); last; } + #else { $port_obj->write( pack("h", 0x06) ); } + print "ACK receipt of page\n"; + $ob->write( pack("h", 0x06) ); + + } + close DMP; +} + +package Device::SerialPort; + +sub read_interval +{ + +} diff --git a/dumpraw.pl b/dumpraw.pl new file mode 100755 index 0000000..8e639fc --- /dev/null +++ b/dumpraw.pl @@ -0,0 +1,133 @@ +#!/usr/bin/perl + +use DBI; +use strict; +use Time::HiRes; +use Serial; +use Device::SerialPort; +use Mojo::IOLoop; +use Mojo::IOLoop::Stream; + +my $devname = "/dev/davis"; + +#my $ob = Serial->new ($devname, 19200) || die; +my $ob = Device::SerialPort->new ($devname) || die; + +$ob->user_msg(1); # misc. warnings +$ob->error_msg(1); # hardware and data errors + +$ob->baudrate(19200); +$ob->parity("none"); +#$ob->parity_enable(1); # for any parity except "none" +$ob->databits(8); +$ob->stopbits(1); +$ob->handshake('none'); +$ob->read_const_time(15000); # ultimate timeout (15 seconds) +$ob->write_settings||die"setting failed"; + +$ob->read_char_time(0); +$ob->read_const_time(300); + +my $awake=0; +my $attempts=0; +$ob->write("\n"); # initial wak + +my $awake = 0; +my $attempts; + +while ($awake==0) { + $ob->write("\n"); # wake for real + my ($count, $result) = $ob->read(10); # read up to 10 chars + if ($result eq "\n\r") { + print "awoke on attempt $attempts :)\n"; + $awake=1; + } else { + print "wake error on attempt $attempts :(\n"; + } + $attempts++; + die("failed to wake device - tried $attempts times\n") unless $attempts<6; +} + +$ob->write("STRMON\n"); + +my $end; + +$SIG{TERM} = $SIG{INT} = sub {++$end; print "\nending $end\n";}; + +while (!$end) { + +# print "end: $end\n"; + + my ($count, $l) = $ob->read(255); + next unless $count; + + s|\cM||g; + s|\cJ||g; + s|\cI||g; + + my @l = split /[\cJ\cM]/, $l; + my ($wspeed, $wdir, $val1, $val2, $sort, $batt); + + my @hex; + + for (@l) { + next unless $_; + my ($k, $v) = $_ =~ /^(\d)\s+=\s+([\dabcdef]+)/; + next unless defined $k && defined $v; + my $h = hex $v; + push @hex, $h; + if ($k == 0) { + if (($h & 0xf0) == 0x80) { + $sort = "T"; + } elsif (($h & 0xf0) == 0xa0) { + $sort = 'H'; + } elsif (($h & 0xf0) == 0xe0) { + $sort = 'R'; + } elsif (($h & 0xf0) == 0x50) { + $sort = 'P'; + } else { + $sort = 'W'; # wind only + } + if ($h & 0x08) { # new battery required + $batt = ' NB'; + } + } elsif ($k == 1) { + $wspeed = $h; + } elsif ($k == 2) { + $wdir = int($h * 360 / 255); + } elsif ($k == 3) { + $val2 = $val1 = $h; + } elsif ($k == 4) { + if ($sort eq 'H') { + $val2 = (($h >> 4) << 8) | $val2; + } elsif ($sort eq 'T') { + $val2 = ($h << 8) | $val2; + } else { + $val2 = ($val2 << 8) | $h; + } + } + } + if ($sort) { + printf("%02X ", $_) for (@hex); + if ($sort eq 'T') { + printf "$sort$batt %3d %3d %0.1f\n", $wspeed, $wdir, (($val2/160) - 32) * 5/9; + } elsif ($sort eq 'H') { + printf "$sort$batt %3d %3d %0.1f\n", $wspeed, $wdir, $val2/10; + } elsif ($sort eq 'P') { + printf "$sort$batt %3d %3d %0.2f\n", $wspeed, $wdir, $val2 / 1000; + } elsif ($sort eq 'R') { + printf "$sort$batt %3d %3d $val1\n", $wspeed, $wdir; + } elsif ($sort eq 'W') { + printf "$sort$batt %3d %3d\n", $wspeed, $wdir, $val2; + } else { + print "\n"; + } + } +} + +$ob->write("STRMOFF\n"); +$ob->read(255); +exit(0); + + + diff --git a/loop.pl b/loop.pl new file mode 100755 index 0000000..ef0c4e0 --- /dev/null +++ b/loop.pl @@ -0,0 +1,252 @@ +#!/usr/bin/perl +use strict; + +use v5.10.1; + +use DBI; +use Serial; +use Mojo::IOLoop; +use Mojo::IOLoop::Stream; + +my $devname = "/dev/davis"; +my $tid; +my $rid; +my $count; +my $state = "ready"; +my $buf; + +our @crc_table = ( + 0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0 + ); + +our %bar_trend; + +$bar_trend{-60} = "Falling Rapidly"; +$bar_trend{196} = "Falling Rapidly"; +$bar_trend{-20} = "Falling Slowly"; +$bar_trend{236} = "Falling Slowly"; +$bar_trend{0} = "Steady"; +$bar_trend{20} = "Rising Slowly"; +$bar_trend{60} = "Rising Rapidly"; + +#$SIG{TERM} = $SIG{INT} = sub {Mojo::IOLoop->reset if Mojo::IOLoop->is_running && !$DB::VERSION}; + +my $s = do_open($devname); +start_loop(); + +Mojo::IOLoop->start unless Mojo::IOLoop->is_running; + +exit 0; + +sub on_read +{ + my ($str, $d) = @_; + $buf .= $d; + $d =~ s/([\%\x00-\x1f\x7f-\xff])/sprintf("%%%02X", ord($1))/eg; +# say "read added '$d' buf lth=" . length $buf; + if ($state eq 'waitnl' && $buf =~ /[\cJ\cM]+/) { + undef $tid; + undef $buf; + $s->write("LOOP 1\n"); + chgstate("waitloop"); + } elsif ($state eq "waitloop") { + if ($buf =~ /\x06/) { + chgstate('waitlooprec'); + undef $buf; + } + } elsif ($state eq 'waitlooprec') { + if (length $buf >= 99) { + say "got loop record\n"; + chgstate(''); + process($buf); + undef $buf; + } + } +} + +sub start_loop +{ + say "writing \\n"; + $s->write("\n"); + $tid = Mojo::IOLoop->timer(0.6 => sub {say "writing \\n"; $s->write("\n")}); + chgstate("waitnl"); +} + +sub chgstate +{ + say "state '$state' -> '$_[0]'"; + $state = $_[0]; +} + +sub do_open +{ + my $name = shift; + my $ob = Serial->new($name, 19200) || die "$name $!\n"; + say "streaming $name fileno(", fileno($ob), ")"; + my $str = Mojo::IOLoop::Stream->new($ob); + $str->on(error=>sub {say "serial $_[1]"; undef $s; Mojo::IOLoop->reset;}); + $str->on(close=>sub {say "serial closing"; undef $s; Mojo::IOLoop->reset;}); + $str->on(timeout=>sub {say "serial timeout";}); + $str->on(read=>sub {on_read(@_)}); + $str->start; + + $rid = Mojo::IOLoop->recurring(2.5 => sub {start_loop() if !$state || $state eq "waitnl";}); + + return $str; +} + +sub process +{ + my $blk = shift; + my $loo = substr $blk,0,3; + unless ( $loo eq 'LOO') { + say "Block invalid loo -> $loo"; return; + } + + my $t; + my %hsh; + + + #$hsh{'next_rec'} = unpack("s", substr $blk,5,2); + + $hsh{'Barometric_Trend'} = unpack("C", substr $blk,3,1); + $hsh{'Barometric_Trend_txt'} = $bar_trend{$hsh{'Barometric_Trend'}}; + $t = unpack("s", substr $blk,7,2) / 1000; + # $hsh{'Barometric_Press_hg'} = $t; + $hsh{'Barometric_Press_mb'} = sprintf("%.2f",$t*33.8637526); + + + $t = unpack("s", substr $blk,9,2) / 10; + # $hsh{'Air_Temp_Inside_f'} = $t; + $hsh{'Air_Temp_Inside_c'} = sprintf("%.1f",($t - 32) * 5/9); + my $tf = unpack("s", substr $blk,12,2) / 10; + # $hsh{'Air_Temp_Outside_f'} = $tf; + $hsh{'Air_Temp_Outside_c'} = sprintf("%.1f",($tf - 32) * 5/9); + + $hsh{'Wind_Speed_mph'} = unpack("C", substr $blk,14,1); + # $hsh{'Wind_Speed_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_mph'}*0.44704); + $hsh{'Wind_Speed_10min_Avg_mph'} = unpack("C", substr $blk,15,1); + # $hsh{'Wind_Speed_10min_Avg_mps'} = sprintf("%.1f",$hsh{'Wind_Speed_10min_Avg_mph'}*0.44704); + $hsh{'Wind_Dir'} = unpack("s", substr $blk,16,2); + + + $hsh{'Humidity_Outside'} = unpack("C", substr $blk,33,1); + $hsh{'Humidity_Inside'} = unpack("C", substr $blk,11,1); + $hsh{'Dew_Point'} = dew_point($tf, $hsh{'Humidity_Outside'}); + + # $hsh{'UV'} = unpack("C", substr $blk,43,1); + # $hsh{'Solar'} = unpack("s", substr $blk,44,2); # watt/m**2 + + $hsh{'Rain_Rate'} = (unpack("s", substr $blk,41,2) / 100) * 25.4; # Inches per hr converted to mm + $hsh{'Rain_Storm'} = (unpack("s", substr $blk,46,2) / 100) * 25.4; # Inches per storm + #$hsh{'Storm_Date'} = unpack("s", substr $blk,48,2); # Need to parse data (not sure what this is) + $hsh{'Rain_Day'} = (unpack("s", substr $blk,50,2)/100) * 25.4; + $hsh{'Rain_Month'} = (unpack("s", substr $blk,52,2)/100) * 25.4; + $hsh{'Rain_Year'} = (unpack("s", substr $blk,54,2)/100) * 25.4; + + $hsh{'ET_Day'} = unpack("s", substr $blk,56,2)/1000; + $hsh{'ET_Month'} = unpack("s", substr $blk,58,2)/100; + $hsh{'ET_Year'} = unpack("s", substr $blk,60,2)/100; + + #$hsh{'Alarms_Inside'} = unpack("b8", substr $blk,70,1); + #$hsh{'Alarms_Rain'} = unpack("b8", substr $blk,70,1); + #$hsh{'Alarms_Outside'} = unpack("b8", substr $blk,70,1); + + $hsh{'Batt_Transmitter'} = unpack("C", substr $blk,86,1); # * 0.005859375 + $hsh{'Batt_Console'} = unpack("s", substr $blk,87,2) * 0.005859375; + + $hsh{'Forecast_Icon'} = unpack("C", substr $blk,89,1); + $hsh{'Forecast_Rule'} = unpack("C", substr $blk,90,1); + + $hsh{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) ); + $hsh{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/; + $hsh{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) ); + $hsh{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/; + + #my $nl = ord substr $blk,95,1; + #my $cr = ord substr $blk,96,1; + + my $crc = unpack "%n", substr($blk,97,2); + my $crc_calc = CRC_CCITT($blk); + + if ($crc_calc==0) { + say "inside: $hsh{Air_Temp_Inside_c} degC $hsh{Humidity_Inside}\% outside: $hsh{Air_Temp_Outside_c} degC $hsh{Humidity_Outside}\% wind: $hsh{Wind_Speed_mph} $hsh{Wind_Dir} deg $hsh{Barometric_Press_mb} mB"; + + } else { + print "CRC check failed for LOOP data!\n"; + return 1; + } + #delete @hsh{'crc', 'crc_calc', 'next_rec'}; + #delete($hsh{crc})||die"cant delete crc"; + #delete($hsh{crc_calc})||die"cant delete crc_calc"; + #delete($hsh{next_rec})||die"cant delete next_rec"; +} + +sub dew_point +{ + my $temp = shift @_; + my $rh = shift @_; + + # Using the simplified approximation for dew point + # Accurate to 1 degree C for humidities > 50 % + # http://en.wikipedia.org/wiki/Dew_point + + my $dew_point = $temp - ( (100 - $rh)/5 ); + + return $dew_point; +} + +sub CRC_CCITT +{ + # Expects packed data... + my $data_str = shift @_; + + my $crc = 0; + my @lst = split //, $data_str; + foreach my $data (@lst) { + my $data = unpack("c",$data); + + my $crc_prev = $crc; + my $index = $crc >> 8 ^ $data; + my $lhs = $crc_table[$index]; + #print "lhs=$lhs, crc=$crc\n"; + my $rhs = ($crc << 8) & 0xFFFF; + $crc = $lhs ^ $rhs; + + + } + + return $crc; +} + -- 2.43.0