8 use Mojo::IOLoop::Stream;
9 use Mojo::JSON qw(decode_json encode_json);
13 my $devname = "/dev/davis";
14 my $rain_mult = 0.1; # 0.1 or 0.2 mm or 0.01 inches
24 0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
25 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
26 0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
27 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
28 0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
29 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
30 0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
31 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
32 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823,
33 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
34 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12,
35 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
36 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
37 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
38 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70,
39 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
40 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
41 0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
42 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
43 0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
44 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
45 0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405,
46 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
47 0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
48 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
49 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3,
50 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
51 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92,
52 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
53 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1,
54 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
55 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0
60 $bar_trend{-60} = "Falling Rapidly";
61 $bar_trend{196} = "Falling Rapidly";
62 $bar_trend{-20} = "Falling Slowly";
63 $bar_trend{236} = "Falling Slowly";
64 $bar_trend{0} = "Steady";
65 $bar_trend{20} = "Rising Slowly";
66 $bar_trend{60} = "Rising Rapidly";
68 #$SIG{TERM} = $SIG{INT} = sub {Mojo::IOLoop->stop if Mojo::IOLoop->is_running && !$DB::VERSION};
70 my $dlog = SMGLog->new("day");
73 my $s = do_open($devname);
76 Mojo::IOLoop->recurring(0.1 => sub { $dlog->flushall });
78 Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
86 $d =~ s/([\%\x00-\x1f\x7f-\xff])/sprintf("%%%02X", ord($1))/eg;
87 # say "read added '$d' buf lth=" . length $buf if $dbg;
88 if ($state eq 'waitnl' && $buf =~ /[\cJ\cM]+/) {
89 Mojo::IOLoop->remove($tid);
91 $s->write("LPS 1 1\n");
93 } elsif ($state eq "waitloop") {
95 chgstate('waitlooprec');
98 } elsif ($state eq 'waitlooprec') {
99 if (length $buf >= 99) {
100 say "got loop record\n" if $dbg;
111 say "writing \\n" if $dbg;
114 $tid = Mojo::IOLoop->recurring(0.6 => sub {say "writing \\n" if $dbg; $s->write("\n")});
121 say "state '$state' -> '$_[0]'" if $dbg;
129 my $ob = Serial->new($name, 19200) || die "$name $!\n";
130 say "streaming $name fileno(", fileno($ob), ")" if $dbg;
132 my $str = Mojo::IOLoop::Stream->new($ob);
133 $str->on(error=>sub {say "serial $_[1]"; undef $s; Mojo::IOLoop->reset;});
135 $str->on(close=>sub {say "serial closing"; undef $s; Mojo::IOLoop->reset;});
136 $str->on(timeout=>sub {say "serial timeout";});
137 $str->on(read=>sub {on_read(@_)});
140 $rid = Mojo::IOLoop->recurring(2.5 => sub {
141 start_loop() if !$state || $state eq "waitnl";
152 my $loo = substr $blk,0,3;
153 unless ( $loo eq 'LOO') {
154 say "Block invalid loo -> $loo" if $dbg; return;
161 $h{Pressure_Trend} = unpack("C", substr $blk,3,1);
162 $h{Pressure_Trend_txt} = $bar_trend{$h{'Pressure_Trend'}};
163 $t = unpack("s", substr $blk,7,2) / 1000;
164 $h{Pressure} = sprintf("%.0f",in2mb($t))+0;
166 $t = unpack("s", substr $blk,9,2) / 10;
167 $h{Temp_In} = sprintf("%.1f", f2c($t))+0;
169 $t = unpack("s", substr $blk,12,2) / 10;
170 $h{Temp_Out} = sprintf("%.1f", f2c($t))+0;
172 $t = unpack("C", substr $blk,14,1);
173 $h{Wind} = sprintf("%.1f",mph2mps($t))+0;
174 $h{Dir} = unpack("s", substr $blk,16,2)+0;
177 $h{'Humidity_Out'} = unpack("C", substr $blk,33,1)+0;
178 $h{'Humidity_In'} = unpack("C", substr $blk,11,1)+0;
180 $t = unpack("C", substr $blk,43,1)+0;
181 $h{'UV'} = $t unless $t >= 255;
182 $t = unpack("s", substr $blk,44,2)+0; # watt/m**2
183 $h{'Solar'} = $t unless $t >= 32767;
185 $h{'Rain_Rate'} = sprintf("%0.1f",unpack("s", substr $blk,41,2) * $rain_mult)+0;
186 $h{'Rain_Day'} = sprintf("%0.1f", unpack("s", substr $blk,50,2) * $rain_mult)+0;
188 # what sort of packet is it?
190 $t = unpack("C", substr $blk,4,1);
194 $t = unpack("C", substr $blk,18,2);
195 $h{Wind_Avg_10} = sprintf("%.1f",mph2mps($t/10))+0;
196 $t = unpack("C", substr $blk,20,2);
197 $h{Wind_Avg_2} = sprintf("%.1f",mph2mps($t/10))+0;
198 $t = unpack("C", substr $blk,22,2);
199 $h{Wind_Gust_10} = sprintf("%.1f",mph2mps($t/10))+0;
201 $h{Dir_Avg_10} = unpack("C", substr $blk,24,2)+0;
202 $t = unpack("C", substr $blk,30,2);
203 $h{Dew_Point} = sprintf("%0.1f", f2c($t))+0;
208 $t = unpack("C", substr $blk,15,1);
209 $h{Wind_Avg_10} = sprintf("%.1f",mph2mps($t))+0;
210 $h{'Dew_Point'} = sprintf("%0.1f", dew_point($h{Temp_Out}, $h{'Humidity_Out'}))+0;
212 $h{'Rain_Month'} = sprintf("%0.1f", unpack("s", substr $blk,52,2) * $rain_mult)+0;
213 $h{'Rain_Year'} = sprintf("%0.1f", unpack("s", substr $blk,54,2) * $rain_mult)+0;
216 $h{'Batt_TX_OK'} = (unpack("C", substr $blk,86,1)+0) ^ 1;
217 $h{'Batt_Console'} = sprintf("%0.2f", unpack("s", substr $blk,87,2) * 0.005859375)+0;
218 $h{'Forecast_Icon'} = unpack("C", substr $blk,89,1);
219 $h{'Forecast_Rule'} = unpack("C", substr $blk,90,1);
221 $h{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) );
222 $h{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/;
223 $h{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) );
224 $h{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/;
227 my $crc = unpack "%n", substr($blk,97,2);
228 my $crc_calc = CRC_CCITT($blk);
231 my $o = gen_hash_diff($last_reading, \%h);
236 if ($t >= $last_time + 60) {
237 $j = encode_json(\%h);
238 $s = qq|{"t":$t,"m":$j}|;
241 $j = encode_json($o);
242 $s = qq|{"t":$t,"r":$j}|;
249 say "CRC check failed for LOOP data!";
261 while (my ($k, $v) = each %$now) {
262 if ($last->{$k} ne $now->{$k}) {
267 return $count ? \%o : undef;
275 # Using the simplified approximation for dew point
276 # Accurate to 1 degree C for humidities > 50 %
277 # http://en.wikipedia.org/wiki/Dew_point
279 my $dewpoint = $temp - ((100 - $rh) / 5);
281 # this is the more complete one (which doesn't work)
285 #my $ytrh = log(($rh/100) + ($b * $temp) / ($c + $temp));
286 #my $dewpoint = ($c * $ytrh) / ($b - $ytrh);
293 # Expects packed data...
294 my $data_str = shift @_;
297 my @lst = split //, $data_str;
298 foreach my $data (@lst) {
299 my $data = unpack("c",$data);
302 my $index = $crc >> 8 ^ $data;
303 my $lhs = $crc_table[$index];
304 #print "lhs=$lhs, crc=$crc\n";
305 my $rhs = ($crc << 8) & 0xFFFF;
316 return ($_[0] - 32) * 5/9;
321 return $_[0] * 0.44704;
326 return $_[0] * 33.8637526;