]> Git — Sourcephile - julm/air-duino.git/blob - collect.pl
change display
[julm/air-duino.git] / collect.pl
1 #!/usr/bin/perl -w
2 # Licence: WTFPLv2 <http://www.wtfpl.net/txt/copying/>
3 # Copyright 2015: Julien Moutinho <julm+air@autogeree.net>
4
5 use strict;
6 use warnings;
7 use DateTime::Duration;
8 use DateTime;
9 use Device::SerialPort qw( 0.07 );
10 use File::Basename;
11 use File::Path;
12 use File::Spec;
13 use Log::Log4perl qw(:easy);
14 use RRDTool::OO;
15 #use Data::Dumper qw(Dumper);
16 #use IO::Handle;
17 my $step = 30; # NOTE: sampling interval in seconds
18
19 sub rrd_init () {
20 Log::Log4perl->easy_init(
21 { level => $INFO
22 , category => 'rrdtool'
23 , layout => '%m%n'
24 });
25 my $now
26 = DateTime->now
27 ( time_zone => 'local'
28 #, locale => $config{locale}
29 ); # ->set_time_zone('floating');
30 my $dir = File::Spec->catdir(dirname($0), 'rrd', (sprintf '%0d', $now->year()));
31 my $file = File::Spec->catfile($dir, (sprintf '%02d.rrd', $now->month()));
32 File::Path::make_path($dir);
33 my $rrd = RRDTool::OO->new(file => $file);
34 if (not (-e $file)) {
35 my $one_month = DateTime::Duration->new(months => 1, end_of_month => 'limit');
36 my $month_begin = $now->clone->truncate(to => 'month');
37 my $month_end = $month_begin->clone->add_duration($one_month);
38 my $month_seconds = $month_end->epoch() - $month_begin->epoch();
39 $rrd->create
40 ( step => $step
41 , data_source =>
42 { name => "humidity"
43 , type => "GAUGE"
44 , min => 0
45 , max => 100
46 , heartbeat => 2 * $step # seconds
47 }
48 , data_source =>
49 { name => "temperature"
50 , type => "GAUGE"
51 , min => -15
52 , max => 50
53 , heartbeat => 2 * $step # seconds
54 }
55 , data_source =>
56 { name => "quality"
57 , type => "GAUGE"
58 , min => 0
59 , max => 1000
60 , heartbeat => 2 * $step # seconds
61 }
62 , data_source =>
63 { name => "particles"
64 , type => "GAUGE"
65 , min => 0
66 , max => 100000
67 , heartbeat => 2 * $step # seconds
68 }
69 , archive =>
70 { cpoints => 1
71 , cfunc => 'AVERAGE'
72 , rows => $month_seconds / $step
73 , xff => 0.99 # ignore unknowns values (U)
74 }
75 );
76 }
77 return $rrd;
78 }
79 sub dev_init ($) {
80 my ($file) = @_;
81 my $dev = Device::SerialPort->new($file);
82 $dev->baudrate(9600); # MUST: match *uino Serial.begin()
83 #$dev->buffers(4096, 4096); # NOTE: no-op on POSIX
84 $dev->databits(8);
85 $dev->dtr_active(1); # NOTE: reset the *uino on serial connection
86 $dev->handshake('none');
87 $dev->parity("none");
88 $dev->read_char_time(0); # NOTE: don't wait for each character
89 $dev->read_const_time(1000); # NOTE: 1 second per unfulfilled "read" call
90 $dev->stopbits(1);
91 $dev->write_settings;
92 return $dev;
93 }
94
95 sub main () {
96 my $rrd = rrd_init();
97 my $dev = dev_init($ARGV[0]);
98
99 #autoflush STDOUT 1;
100 my $read_timeout = 60;
101 my $timeout = $read_timeout;
102 my $buffer = "";
103 my $last_time = 0;
104 my @collect = ();
105 while ($timeout>0) {
106 my $curr_day = (localtime)[3];
107 if ($curr_day == 1) {
108 # NOTE: month changed, change RRD
109 # FIXME: avoid to check that every second...
110 $rrd = rrd_init();
111 }
112 my ($count, $saw) = $dev->read(1);
113 # NOTE: this could have read up to 255 chars (max for portability)
114 # but reading only 1 char at a time
115 # enables to add a more accurate timestamp.
116 if (defined $count and $count > 0) {
117 $buffer .= $saw;
118 my @lines = split /\r\n/, $buffer;
119 if (@lines > 1) {
120 my $time = time;
121 # NOTE: process only ended lines
122 $buffer = pop @lines;
123 foreach (@lines) {
124 my ($counter, $uptime, $humidity, $temperature, $quality, $particles)
125 = $_ =~ m/^([0-9]+);([0-9]+);([0-9]+.[0-9]+);([0-9]+.[0-9]+);([0-9]+);([0-9]+.[0-9]+)$/;
126 if (defined $particles) {
127 #print STDOUT ($time, ";", $_, "\n");
128 $collect[0] = $humidity if $humidity ne '';
129 $collect[1] = $temperature if $temperature ne '';
130 $collect[2] = $quality if $quality ne '';
131 $collect[3] = $particles if $particles ne '';
132 if ($time >= $last_time + $step && @collect == 4) {
133 $rrd->update
134 ( time => $time
135 , values => [join (':', @collect)]
136 );
137 @collect = ();
138 $last_time = $time;
139 }
140 }
141 else {
142 print STDERR ($_, "\n");
143 }
144 }
145 }
146 $timeout = $read_timeout;
147 }
148 else {
149 $timeout--;
150 }
151 }
152 if ($timeout==0) {
153 die "Connection timeout (after ${read_timeout}s)\n";
154 }
155 undef $dev;
156 }
157 main;
158 1;