]> Git — Sourcephile - julm/air-duino.git/blob - collect.pl
add MQ9 sensor
[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 => "temperature"
43 , type => "GAUGE"
44 , min => -15
45 , max => 49
46 , heartbeat => 2 * $step # seconds
47 }
48 , data_source =>
49 { name => "humidity"
50 , type => "GAUGE"
51 , min => 0
52 , max => 100
53 , heartbeat => 2 * $step # seconds
54 }
55 , data_source =>
56 { name => "air"
57 , type => "GAUGE"
58 , min => 0
59 , max => 1000
60 , heartbeat => 2 * $step # seconds
61 }
62 , data_source =>
63 { name => "dust"
64 , type => "GAUGE"
65 , min => 0
66 , max => 100000
67 , heartbeat => 2 * $step # seconds
68 }
69 , data_source =>
70 { name => "co_ch4_lpg"
71 , type => "GAUGE"
72 , min => 0
73 , max => 31
74 , heartbeat => 2 * $step # seconds
75 }
76 , archive =>
77 { cpoints => 1
78 , cfunc => 'AVERAGE'
79 , rows => $month_seconds / $step
80 , xff => 0.99 # ignore unknowns values (U)
81 }
82 );
83 }
84 return $rrd;
85 }
86 sub dev_init ($) {
87 my ($file) = @_;
88 my $dev = Device::SerialPort->new($file);
89 $dev->baudrate(9600); # MUST: match *uino Serial.begin()
90 #$dev->buffers(4096, 4096); # NOTE: no-op on POSIX
91 $dev->databits(8);
92 $dev->dtr_active(1); # NOTE: reset the *uino on serial connection
93 $dev->handshake('none');
94 $dev->parity("none");
95 $dev->read_char_time(0); # NOTE: don't wait for each character
96 $dev->read_const_time(1000); # NOTE: 1 second per unfulfilled "read" call
97 $dev->stopbits(1);
98 $dev->write_settings;
99 return $dev;
100 }
101
102 sub main () {
103 my $rrd = rrd_init();
104 my $dev = dev_init($ARGV[0]);
105
106 #autoflush STDOUT 1;
107 my $read_timeout = 60;
108 my $timeout = $read_timeout;
109 my $buffer = "";
110 my $last_time = 0;
111 my @collect = ();
112 while ($timeout>0) {
113 my $curr_day = (localtime)[3];
114 if ($curr_day == 1) {
115 # NOTE: month changed, change RRD
116 # FIXME: avoid to check that every second...
117 $rrd = rrd_init();
118 }
119 my ($count, $saw) = $dev->read(1);
120 # NOTE: this could have read up to 255 chars (max for portability)
121 # but reading only 1 char at a time
122 # enables to add a more accurate timestamp.
123 if (defined $count and $count > 0) {
124 $buffer .= $saw;
125 my @lines = split /\r\n/, $buffer;
126 if (@lines > 1) {
127 my $time = time;
128 # NOTE: process only ended lines
129 $buffer = pop @lines;
130 foreach (@lines) {
131 my ($counter, $uptime, $temperature, $humidity, $quality, $dust, $coch4lpg)
132 = $_ =~ m/^([0-9]+);([0-9]+);([0-9]+.[0-9]+);([0-9]+.[0-9]+);([0-9]+);([0-9]+.[0-9]+);([0-9]+.[0-9]+)$/;
133 if (defined $dust) {
134 #print STDOUT ($time, ";", $_, "\n");
135 $collect[0] = $humidity if $humidity ne '';
136 $collect[1] = $temperature if $temperature ne '';
137 $collect[2] = $quality if $quality ne '';
138 $collect[3] = $dust if $dust ne '';
139 $collect[4] = $coch4lpg if $coch4lpg ne '';
140 if ($time >= $last_time + $step && @collect == 5) {
141 $rrd->update
142 ( time => $time
143 , values => [join (':', @collect)]
144 );
145 @collect = ();
146 $last_time = $time;
147 }
148 }
149 else {
150 print STDERR ($_, "\n");
151 }
152 }
153 }
154 $timeout = $read_timeout;
155 }
156 else {
157 $timeout--;
158 }
159 }
160 if ($timeout==0) {
161 die "Connection timeout (after ${read_timeout}s)\n";
162 }
163 undef $dev;
164 }
165 main;
166 1;