SCP1000+Arduino+Pachube+cacti
公開日:
:
最終更新日:2012/07/30
プログラミング
気圧をグラフにして残す
SCP1000+ArduinoをCentOS機(NEC 110ge)にUSB接続してデータを保管、Pachubeとcactiにてグラフ表示させてみました。
当初、cactiでひっそりこっそりデータを確認してたのですが、Pachubeが思ったより簡単だったので、同時にデータを投げるようにしました。今回もいきあたりばったりでDBは使用せず、テキストファイルを使っています。
USBデバイスからのデータを読み込む
ガイガーカウンターのときと同じような感じで、データ生成ツール(今回はPerlスクリプト)をcronで回して、テキストで追記。これをPHPで読み取り、cacti用にprintfした後、Pachubeに投げます。
Perlスクリプトは「ねこひげめも|Linux(CentOS)でUSBウェザーボードを使って遊ぶ」を参考にさせて頂きました。参考というか、まるごとパクリです。
追記
どうもSCP1000が返す数字を拾うのに失敗することが多いようで、グラフが歯抜けになってしまうので、ifを2回繰り返して、それでもダメなら空白を返すようにしました。0じゃなく、空白ならグラフも途切れるだけかな。3回待ってまで拾う意味のある数字でもないのでとりあえずこれで。
再追記(2012/07/30)
やっぱり数値取得がうまくいかないので、データファイルを読み込んで直近何件かの数値から外れ値(という言葉を学んだ)を除外するために、中央値・標準偏差を使うことにしました。大学で統計学を選択しましたが、教科書を買ったくらいであまり覚えていませんので適当にググったところ、スミルノフ・グラブス検定というのがよさそうでしたが、とりあえず今回は外れ値を残さないように取り急ぎ以下のように変更しました。
初期バージョン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#!/usr/bin/perl use strict; open(PORT,"/dev/ttyUSB0") || die "NG!"; my $times = time(); (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $stime) = localtime($times); $year = $year +1900; $mon++; my $datetime=sprintf("%04d-%02d-%02dT%02d:%02d:%02d+09:00", $year,$mon,$mday,$hour,$min,$sec); my $serial = <PORT>; chomp $serial; my @val = split /,/,$serial; if($val[1]<1){ sleep(5); my $serial = <PORT>; chomp $serial; my @val = split /,/,$serial; } my $temp = $val[0]; my $press = $val[1]; chomp $temp; chomp $press; printf("%s,%0.2f,%2.2f\n", $datetime, $press, $temp); close(PORT); |
外れ値除外バージョン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
#!/usr/bin/perl use strict; use warnings; use Statistics::Basic qw(:all); # 現在接続しているポートはUSB0 open(PORT,"/dev/ttyUSB0") || die "NG!"; my $serial = <PORT>; chomp $serial; my @val = split /,/,$serial; # データの最新$n行を取得し@items配列に入れる # $nは5分ごと取得のため一時間で12回のためこんな表記 my $items = []; my $n = 12 * 3; my @lines = `tail -n $n /hoge/hoge.txt`; for (my $i = 0; $i < $n; $i++) { my $line = $lines[$i]; chomp $line; my @last_line = split(',', $line); push @$items, \@last_line; } # 気温 値が未入力(=空白)以外の要素を@tempsに入れる # $count_tempはmodule使うことにしたので不要ですが残してある my $temps = []; my $count_temp = 0; foreach my $item(@$items) { my $item_temp = $item->[2]; if (defined $item->[2]) { push @$temps, $item_temp; $count_temp++; } } # 気圧 値が未入力(=空白)以外の要素を@presに入れる my $pres = []; my $count_pres = 0; foreach my $item(@$items) { my $item_pres = $item->[1]; if (defined $item->[1]) { push @$pres, $item_pres; $count_pres++; } } # 上限を中央値+標準偏差*3 下限を中央値+標準偏差*3とする # median, stddevはStatistics::Basic my $med_temp_top = median(@$temps) + stddev(@$temps) * 3; my $med_temp_bottom = median(@$temps) - stddev(@$temps) * 3; # 同上 my $med_pres_top = median(@$pres) + stddev(@$pres) * 3; my $med_pres_bottom = median(@$pres) - stddev(@$pres) * 3; # 気圧の数値が変な場合は再度取得 2回だけ挑戦しとく if($val[1]<1){ sleep(5); my $serial = <PORT>; chomp $serial; my @val = split /,/,$serial; } if($val[1]<1){ sleep(5); my $serial = <PORT>; chomp $serial; my @val = split /,/,$serial; } # 現在時刻を整形 my $times = time(); (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $stime) = localtime($times); $year = $year +1900; $mon++; my $datetime=sprintf("%04d-%02d-%02dT%02d:%02d:%02d+09:00", $year,$mon,$mday,$hour,$min,$sec); # USBより取得した現在の温度および気圧 my $get_temp = $val[0]; my $get_pres = $val[1]; chomp $get_temp; chomp $get_pres; # やっぱり気圧の数値がおかしければ時刻だけ出力 # 気圧数値正常な場合は、気圧・気温が上限・下限内に収まっているか確認 # 論理式がおかしいようでちゃんと動かないので急ぎ適当に修正 =pod if($val[1]<1){ printf("%s,,\n", $datetime); } elsif (($get_pres > $med_pres_bottom && $get_pres < $med_pres_top) && ($get_temp > $med_temp_bottom && $get_temp < $med_temp_top)) { printf("%s,%0.2f,%2.2f\n", $datetime, $get_pres, $get_temp); } else { printf("%s,,\n", $datetime); } =cut if ($get_pres > $med_pres_bottom && $get_pres < $med_pres_top) { my $pres_now = $get_pres; } else { my $pres_now = ''; } if ($get_temp > $med_temp_bottom && $get_temp < $med_temp_top) { my $temp_now = $get_temp; } else { my $temp_now = ''; } printf("%s,%0.2f,%2.2f\n", $datetime, $get_pres, $get_temp); # 閉じる close(PORT); |
定期実行させる
crontabはこんな感じで。
1 |
*/5 * * * * /hoge/scp1000.pl >> /hoge/scp1000tmp.txt |
1 2 3 4 5 6 |
scp1000tmp.txt 2011-09-21T16:00:03+09:00,986.60,28.03 2011-09-21T16:05:03+09:00,986.90,28.03 2011-09-21T16:10:03+09:00,987.05,28.07 2011-09-21T16:15:02+09:00,987.32,28.03 |
追記2
cronで5分ごとにガイガーカウンターの数値もとっているためか、USBより数値を拾い損ねることが多発し、グラフが歯抜けすぎるので、1分ずらしてみました。
1 |
1,6,11,16,21,26,31,36,41,46,51,56 * * * * /hoge/scp1000.pl >> /hoge/scp1000tmp.txt |
なお、SCP1000が日光の影響を受けるという動画がありました。気温と連動してるのかと思ってたら陽射しと連動していたのか・・・。これは急いで対策しなければ。
cactiとPachubeへ送信
PHPスクリプトはcactiデータ取得用とPachube送信と兼用なのはガイガーカウンターのときと同様です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?php require_once('/PachubeAPI.php'); mb_internal_encoding("UTF-8"); $datafile = "/hoge/scp1000tmp.txt"; $t_file = file_get_contents($datafile); //Pachube $api_key = "your_api_key_here"; $feed = '5digit_feed_number'; //put all data into array $rows = explode("\n", $t_file); $cols = explode(",", $t_file); $csv_arr = array_map("str_getcsv", $rows); $row_c = count($csv_arr); //read the last line $last_1time = $csv_arr[$row_c-2][0]; $last_1pres = $csv_arr[$row_c-2][1]; $last_1temp = $csv_arr[$row_c-2][2]; $datetime = $last_1time; $pressure = $last_1pres; $temp = $last_1temp; //print for cacti printf("scppres:%0.2f scptemp:%2.2f", $pressure,$temp); //max and min data for Pachube $press_min = '800'; $press_max = '1050'; $temp_min = '-10'; $temp_max = '50'; //Pachube //id1:Press id2:Temp $json_arr = array( 'version' => '1.0.0', 'datastreams' => array(array('id' => '1', 'at' => $datetime, 'max_value' => $press_max, 'min_value' => $press_min, 'current_value' => $pressure), array('id' => '2', 'at' => $datetime, 'max_value' => $temp_max, 'min_value' => $temp_min, 'current_value' => $temp)) ); $update_data = json_encode($json_arr); $obj = new PachubeAPI($api_key); $obj->updateFeed('json', $feed, $update_data); ?> |