File Coverage

blib/lib/Device/VantagePro.pm
Criterion Covered Total %
statement 27 405 6.6
branch 0 88 0.0
condition 0 30 0.0
subroutine 9 32 28.1
pod 14 20 70.0
total 50 575 8.7


line stmt bran cond sub pod time code
1             package Device::VantagePro;
2              
3 1     1   48077 use 5.008008;
  1         5  
  1         42  
4 1     1   7 use strict;
  1         2  
  1         37  
5 1     1   6 use warnings;
  1         7  
  1         98  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             our $VERSION = '0.25';
12              
13             #-#use Win32::SerialPort qw(:STAT 0.19 );
14 1     1   1392 use Device::SerialPort qw(:STAT 0.19 );
  1         48357  
  1         285  
15              
16 1     1   1076 use Time::HiRes qw(usleep gettimeofday time);
  1         1823  
  1         5  
17 1     1   1382 use Data::Dumper;
  1         28938  
  1         79  
18              
19 1     1   9 use POSIX qw(:errno_h :fcntl_h strftime);
  1         1  
  1         10  
20              
21 1     1   1743 use Time::Local;
  1         1930  
  1         4061  
22              
23             our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
24             our @EXPORT_OK = qw();
25             our @EXPORT = qw();
26              
27             our $Verbose = 0;
28              
29             # - - - - - - - - - - - - - - - -
30             sub new
31             {
32 0     0 1   my $caller = shift @_;
33              
34             # In case someone wants to sub-class
35 0           my $caller_is_obj = ref($caller);
36 0   0       my $class = $caller_is_obj || $caller;
37              
38             # Passing reference or hash
39 0           my %arg_hsh;
40 0 0         if ( ref($_[0]) eq "HASH" ) { %arg_hsh = %{ shift @_ } }
  0            
  0            
41 0           else { %arg_hsh = @_ }
42              
43 0   0       my $port = $arg_hsh{'port'} || "/dev/ttyS0";
44              
45             #my $conf = $arg_hsh{'conf'} || 'Conf.ini';
46            
47             #my $port_obj = new Win32::SerialPort ($port) || die "Can't open $port: $^E\n";
48 0   0       my $port_obj = new Device::SerialPort ($port) || die "Can't open $port: $^E\n";
49            
50 0   0       my $baudrate = $arg_hsh{baudrate} || 19200;
51 0   0       my $parity = $arg_hsh{parity} || "none";
52 0   0       my $databits = $arg_hsh{databits} || 8;
53 0   0       my $stopbits = $arg_hsh{stopbits} || 1;
54              
55             # After new, must check for failure
56 0           $port_obj->baudrate($baudrate);
57 0           $port_obj->parity($parity);
58 0           $port_obj->databits($databits);
59 0           $port_obj->stopbits($stopbits);
60             #-# $port_obj->read_interval(1); # max time between read char (milliseconds) Not in Device::SerialPort
61            
62 0           $port_obj->read_const_time(10000); # total = (avg * bytes) + const
63            
64             #$port_obj->handshake("rts");
65             #$port_obj->buffers(4096, 4096);
66              
67 0 0         $port_obj->write_settings || warn 'Write Settings Failed';
68              
69             #$port_obj->save($conf);
70              
71 0 0         unless ($port_obj) { die "Can't change Device_Control_Block: $^E\n"; }
  0            
72              
73 0   0       my ($BlockingFlags, $InBytes, $OutBytes, $LatchErrorFlags) = $port_obj->status
74             || warn "could not get port status\n";
75              
76 0 0         if ($BlockingFlags)
77             {
78             #warn "Port is blocked $BlockingFlags, $InBytes, $OutBytes, $LatchErrorFlags\n";
79             }
80              
81 0           $port_obj->purge_all(); # these don't seem to work but try anyway.
82 0           $port_obj->purge_rx();
83            
84             # The object data structure
85 0           my $self = bless {
86             'arg_hsh' => { %arg_hsh },
87             'port_obj' => $port_obj,
88             'loop_cnt' => 0,
89             }, $class;
90            
91             # if ( $self->wake_up() ) { print "Station found ready for communications\n" }
92            
93 0           return $self;
94             }
95              
96             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
97             sub wake_up
98             {
99 0     0 1   my $self = shift @_;
100            
101 0           foreach (1..3)
102             {
103 0           my $cnt_out = $self->{'port_obj'}->write("\n");
104 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
105 0           my ($cnt_in, $str) = $self->read(2);
106            
107 0 0         if ($str eq "\n\r" )
108             {
109 0 0         print "Success on Wakeup $_\n" if $Verbose;
110 0           return 1;
111             }
112            
113 0           warn "Not responding to Wakeup\n";
114            
115 0           usleep 1200000; # As per page 5 of VantagePro Doc
116             }
117              
118 0           warn("Could not unit wake up");
119 0           return -1; # fail
120             }
121              
122             # - - - - - - - - - - - - - - - - - - - - - - - - - -
123             sub plug_test
124             {
125 0     0 0   my $self = shift @_;
126              
127 0           my $port_obj = $self->{'port_obj'};
128            
129 0           my $str = "TEST\n";
130            
131 0           print "Sending $str";
132 0           my $cnt_out = $port_obj->write($str);
133 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
134            
135 0           my ($cnt_in, $str_in) = $port_obj->read(8);
136              
137 0           print "returned: $cnt_in, $str_in";
138            
139 0           return $str;
140             }
141              
142             # - - - - - - - - - - - - - - - - - - - - - - - - - -
143             sub do_dmpaft
144             {
145 0     0 1   my $self = shift @_;
146              
147 0           my $vDateStamp = shift @_;
148 0           my $vTimeStamp = shift @_;
149              
150             # If not date/time stamp then assume 0 which will down load the entire archive
151 0 0         unless ( $vDateStamp ) { $vDateStamp = 0 }
  0            
152 0 0         unless ( $vTimeStamp ) { $vTimeStamp = 0 }
  0            
153            
154 0           my $port_obj = $self->{'port_obj'};
155            
156 0           my $datetime = pack("ss",$vDateStamp, $vTimeStamp);
157            
158 0           my $crc = CRC_CCITT($datetime);
159 0           my $cmd = pack("ssn",$vDateStamp,$vTimeStamp,$crc);
160              
161             #-----------------------
162             #my $str = unpack("H*", $cmd);
163             #$str =~ s/(\w{2})/$1 /g;
164             # Documentation is wrong! The example should be <0xC6><0x06><0xA2><0x03> in section X
165             #print "cmd : $str \n";exit;
166             #-----------------------
167              
168 0           sleep 2; # Needed after loop
169 0           $self->wake_up();
170            
171             # Ok let's start the communication sequence....
172 0           my $cnt_out = $port_obj->write("DMPAFT\n");
173 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
174 0           my ($cnt_in, $str) = $self->read(1);
175            
176 0           my $ack = ord $str;
177 0 0         unless ($ack == 6) { warn "Ack not received on DMPAFT command: $ack"; exit -1; }
  0            
  0            
178            
179 0           $cnt_out = $port_obj->write($cmd);
180 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
181 0           ($cnt_in, $str) = $self->read(7);
182            
183 0           $ack = ord substr($str,0,1);
184            
185 0           my $ls = unpack("H20",substr($str,1,4) );
186 0           $ls =~ s/(\w{2})/$1 /g;
187            
188 0           my $pages = unpack("s",substr($str,1,2) );
189 0           my $rec_start = unpack("s",substr($str,3,2) );
190            
191 0           $crc = CRC_CCITT(substr($str,1,6) );
192              
193 0           print "Pages = $pages : rec = $rec_start Datestamp $vDateStamp $crc\n";
194            
195 0           $cnt_out = $port_obj->write( pack("h", 0x06) );
196            
197             #if ($pages == 513 ) { return -1 }
198            
199 0           my @arc_rec_lst;
200 0           foreach my $page (1..$pages)
201             {
202 0           my $page_sz = 267;
203 0           my ($cnt_in, $str) = $self->read($page_sz,3);
204 0 0         print "Page $page\n" if ( $Verbose );
205            
206 0           my $rec_sz = 52;
207 0           my $date_prev = 0;
208 0           my %hsh;
209            
210 0           foreach my $rec ( 0..4 )
211             {
212 0 0 0       if ( ($page == 1) && ($rec < $rec_start ) ) { next } # Find the right starting point...
  0            
213              
214 0           my $start_ptr = 1 + ($rec * $rec_sz );
215 0           my $rec_str = substr($str, $start_ptr ,52);
216             #print "$start_ptr \t > " . unpack( "h*", $rec_str) . "\n";
217            
218 0           my $date = substr($rec_str,0,2);
219 0           my $date_curr = unpack "s", $date;
220            
221             # Check if we have wrapped...
222 0 0         if ( $date_curr < $date_prev ) { last; }
  0            
223 0           $date_prev = $date_curr;
224            
225 0           $hsh{'date_stamp'} = $date_curr;
226 0           $hsh{'time_stamp'} = unpack "s", substr($rec_str,2,2);
227            
228 0           $hsh{'day'} = unpack( "c", $date & pack("c",0x1F) );
229 0           $hsh{'month'} = ( $hsh{'date_stamp'} >> 5) & 0xF;
230 0           $hsh{'year'} = ( $hsh{'date_stamp'} >> 9) + 2000;
231            
232 0           $hsh{'hour'} = sprintf("%02d", int ( $hsh{'time_stamp'} / 100 ));
233            
234 0           $hsh{'min'} = $hsh{'time_stamp'} - ($hsh{'hour'} * 100);
235 0           $hsh{'min'} = sprintf("%02d", $hsh{'min'});
236            
237 0           $hsh{'time_stamp_fmt'} = "$hsh{'hour'}:$hsh{'min'}:00";
238 0           $hsh{'date_stamp_fmt'} = "$hsh{'year'}_$hsh{'month'}_$hsh{'day'}";
239              
240 0           $hsh{'unixtime'} = timelocal(0,$hsh{min}, $hsh{hour},
241             $hsh{day}, $hsh{month}-1, $hsh{year}-1900);
242            
243 0           $hsh{'Air_Temp'} = unpack("s", substr($rec_str,4,2)) / 10;
244 0           $hsh{'Air_Temp_Hi'} = unpack("s", substr($rec_str,6,2)) / 10;
245 0           $hsh{'Air_Temp_Lo'} = unpack("s", substr($rec_str,8,2)) / 10;
246 0           $hsh{'Rain_Clicks'} = unpack("s", substr($rec_str,10,2));
247 0           $hsh{'Rain_Rate_Clicks'} = unpack("s", substr($rec_str,12,2));
248 0           $hsh{'Rain_Rate'} = $hsh{'Rain_Rate_Clicks'} / 100; # Inches per hour
249 0           $hsh{'Barometric_Press'} = unpack("s", substr $rec_str,14,2) / 1000;
250 0           $hsh{'Solar'} = unpack("s", substr $rec_str,16,2); # watt/m**2
251 0           $hsh{'Wind_Samples'} = unpack("s", substr $rec_str,18,2);
252 0           $hsh{'Air_Temp_Inside'} = unpack("s", substr $rec_str,20,2) / 10;
253              
254 0           $hsh{'Relative_Humidity_Inside'} = unpack("C", substr $rec_str,22,1);
255 0           $hsh{'Relative_Humidity'} = unpack("C", substr $rec_str,23,1);
256              
257 0           $hsh{'Wind_Speed'} = unpack("C", substr($rec_str,24,1));
258 0           $hsh{'Wind_Gust_Max'} = unpack("C", substr($rec_str,25,1));
259 0           $hsh{'Wind_Dir_Max'} = unpack("C", substr($rec_str,26,1));
260 0           $hsh{'Wind_Dir'} = unpack("C", substr($rec_str,27,1));
261              
262 0           $hsh{'UV'} = unpack("C", substr($rec_str,28,1)) / 10;
263 0           $hsh{'ET'} = unpack("C", substr($rec_str,29,1)) / 1000;
264              
265 0           $hsh{'Solar_Max'} = unpack("s", substr($rec_str,30,2));
266 0           $hsh{'UV_Max'} = unpack("C", substr($rec_str,32,1));
267            
268 0           $hsh{'Forecast_Rule'} = unpack("C", substr($rec_str,33,1));
269              
270 0           $hsh{'Dew_Point'} = _dew_point($hsh{'Air_Temp'},$hsh{'Relative_Humidity'});
271            
272             # Miscellaneous others omitted for now
273            
274 0 0         print "date> $hsh{'time_stamp'} $hsh{'time_stamp_fmt'} $hsh{'date_stamp'} $hsh{'date_stamp_fmt'}\n" if ( $Verbose );
275             #print Dumper \%hsh;
276            
277 0           push @arc_rec_lst, {%hsh};
278             }
279            
280             #$in = ; # Testing step through facility
281             #if ($in =~ /q/i ) { $port_obj->write( pack("h", 0x1B) ); last; }
282             #else { $port_obj->write( pack("h", 0x06) ); }
283 0           $port_obj->write( pack("h", 0x06) );
284            
285             }
286            
287 0           return \@arc_rec_lst;
288             }
289              
290             # - - - - - - - - - - - - - - - - - - - - - - - - - -
291             sub get_one_loop
292             {
293 0     0 1   my $self = shift @_;
294            
295 0 0         unless ( $self->start_loop(1) ) { return 0; }
  0            
296 0           my $hsh_ref = $self->read_loop();
297            
298 0           return $hsh_ref;
299             }
300              
301             # - - - - - - - - - - - - - - - - - - - - - - - - - -
302             sub start_loop
303             {
304 0     0 1   my $self = shift @_;
305 0   0       my $lp_cnt = shift @_ || 1;
306              
307 0           $self->wake_up();
308            
309 0           my $cnt_out = $self->{'port_obj'}->write("LOOP $lp_cnt\n");
310            
311 0           my ($cnt_in, $str) = $self->read(1);
312            
313 0 0         if ( ord($str) != 6 ) { warn("Ack not returned for Loop"); return 0; }
  0            
  0            
314              
315 0           return 1;
316             }
317              
318             # - - - - - - - - - - - - - - - - - - - - - - - - - - -
319             sub read_loop
320             {
321 0     0 1   my $self = shift @_;
322            
323 0           my ($cnt_in, $str) = $self->read(99, 4); # extend timeout to 3 seconds
324 0 0         if ( $cnt_in != 99 ) { return 0 }
  0            
325            
326 0           my $hsh_ref = parse_loop_blck($str);
327            
328 0           return $hsh_ref;
329             }
330              
331             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
332             sub parse_loop_blck
333             {
334 0     0 0   my $blk = shift @_;
335 0           my $loo = substr $blk,0,3;
336              
337 0           my $ack = ord substr($blk,0,1);
338            
339 0 0         unless ( $loo eq 'LOO') { warn("Block invalid loo -> $loo\n"); return ""; }
  0            
  0            
340            
341 0           my %hsh;
342              
343 0           $hsh{'Barometric_Trend'} = unpack("C", substr $blk,3,1);
344 0           $hsh{'next_rec'} = unpack("s", substr $blk,5,2);
345 0           $hsh{'Barometric_Press'} = unpack("s", substr $blk,7,2) / 1000;
346 0           $hsh{'Air_Temp_Inside'} = unpack("s", substr $blk,9,2) / 10;
347 0           $hsh{'Humidity_Inside'} = unpack("C", substr $blk,11,1);
348 0           $hsh{'Air_Temp'} = unpack("s", substr $blk,12,2) / 10;
349 0           $hsh{'Wind_Speed'} = unpack("C", substr $blk,14,1);
350 0           $hsh{'Wind_Speed_10min_Ave'} = unpack("C", substr $blk,15,1);
351 0           $hsh{'Wind_Dir'} = unpack("s", substr $blk,16,2);
352             # Skip other temps for now...
353            
354 0           $hsh{'Relative_Humidity'} = unpack("C", substr $blk,33,1);
355             # Skip other humidities for now...
356              
357 0           $hsh{'Rain_Rate_Clicks'} = unpack("s", substr $blk,41,2);
358 0           $hsh{'Rain_Rate'} = $hsh{'Rain_Rate_Clicks'} / 100; # Inches per hr
359 0           $hsh{'UV'} = unpack("C", substr $blk,43,1);
360 0           $hsh{'Solar'} = unpack("s", substr $blk,44,2); # watt/m**2
361 0           $hsh{'Rain_Storm'} = unpack("s", substr $blk,46,2) / 100; # Inches per storm
362              
363 0           $hsh{'Storm_Date'} = unpack("s", substr $blk,48,2); # Need to parse data (not sure what this is)
364 0           $hsh{'Rain_Day'} = unpack("s", substr $blk,50,2)/100;
365 0           $hsh{'Rain_Month'} = unpack("s", substr $blk,52,2)/100;
366 0           $hsh{'Rain_Year'} = unpack("s", substr $blk,54,2)/100;
367              
368 0           $hsh{'Day_ET'} = unpack("s", substr $blk,56,2)/1000;
369 0           $hsh{'Month_ET'} = unpack("s", substr $blk,58,2)/100;
370 0           $hsh{'Year_ET'} = unpack("s", substr $blk,60,2)/100;
371             # Skip Soil/Leaf Wetness
372            
373 0           $hsh{'Alarms_Inside'} = unpack("b8", substr $blk,70,1);
374 0           $hsh{'Alarms_Rain'} = unpack("b8", substr $blk,70,1);
375 0           $hsh{'Alarms_Outside'} = unpack("b8", substr $blk,70,1);
376             # Skip extra alarms
377            
378 0           $hsh{'Batt_Xmit'} = unpack("C", substr $blk,86,1) * 0.005859375;
379 0           $hsh{'Batt_Cons'} = unpack("s", substr $blk,87,2) * 0.005859375;
380              
381 0           $hsh{'Forecast_Icon'} = unpack("C", substr $blk,89,1);
382 0           $hsh{'Forecast_Rule'} = unpack("C", substr $blk,90,1);
383              
384 0           $hsh{'Sunrise'} = sprintf( "%04d", unpack("S", substr $blk,91,2) );
385 0           $hsh{'Sunrise'} =~ s/(\d{2})(\d{2})/$1:$2/;
386            
387 0           $hsh{'Sunset'} = sprintf( "%04d", unpack("S", substr $blk,93,2) );
388 0           $hsh{'Sunset'} =~ s/(\d{2})(\d{2})/$1:$2/;
389              
390 0           $hsh{'Dew_Point'} = _dew_point($hsh{'Air_Temp'},$hsh{'Relative_Humidity'});
391            
392 0           my $nl = ord substr $blk,95,1;
393 0           my $cr = ord substr $blk,96,1;
394              
395 0           $hsh{crc} = unpack "%n", substr($blk,97,2);
396 0           $hsh{'crc_calc'} = CRC_CCITT($blk);
397            
398 0           return \%hsh;
399             }
400              
401             # - - - - - - - - - - - - - - - - - - - - - - - - - -
402             sub get_eeprom
403             {
404 0     0 1   my $self = shift @_;
405 0           my $item = shift @_;
406            
407 0           my ($loc, $size);
408             # Not all supported.... More to follow
409 0 0         if ( uc($item) eq 'ARCHIVE_PERIOD' ){ $loc = '2D'; $size = '01' }
  0 0          
  0 0          
    0          
    0          
    0          
    0          
410 0           elsif ( uc($item) eq 'TIME_ZONE' ){ $loc = '11'; $size = '01' }
  0            
411 0           elsif ( uc($item) eq 'MANUAL_OR_AUTO' ){ $loc = '12'; $size = '01' }
  0            
412 0           elsif ( uc($item) eq 'DAYLIGHT_SAVINGS' ){ $loc = '13'; $size = '01' }
  0            
413 0           elsif ( uc($item) eq 'GMT_OFFSET' ){ $loc = '14'; $size = '02' }
  0            
414 0           elsif ( uc($item) eq 'GMT_OR_ZONE' ){ $loc = '16'; $size = '01' }
  0            
415 0           elsif ( uc($item) eq 'SETUP_BITS' ){ $loc = '2B'; $size = '01' }
  0            
416 0           else { warn "$item not found"; return -1; }
  0            
417            
418 0           my $port_obj = $self->{port_obj};
419            
420 0           my $cnt_out = $port_obj->write("EERD $loc $size\n");
421 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
422            
423             # A \n\r is prefixed not as in the documentation...
424 0           my $read_size = (hex($size) * 4) + 6;
425            
426 0           my ($cnt_in, $str) = $self->read($read_size);
427              
428 0           my @rsp_lst = split /\n\r/, $str;
429 0           shift(@rsp_lst);
430            
431 0 0         if ( $rsp_lst[0] ne 'OK' ) { _dump($str); warn "OK Not returned"; }
  0            
  0            
432 0           shift(@rsp_lst);
433            
434 0           return \@rsp_lst;
435             }
436              
437             # - - - - - - - - - - - - - - - - - - - - - - - - - -
438             sub gettime
439             {
440 0     0 1   my $self = shift @_;
441            
442 0           my $port_obj = $self->{port_obj};
443            
444 0           my $cnt_out = $port_obj->write("GETTIME\n");
445 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
446              
447 0           my ($cnt_in, $str) = $port_obj->read(9);
448            
449 0           my $ck = CRC_CCITT(substr($str,1,9));
450 0 0         if ( $ck ) { warn "checksum error"; return 0; }
  0            
  0            
451              
452 0           my @rsp_lst = split //, $str;
453 0           shift @rsp_lst;
454            
455 0           @rsp_lst = map ord, @rsp_lst;
456            
457 0           return \@rsp_lst;
458             }
459              
460             # - - - - - - - - - - - - - - - - - - - - - - - - - -
461             sub settime
462             {
463 0     0 1   my $self = shift @_;
464 0           my $t_ref = shift @_;
465            
466 0           my $port_obj = $self->{port_obj};
467            
468 0           my $cnt_out = $port_obj->write("SETTIME\n");
469 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
470            
471 0           my ($cnt_in, $str) = $port_obj->read(1);
472 0           my $ack = ord $str;
473 0 0         if ( $ack != 6 ) { warn "SETTIME not set ack $ack !"; return 0; }
  0            
  0            
474              
475 0           my ($sec, $min, $hour, $day, $mon, $yr) = @{$t_ref};
  0            
476            
477 0           $str = join "", map chr, ($sec, $min, $hour, $day, $mon, $yr);
478            
479 0           my $ck = CRC_CCITT($str);
480 0           $str = $str . pack("n",$ck);
481            
482 0           $cnt_out = $port_obj->write($str);
483 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
484            
485 0           ($cnt_in, $str) = $port_obj->read(1);
486 0 0         if ( ord($str) != 6 ) { warn "SETTIME not set!"; return 0; }
  0            
  0            
487              
488 0           sleep 3; # The console seems to need to some time here...
489            
490 0           return 1;
491             }
492              
493             # - - - - - - - - - - - - - - - - - - - - - - - - - -
494             sub set_archive_period
495             {
496 0     0 1   my $self = shift @_;
497 0           my $period = shift @_;
498              
499 0 0         unless ( grep { $_ == $period } (1, 5, 10, 15, 30, 60, 120) )
  0            
500             {
501 0           warn "Not valid archive period"; # Limits in document
502 0           return 0;
503             }
504            
505 0           my $port_obj = $self->{port_obj};
506            
507 0           my $cnt_out = $port_obj->write("SETPER $period\n");
508 0 0         unless ($cnt_out) { warn "write failed\n" };
  0            
509            
510 0           my ($cnt_in, $str) = $port_obj->read(1);
511              
512 0           my $ack = ord $str;
513            
514 0 0         unless ( $ack != 6 ) { warn "Archive not set!"; return 0; }
  0            
  0            
515            
516 0           return 1;
517              
518             }
519              
520             my $t_prv = time;
521              
522             # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
523             sub get_archive_period
524             {
525 0     0 1   my $self = shift @_;
526              
527 0           my $rst = $self->get_eeprom('archive_period');
528 0           my $archive_period = hex($rst->[0]);
529              
530 0           return $archive_period;
531             }
532              
533             # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
534             sub get_timezone
535             {
536 0     0 1   my $self = shift @_;
537              
538 1     1   1123 use DateTime::TimeZone;
  1         92357  
  1         1761  
539              
540             # Calculate the time zone used by the VP and return as a TimeZone object
541            
542 0           my $timezone;
543 0 0         if (hex $self->get_eeprom('gmt_or_zone')->[0])
544             {
545             # Unit is configured for GMT offset value
546             # Wow, this is messy!
547 0           my $dst = 0; # Manual daylight saving adjustment to make
548 0 0         if (hex $self->get_eeprom('manual_or_auto')->[0])
549             {
550             # Unit has daylight saving in manual
551 0           $dst = hex $self->get_eeprom('daylight_savings')->[0];
552             }
553 0           my $val = $self->get_eeprom('gmt_offset'); # Get offset in hours
554 0           my $offset = hex ($val->[1].$val->[0]); # Combine the 2 bytes together
555 0 0         $offset -= 65536 if $offset > 32767; # 2's complement if -ve
556 0           $offset /= 100; # Convert to hours
557 0           $offset += $dst; # Adjust for daylight saving if required
558 0           my $hours = int $offset; # The whole number of hours
559 0           my $minutes = abs ($offset - $hours) * 60; # The number of minutes
560 0           $minutes = sprintf("%02d", $minutes); # Prefix with 0 if required
561 0           my $tzstr = $hours.$minutes; # The 2 together to create tz string
562 0 0 0       $tzstr *= -1 if $offset < 0 && $hours == 0; # Fix negative for 0 hours
563 0           $tzstr = sprintf("%+05d", $tzstr); # The final formatted string
564 0           $timezone = DateTime::TimeZone->new( name => $tzstr );
565             }
566             else {
567             # Unit configured for specific timezone
568 0           my $tz = hex $self->get_eeprom('time_zone')->[0];
569 0           my @timezones = qw( Pacific/Kwajalein
570             Pacific/Midway
571             Pacific/Honolulu
572             America/Anchorage
573             America/Tijuana
574             America/Denver
575             America/Chicago
576             America/Mexico_City
577             America/Monterrey
578             America/Bogota
579             America/New_York
580             America/Halifax
581             America/Santiago
582             America/St_Johns
583             America/Sao_Paulo
584             America/Argentina/Buenos_Aires
585             Atlantic/South_Georgia
586             Atlantic/Azores
587             Europe/London
588             Africa/Casablanca
589             Europe/Berlin
590             Europe/Paris
591             Europe/Prague
592             Europe/Athens
593             Africa/Cairo
594             Europe/Bucharest
595             Africa/Harare
596             Asia/Jerusalem
597             Asia/Baghdad
598             Europe/Moscow
599             Asia/Tehran
600             Asia/Muscat
601             Asia/Kabul
602             Asia/Karachi
603             Asia/Kolkata
604             Asia/Almaty
605             Asia/Bangkok
606             Asia/Shanghai
607             Asia/Hong_Kong
608             Asia/Tokyo
609             Australia/Adelaide
610             Australia/Darwin
611             Australia/Brisbane
612             Australia/Hobart
613             Asia/Magadan
614             Pacific/Fiji
615             Pacific/Auckland
616             );
617 0           $timezone = DateTime::TimeZone->new( name => $timezones[$tz] );
618             }
619              
620 0           return $timezone;
621             }
622              
623             # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
624             sub make_date_time_stamp
625             {
626 0     0 1   my $self = shift @_;
627            
628 0           my ($year, $mon, $mday, $hour, $min) = @_;
629              
630            
631             # Test Example as per Page 31 in Document
632             #$mon = 6;$mday = 6;$year = 2003;$hour = 9;$min = 30;
633             # See print time stamps below after CRC and formatting
634              
635             #print "Looking for record $year, $mon $mday $hour:$min\n";
636            
637             # The friggen Vantage pro requires time stamps that _exactly_ match
638             # the record in memory or it sends the whole archive.
639             #my $rmn = $self->get_archive_period();
640             #$min = $min - $rmn; # Note this does not work for any archive_period > 60
641              
642             #if ( $min > 0 )
643             #{ $min = 60 + $min;
644             # $hour -= 1;
645             # if ($hour < 0 ) { $hour = 23; }
646             #}
647              
648             #my $gap = $min % $rmn;
649             #$min = $min - $gap;
650            
651             #print "Looking for record $year, $mon $mday $hour:$min\n";
652            
653 0           my $vDateStamp = $mday + ($mon)*32 + ($year-2000)*512;
654 0           my $vTimeStamp = (100 * $hour) + $min;
655              
656 0           return ($vDateStamp, $vTimeStamp);
657             }
658              
659             # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
660             sub get_setup_bits
661             {
662 0     0 1   my $self = shift @_;
663              
664 0           my $rst = $self->get_eeprom('setup_bits');
665 0           my $enc = hex($rst->[0]);
666 0           my %setup_bits;
667 0           $setup_bits{TimeMode} = $enc & 0x01;
668 0           $setup_bits{IsAM} = $enc >> 1 & 0x01;
669 0           $setup_bits{MonthDayFormat} = $enc >> 2 & 0x01;
670 0           $setup_bits{WindCupSize} = $enc >> 3 & 0x01;
671 0           $setup_bits{RainCollectorSize} = $enc >> 4 & 0x03;
672 0           $setup_bits{Latitude} = $enc >> 6 & 0x01;
673 0           $setup_bits{Longitude} = $enc >> 7 & 0x01;
674              
675 0           return \%setup_bits;
676             }
677              
678             # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
679             sub get_ymdhm
680             {
681 0     0 0   my $self = shift @_;
682 0           my $utime = shift @_;
683              
684 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($utime);
685 0           $mon = $mon + 1;
686 0           $year = $year + 1900;
687              
688 0           return ($year, $mon, $mday, $hour, $min);
689             }
690              
691             # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
692             sub read
693             {
694 0     0 0   my $self = shift @_;
695 0   0       my $bytes = shift @_ || 255;
696 0   0       my $timeout = shift @_ || 2;
697            
698 0           my $port_obj = $self->{port_obj};
699              
700 0           my ($cnt_in, $str);
701              
702 0           eval {
703 0     0     local $SIG{ALRM} = sub { die "alarm $timeout expired\n" }; # NB: \n required
  0            
704 0           alarm $timeout;
705            
706 0           ($cnt_in, $str) = $self->{'port_obj'}->read($bytes);
707              
708 0           alarm 0;
709             };
710            
711 0 0         if ($@)
712             {
713 0           warn "Read Timeout $timeout\n";
714 0           return 0;
715             }
716              
717 0           return ($cnt_in, $str);
718              
719             }
720              
721             sub _dew_point
722             {
723 0     0     my $temp = shift @_;
724 0           my $rh = shift @_;
725            
726             # Using the simplified approximation for dew point
727             # Accurate to 1 degree C for humidities > 50 %
728             # http://en.wikipedia.org/wiki/Dew_point
729              
730 0           my $dew_point = $temp - ( (100 - $rh)/5 );
731            
732 0           return $dew_point;
733             }
734              
735              
736             sub _dump
737             {
738 0     0     my @lst = split //, $_[0];
739 0           print "Bytes " . scalar(@lst) . "\n";
740 0           foreach my $i ( @lst ) {
741 0           print "> " . ord($i) . "\n";
742             }
743             }
744              
745             # - - - - - - - - - - - - - - - - - - -
746             sub CRC_CCITT
747             {
748             # Expects packed data...
749 0     0 0   my $data_str = shift @_;
750              
751 0           my @crc_table = crc_table();
752              
753 0           my $crc = 0;
754 0           my @lst = split //, $data_str;
755 0           foreach my $data (@lst)
756             {
757 0           my $data = unpack("c",$data);
758            
759 0           my $crc_prev = $crc;
760 0           my $index = $crc >> 8 ^ $data;
761 0           my $lhs = $crc_table[$index];
762 0           my $rhs = ($crc << 8) & 0xFFFF;
763 0           $crc = $lhs ^ $rhs;
764            
765             #$data = unpack("H*",$data);
766             #printf("%X\t %s\t %X\t %X\t %X\t : %x \n", $crc_prev, $data, $index, $lhs, $rhs, $crc);
767             }
768            
769 0           return $crc;
770             }
771              
772             # - - - - - - - - - - - - - - - - - - -
773             sub crc_table
774             {
775              
776 0     0 0   my @crc_table = (
777             0x0, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
778             0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
779             0x1231, 0x210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
780             0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
781             0x2462, 0x3443, 0x420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
782             0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
783             0x3653, 0x2672, 0x1611, 0x630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
784             0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
785             0x48c4, 0x58e5, 0x6886, 0x78a7, 0x840, 0x1861, 0x2802, 0x3823,
786             0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
787             0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0xa50, 0x3a33, 0x2a12,
788             0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
789             0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0xc60, 0x1c41,
790             0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
791             0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0xe70,
792             0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
793             0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
794             0x1080, 0xa1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
795             0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
796             0x2b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
797             0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
798             0x34e2, 0x24c3, 0x14a0, 0x481, 0x7466, 0x6447, 0x5424, 0x4405,
799             0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
800             0x26d3, 0x36f2, 0x691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
801             0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
802             0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x8e1, 0x3882, 0x28a3,
803             0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
804             0x4a75, 0x5a54, 0x6a37, 0x7a16, 0xaf1, 0x1ad0, 0x2ab3, 0x3a92,
805             0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
806             0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0xcc1,
807             0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
808             0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0xed1, 0x1ef0);
809             }
810              
811              
812             1;
813             __END__