(35連休)6日目:Raspberry Pi,fluentd,TreasureData,AWS,d3.jsを使った自宅の温度・湿度データモニタリングと可視化①
今回は、これまで準備したいろいろな要素技術を組みあわせて、自宅の温度、湿度を定期的に自動でTwitterに投稿し、経時での変化をグラフとしてみれるようなWEB画面を構築しようと思います。
背景
私が好きな、NHK Eテレの最終土曜日にやっている「NHK 新世代が解く!ニッポンのジレンマ」という番組で、前回(7月末の回)のテーマだった「僕らのアグリビジネス論」にて、農地で温度・湿度をはかるセンサーをおいて30分おきとかに計測して、Twitterにその場で自動的に投稿して、自宅や外出さきでも自分の農地の状態がみれるようなものが紹介されていました。
また、最近のカンブリア宮殿でも、北海道の浜中町という町で、農協が各酪農家から牛乳を2日に1回集めて、成分分析をして酪農家に結果をフォードバックし、酪農家はその結果に基づいて飼料の配合を変えるなど手をうっていく、というような事例も紹介されていました。
2014年8月14日放送 浜中町農協組合長 石橋 榮紀(いしばし しげのり)氏|カンブリア宮殿:テレビ東京
やっぱこういう時代なんだな〜とか思いつつ、1つめのようなものはRaspberryPi使って簡単に作れそうだったので、やってみることにしました。
今想定しているシステム構成
なんとなく技術的にははやりものをふんだんに使う感じでやってみます。
前提
先週やってきた下記のことは、今回やる上での前提となります。
- Raspberry Piの初期設定 http://hsuetsugu.hatenablog.com/entry/2014/08/10/181408
- pythonでのTwitterBot http://hsuetsugu.hatenablog.com/entry/2014/08/11/112032
- Fluentd(ログ収集ツール)のインストールとTreasureDataへのデータ格納 http://hsuetsugu.hatenablog.com/entry/2014/08/15/105749
- AWS上でのd3.jsを使った画面構築 http://hsuetsugu.hatenablog.com/entry/2014/08/12/160150
温度湿度センサーの購入
ここで、完成品を購入しました。
http://strawberry-linux.com/catalog/items?code=52002
Raspberry Piに接続
USBで接続して、lsusbやdmsegコマンドで認識されていることを確認する。
計測データの取得
sudo apt-get install gcc libusb-dev sudo wget http://www.dd.iij4u.or.jp/~briareos/soft/usbrh-0.05.tar.gz sudo tar xvfz usbrh-0.05.tar.gz
この状態で、/home/pi/usbhrにてmakeして、sudo ./usbhrしても、"usb_set_configuration error"となるので、"usbhr_main.c"を下記のように修正する。
引用:USB温湿度計 - PukiWiki
USBRH on Linuxの実行時に発生する usb_set_configuration error のエラーの対処としていくつか書き込みがありましたが、以下の方法でもうまくいきました。
232行〜236行に2行追加if((rc = usb_set_configuration(dh, dev->config->bConfigurationValue))<0){ if( rc = usb_detach_kernel_driver_np(dh, dev->config->interface->altsetting->bInterfaceNumber)<0 ){ puts("usb_set_configuration error"); usb_close(dh); exit(3); } }
再度makeすると、下記のように温度と湿度が取得できました。暑い・・・。
下記コマンドにより、簡単に取得できるようになりました。
$ sudo chown root:root usbrh $ sudo chmod u+s usbrh $ sudo mv -i usbrh /usr/local/bin/ $ usbrh
Twitterに投稿(pythonで)
pythonからtwitterに投稿するのは先週やったので、少しだけ手をいれて、やってみました。
※先週の記事(http://hsuetsugu.hatenablog.com/entry/2014/08/11/112032)では、単にpython tweet.py "XXX"とすればXXXとtweetされるものでsys.argv[1]で取得していたので、この部分に取得したデータをいれるようにだけ修正しました。
-- Automatic Tweet from Raspberry Pi -- Temp:31.47 Hum:57.45
— hiroshi suetsugu (@suetsugu_h) August 16, 2014
pythonのコードはこちら。なにげにシェルの実行結果で返り値をどう取得するかというのを調べていたら時間がかかってしまいました。subprocessという標準ライブラリがでたおかげで簡単になったようです。
#!/usr/bin/env python # -*- coding: utf-8 -*- # センサーからデータ取得 from subprocess import Popen,PIPE p=Popen('usbrh',shell=True,stdin=PIPE,stdout=PIPE,close_fds=True) line = p.stdout.readline() temphum=line.rstrip().split(' ') print temphum # 取得データをTweet import time, random, urllib, urllib2, cgi, hmac, hashlib, commands import sys # text = sys.argv[1] #この部分を変えただけ。 text = '-- Automatic Tweet from Raspberry Pi -- Temp:' + temphum[0] + ' Hum:' + temphum[1] conf = {} for line in open('conf.txt'): name,value = line.rstrip().split(' ') conf.update({name:value}) for line in open('access_token.txt'): name,value = line.rstrip().split(' ') conf.update({name:value}) update_url = conf["update_url"] params = { "oauth_consumer_key": conf["consumer_key"], "oauth_signature_method": "HMAC-SHA1", "oauth_timestamp": str(int(time.time())), "oauth_nonce": str(random.getrandbits(64)), "oauth_version": "1.0" } params['oauth_token'] = conf["oauth_token"] params['status'] = text for k,v in params.items(): if isinstance(v, unicode): params[k] = v.encode('utf8') params_str = '&'.join(['%s=%s' % (urllib.quote(key, ''),urllib.quote(params[key], '~')) for key in sorted(params)]) message = '%s&%s&%s' % ('POST',urllib.quote(update_url,''), urllib.quote(params_str,'')) key = "%s&%s" % (conf["consumer_secret"], conf["oauth_token_secret"]) signature = hmac.new(key, message, hashlib.sha1) digest_base64 = signature.digest().encode("base64").strip() params['oauth_signature'] = digest_base64 del params['status'] header_params_str = ",".join(["%s=%s" % (urllib.quote(k,''), urllib.quote(params[k],'~')) for k in sorted(params)]) opener = urllib2.build_opener() opener.addheaders = [('Authorization','OAuth %s' % header_params_str)] result = opener.open(update_url,urllib.urlencode( {'status':text.encode('utf-8')})).read()
fluentdでTreasureDataに収集データを送る。(pythonで)
参考URL:Streaming Import from Python Apps | Treasure Data
fluent-loggerというライブラリを活用します。
$ sudo apt-get install python-setuptools $ sudo easy_install pip $ sudo pip install fluent-logger
決まりがあるらしく(理解できていないのですが・・・)、設定ファイルのsourceのところを書き換えます。
# Treasure Data Input and Output <source> type forward port 24224 </source> <match td.*.*> type tdlog apikey YOUR_API_KEY auto_create_table buffer_type file buffer_path /var/log/td-agent/buffer/td </match>
pythonのコードは、下記の通りです。これで、TreasureDataのpidbというDBのusbrhというテーブルに測定データが格納されます。
# センサーからデータ取得 from subprocess import Popen,PIPE p=Popen('usbrh',shell=True,stdin=PIPE,stdout=PIPE,close_fds=True) line = p.stdout.readline() temphum=line.rstrip().split(' ') print temphum temp=float(temphum[0]) hum=float(temphum[1]) # fluentdに送る from fluent import sender from fluent import event sender.setup('td.pidb', host='localhost', port=24224) event.Event('usbrh', { 'temp': temp, 'hum': hum })
定期的に実行
crontab(crontab - Wikipedia)という便利なツールがあったので、これを使いました。
crontab -eで設定、crontab -lで参照でき、実行したいコマンドを「分、時、日、月、曜日」の順番に設定していきます。下記の場合は、上記pythonのコードを10おきに実行するようになるはずです。
0,10,20,30,40,50 * * * * python /home/pi/SendViaFluentd.py
・・・なるはずだったのですが、実行されません。
/var/log/syslogをみると、"(CRON) info (No MTA installed, discarding output)"というメッセージが出力されていました。これを調べて、postfixをいれるとよさそうだったので、下記コマンドでインストール。
sudo apt-get install postfix
そして次の実行タイミング(10分後)がすぎてから、再び/var/log/syslogをみると、"メールが /var/mail/pi にあります"と書かれていたので、/var/mail/piをみてみると、"usbrh: not found"というメッセージがありました。
ということで、上記pythonのコードでセンサーから温度と湿度のデータを取得しているところを、
'usbrh' → 'sudo /usr/local/bin/usbrh'
と変更したら、無事10分おきに実行されてTreasureDataに格納されるようになりました。
ただずっと30度超えていて、なんか暑すぎるような・・・。それより体調を崩してしまい、自分の熱が38度くらいあるので、自分の熱をモニタリングしてみれるほうが今はずっと助かる・・・。
少し手を加えようとするだけでも何かしらエラーが出るので、意外に時間がかかってしまいました。明日はTreasureDataにためたこのデータをAWS上に、d3.jsで可視化する画面を作ろうと思います。
*1:ここが今回やるなかで技術的に見えていないところ。