9 use Mojo::IOLoop::Stream;
10 use Mojo::JSON qw(decode_json encode_json);
12 my $devname = "/dev/davis";
13 my $rain_mult = 0.1; # 0.1 or 0.2 mm or 0.01 inches
23 0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
24 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
25 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
26 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
27 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
28 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
29 0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
30 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
31 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823,
32 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
33 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12,
34 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
35 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
36 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
37 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70,
38 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
39 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
40 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
41 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
42 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
43 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
44 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405,
45 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
46 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
47 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
48 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3,
49 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
50 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92,
51 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
52 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1,
53 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
54 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0
59 $bar_trend{-60} = "Falling Rapidly";
60 $bar_trend{196} = "Falling Rapidly";
61 $bar_trend{-20} = "Falling Slowly";
62 $bar_trend{236} = "Falling Slowly";
63 $bar_trend{0} = "Steady";
64 $bar_trend{20} = "Rising Slowly";
65 $bar_trend{60} = "Rising Rapidly";
67 #$SIG{TERM} = $SIG{INT} = sub {Mojo::IOLoop->stop if Mojo::IOLoop->is_running && !$DB::VERSION};
69 my $s = do_open($devname);
72 Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
82 $d =~ s/([\%\x00-\x1f\x7f-\xff])/sprintf("%%%02X", ord($1))/eg;
83 # say "read added '$d' buf lth=" . length $buf if $dbg;
84 if ($state eq 'waitnl' && $buf =~ /[\cJ\cM]+/) {
87 $s->write("LOOP 1\n");
89 } elsif ($state eq "waitloop") {
91 chgstate('waitlooprec');
94 } elsif ($state eq 'waitlooprec') {
95 if (length $buf >= 99) {
96 say "got loop record\n" if $dbg;
107 say "writing \\n" if $dbg;
110 $tid = Mojo::IOLoop->timer(0.6 => sub {say "writing \\n" if $dbg; $s->write("\n")});
117 say "state '$state' -> '$_[0]'" if $dbg;
125 my $ob = Serial->new($name, 19200) || die "$name $!\n";
126 say "streaming $name fileno(", fileno($ob), ")" if $dbg;
128 my $str = Mojo::IOLoop::Stream->new($ob);
129 $str->on(error=>sub {say "serial $_[1]"; undef $s; Mojo::IOLoop->reset;});
131 $str->on(close=>sub {say "serial closing"; undef $s; Mojo::IOLoop->reset;});
132 $str->on(timeout=>sub {say "serial timeout";});
133 $str->on(read=>sub {on_read(@_)});
136 $rid = Mojo::IOLoop->recurring(2.5 => sub {start_loop() if !$state || $state eq "waitnl";});
144 my $loo = substr $blk,0,3;
145 unless ( $loo eq 'LOO') {
146 say "Block invalid loo -> $loo" if $dbg; return;
153 #$h{'next_rec'} = unpack("s", substr $blk,5,2);
155 $h{'Barometric_Trend'} = unpack("C", substr $blk,3,1);
156 $h{'Barometric_Trend_txt'} = $bar_trend{$h{'Barometric_Trend'}};
157 $t = unpack("s", substr $blk,7,2) / 1000;
158 $h{'Barometric_Press_mb'} = sprintf("%.0f",$t*33.8637526);
161 $t = unpack("s", substr $blk,9,2) / 10;
162 $h{'Air_Temp_Inside_c'} = sprintf("%.1f",($t - 32) * 5/9);
163 my $tf = unpack("s", substr $blk,12,2) / 10;
164 $h{'Air_Temp_Outside_c'} = sprintf("%.1f",($tf - 32) * 5/9);
166 $h{'Wind_Speed_mph'} = unpack("C", substr $blk,14,1);
167 $h{'Wind_Speed_mps'} = sprintf("%.1f",$h{'Wind_Speed_mph'}*0.44704);
168 $h{'Wind_Speed_10min_Avg_mph'} = unpack("C", substr $blk,15,1);
169 $h{'Wind_Speed_10min_Avg_mps'} = sprintf("%.1f",$h{'Wind_Speed_10min_Avg_mph'}*0.44704);
170 $h{'Wind_Dir'} = unpack("s", substr $blk,16,2);
173 $h{'Humidity_Outside'} = unpack("C", substr $blk,33,1);
174 $h{'Humidity_Inside'} = unpack("C", substr $blk,11,1);
175 $h{'Dew_Point'} = dew_point($h{Air_Temp_Outside_c}, $h{'Humidity_Outside'});
177 $h{'UV'} = unpack("C", substr $blk,43,1);
178 $h{'Solar'} = unpack("s", substr $blk,44,2); # watt/m**2
180 $h{'Rain_Rate'} = unpack("s", substr $blk,41,2) * $rain_mult;
181 $h{'Rain_Day'} = unpack("s", substr $blk,50,2) * $rain_mult;
182 $h{'Rain_Month'} = unpack("s", substr $blk,52,2) * $rain_mult;
183 $h{'Rain_Year'} = unpack("s", substr $blk,54,2) * $rain_mult;
185 $h{'ET_Day'} = unpack("s", substr $blk,56,2)/1000;
186 $h{'ET_Month'} = unpack("s", substr $blk,58,2)/100;
187 $h{'ET_Year'} = unpack("s", substr $blk,60,2)/100;
189 #$h{'Alarms_Inside'} = unpack("b8", substr $blk,70,1);
190 #$h{'Alarms_Rain'} = unpack("b8", substr $blk,70,1);
191 #$h{'Alarms_Outside'} = unpack("b8", substr $blk,70,1);
193 $h{'Batt_TX_OK'} = (unpack("C", substr $blk,86,1)+0) ^ 1;
194 $h{'Batt_Console'} = unpack("s", substr $blk,87,2) * 0.005859375;
196 $h{'Forecast_Icon'} = unpack("C", substr $blk,89,1);
197 $h{'Forecast_Rule'} = unpack("C", substr $blk,90,1);
199 $h{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) );
200 $h{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/;
201 $h{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) );
202 $h{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/;
204 #my $nl = ord substr $blk,95,1;
205 #my $cr = ord substr $blk,96,1;
207 my $crc = unpack "%n", substr($blk,97,2);
208 my $crc_calc = CRC_CCITT($blk);
211 my $o = gen_hash_diff($last_reading, \%h);
213 if (time % 60 == 0) {
214 my $oo = {t => time, r =>\%h};
215 say encode_json($oo);
217 my $oo = {t => time, r =>$o};
218 say encode_json($oo);
221 say "CRC check failed for LOOP data!";
224 #delete @h{'crc', 'crc_calc', 'next_rec'};
225 #delete($h{crc})||die"cant delete crc";
226 #delete($h{crc_calc})||die"cant delete crc_calc";
227 #delete($h{next_rec})||die"cant delete next_rec";
237 while (my ($k, $v) = each %$now) {
238 if ($last->{$k} ne $now->{$k}) {
243 return $count ? \%o : undef;
251 # Using the simplified approximation for dew point
252 # Accurate to 1 degree C for humidities > 50 %
253 # http://en.wikipedia.org/wiki/Dew_point
255 my $dew_point = $temp - ( (100 - $rh)/5 );
262 # Expects packed data...
263 my $data_str = shift @_;
266 my @lst = split //, $data_str;
267 foreach my $data (@lst) {
268 my $data = unpack("c",$data);
271 my $index = $crc >> 8 ^ $data;
272 my $lhs = $crc_table[$index];
273 #print "lhs=$lhs, crc=$crc\n";
274 my $rhs = ($crc << 8) & 0xFFFF;