8 use Mojo::IOLoop::Stream;
9 use Mojo::JSON qw(decode_json encode_json);
13 my $devname = "/dev/davis";
14 my $rain_mult = 0.2; # 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};
77 my $dlog = SMGLog->new("day");
80 my $s = do_open($devname);
83 Mojo::IOLoop->recurring(0.1 => sub { $dlog->flushall });
85 Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
93 $d =~ s/([\%\x00-\x1f\x7f-\xff])/sprintf("%%%02X", ord($1))/eg;
94 dbg "read added '$d' buf lth=" . length $buf if isdbg('raw');
95 if ($state eq 'waitnl' && $buf =~ /[\cJ\cM]+/) {
96 Mojo::IOLoop->remove($tid);
98 $s->write("LPS 1 1\n");
100 } elsif ($state eq "waitloop") {
101 if ($buf =~ /\x06/) {
102 chgstate('waitlooprec');
105 } elsif ($state eq 'waitlooprec') {
106 if (length $buf >= 99) {
107 dbg "got loop record\n" if isdbg('chan');
118 dbg "writing \\n" if isdbg('chan');
121 $tid = Mojo::IOLoop->recurring(0.6 => sub {dbg "writing \\n" if isdbg('chan'); $s->write("\n")});
128 dbg "state '$state' -> '$_[0]'" if isdbg('chan');
136 my $ob = Serial->new($name, 19200) || die "$name $!\n";
137 dbg "streaming $name fileno(", fileno($ob), ")" if isdbg('chan');
139 my $str = Mojo::IOLoop::Stream->new($ob);
140 $str->on(error=>sub {dbg "serial $_[1]"; undef $s; Mojo::IOLoop->reset;});
142 $str->on(close=>sub {dbg "serial closing"; undef $s; Mojo::IOLoop->reset;});
143 $str->on(timeout=>sub {dbg "serial timeout";});
144 $str->on(read=>sub {on_read(@_)});
147 $rid = Mojo::IOLoop->recurring(2.5 => sub {
148 start_loop() if !$state || $state eq "waitnl";
154 our $last_min = time;
155 our $last_hour = time;
163 my $loo = substr $blk,0,3;
164 unless ( $loo eq 'LOO') {
165 dbg "Block invalid loo -> $loo" if isdbg('chan'); return;
172 $h{Pressure_Trend} = unpack("C", substr $blk,3,1);
173 $h{Pressure_Trend_txt} = $bar_trend{$h{'Pressure_Trend'}};
174 $t = unpack("s", substr $blk,7,2) / 1000;
175 $h{Pressure} = sprintf("%.0f",in2mb($t))+0;
177 $t = unpack("s", substr $blk,9,2) / 10;
178 $h{Temp_In} = sprintf("%.1f", f2c($t))+0;
180 $t = unpack("s", substr $blk,12,2) / 10;
181 $h{Temp_Out} = sprintf("%.1f", f2c($t))+0;
183 $t = unpack("C", substr $blk,14,1);
184 $h{Wind} = sprintf("%.1f",mph2mps($t))+0;
185 $h{Dir} = unpack("s", substr $blk,16,2)+0;
187 my $wind = {w => $h{Wind}, d => $h{Dir}};
190 $h{'Humidity_Out'} = unpack("C", substr $blk,33,1)+0;
191 $h{'Humidity_In'} = unpack("C", substr $blk,11,1)+0;
193 $t = unpack("C", substr $blk,43,1)+0;
194 $h{'UV'} = $t unless $t >= 255;
195 $t = unpack("s", substr $blk,44,2)+0; # watt/m**2
196 $h{'Solar'} = $t unless $t >= 32767;
198 $h{'Rain_Rate'} = sprintf("%0.1f",unpack("s", substr $blk,41,2) * $rain_mult)+0;
199 $h{'Rain_Day'} = sprintf("%0.1f", unpack("s", substr $blk,50,2) * $rain_mult)+0;
201 # what sort of packet is it?
203 $t = unpack("C", substr $blk,4,1);
207 $t = unpack("C", substr $blk,18,2);
208 # $h{Wind_Avg_10} = sprintf("%.1f",mph2mps($t/10))+0;
209 $t = unpack("C", substr $blk,20,2);
210 # $h{Wind_Avg_2} = sprintf("%.1f",mph2mps($t/10))+0;
211 $t = unpack("C", substr $blk,22,2);
212 # $h{Wind_Gust_10} = sprintf("%.1f",mph2mps($t/10))+0;
214 # $h{Dir_Avg_10} = unpack("C", substr $blk,24,2)+0;
215 $t = unpack("C", substr $blk,30,2);
216 $h{Dew_Point} = sprintf("%0.1f", f2c($t))+0;
221 $t = unpack("C", substr $blk,15,1);
222 # $h{Wind_Avg_10} = sprintf("%.1f",mph2mps($t))+0;
223 $h{'Dew_Point'} = sprintf("%0.1f", dew_point($h{Temp_Out}, $h{'Humidity_Out'}))+0;
225 $h{'Rain_Month'} = sprintf("%0.1f", unpack("s", substr $blk,52,2) * $rain_mult)+0;
226 $h{'Rain_Year'} = sprintf("%0.1f", unpack("s", substr $blk,54,2) * $rain_mult)+0;
229 $h{'Batt_TX_OK'} = (unpack("C", substr $blk,86,1)+0) ^ 1;
230 $h{'Batt_Console'} = sprintf("%0.2f", unpack("s", substr $blk,87,2) * 0.005859375)+0;
231 $h{'Forecast_Icon'} = unpack("C", substr $blk,89,1);
232 $h{'Forecast_Rule'} = unpack("C", substr $blk,90,1);
234 $h{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) );
235 $h{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/;
236 $h{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) );
237 $h{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/;
240 my $crc = unpack "%n", substr($blk,97,2);
241 my $crc_calc = CRC_CCITT($blk);
244 my $o = gen_hash_diff($last_reading, \%h);
249 if ($t >= $last_min + 60) {
250 my $a = average(@min);
253 $h{Wind_1m} = sprintf("%0.1f", $a->{w})+0;
254 $h{Dir_1m} = sprintf("%0.0f", $a->{d})+0;
256 $j = encode_json(\%h);
257 $s = qq|{"t":$t,"m":$j}|;
259 if ($t >= $last_hour + 3600) {
260 my $a = average(@hour);
264 $h{Wind_1h} = sprintf("%0.1f", $a->{w})+0;
265 $h{Dir_1h} = sprintf("%0.0f", $a->{d})+0;
267 $j = encode_json(\%h);
268 $s = qq|{"t":$t,"h":$j}|;
272 $j = encode_json($o);
273 $s = qq|{"t":$t,"r":$j}|;
280 dbg "CRC check failed for LOOP data!";
291 while (my ($k, $v) = each %$now) {
292 if ($last->{$k} ne $now->{$k}) {
297 return $count ? \%o : undef;
305 # Using the simplified approximation for dew point
306 # Accurate to 1 degree C for humidities > 50 %
307 # http://en.wikipedia.org/wiki/Dew_point
309 my $dewpoint = $temp - ((100 - $rh) / 5);
311 # this is the more complete one (which doesn't work)
315 #my $ytrh = log(($rh/100) + ($b * $temp) / ($c + $temp));
316 #my $dewpoint = ($c * $ytrh) / ($b - $ytrh);
323 # Expects packed data...
324 my $data_str = shift @_;
327 my @lst = split //, $data_str;
328 foreach my $data (@lst) {
329 my $data = unpack("c",$data);
332 my $index = $crc >> 8 ^ $data;
333 my $lhs = $crc_table[$index];
334 #print "lhs=$lhs, crc=$crc\n";
335 my $rhs = ($crc << 8) & 0xFFFF;
346 return ($_[0] - 32) * 5/9;
351 return $_[0] * 0.44704;
356 return $_[0] * 33.8637526;
365 while (my ($k, $v) = each %$r) {
370 while (my ($k, $v) = each %out) {