本記事で取り扱うこと
IoTでペットヘルスケア[構想編]
IoTでペットヘルスケア[実装編:センサデータの取得~AWS IoT連携] ←本記事(構想編の続編)です
IoTでペットヘルスケア[実装編:AWSサービス間の連携]←次回です
IoTでペットヘルスケア[構想編]で紹介したアーキテクチャのうち、下記の赤枠内の実装(センサデータの取得からAWS IoTへデータを取り込むまでの部分)について、順を追って説明します。手順は現時点(2017/5)のものであるため、時間が経過している場合には、より便利なものが出ている可能性もありますので最新の情報をご確認ください。
Raspberry Pi 3の準備
利用機材
以下を準備します。
- Raspberry Pi3 Model B
- GrovePi+
- Grove - PIR Motion Sensor
- microSDカード(今回は16GB)
- USB Micro B(給電用)
- その他(セットアップ用)
- HDMIケーブル
- モニタ(HDMI対応)
- USB接続マウス
- USB接続キーボード
- microSDカード・リーダライタ
OSのインストール
RASPBIAN JESSIE WITH PIXELをインストールします。
シンプルにインストールするだけですが、以下のとおりです。
-
RASPBIAN JESSIE WITH PIXELをダウンロード、解凍します。
- 今回は以下のリリースを利用。
- Version:April 2017
- Release date:2017-04-10
- Kernel version:4.4
- 今回は以下のリリースを利用。
-
解答したイメージファイルをmicroSDカードへDDで書き込む。
- Windowsの場合はDD for Windowsなどを利用。
microSDカードをRaspberry Piへ挿入、セットアップ用のその他機器を接続して給電用USBケーブルを接続する。
[オプション] 日本語環境を設定
初期インストール時は英語環境になっているため、必要であれば日本語環境化しましょう。
- OS起動後、MenuからRaspberry Pie Configurationをクリックします。
- このあたりのメニューの名前はOSのバージョンで少しずつ変わっていますので、それらしいものをお探しください。
-
Localisationのタブを選択し、以下を設定する。(※設定後、再起動を促されますが、Noを選択する)
- Locale
- Language: ja(Japanese)
- Country: JP(Japan)
- Character Set: UTF-8
- Time zone
- Area: Japan
- Keyboard
- Country: Japan
- Variant: Japanese
- Locale
- 日本語フォントをインストールする。
- ログイン時のデフォルトユーザ(pi)でターミナルを開き、下記を実行します。
$ sudo apt-get install jfbterm
上記の手順を実行後、OSを再起動すれば日本語化されます。
ネットワーク接続
Raspberry Piをインターネットへ接続できるように設定します。
有線でも無線でも、お好きなほうでOKですが、設置場所を考えると無線のほうが取り回しが楽なのでいいかもしれません。GUIからも設定できるため、特に難しいことはないので説明は割愛します。
[オプション] エディタのインストール
好みの問題かもしれませんが、初期のviは使いづらいためvimをインストールします。
$ sudo apt-get update
$ sudo apt-get install vim
[オプション] tmpのRAMDISK化
長期間の稼働を前提とするため、念のためにファイルI/Oが継続的に行われる部分についてはRAMDISK化しておきます。(そんなに大量に読み書きするわけではないため、気休め程度ですが。)
/etc/fstabに以下を追記し、再起動後に正常にマウントされていることを確認します。
tmpfs /tmp tmpfs defaults,size=32m 0 0
tmpfs /var/tmp tmpfs defaults,size=16m 0 0
tmpfs /var/log tmpfs defaults,size=32m 0 0
node.jsのインストール
今回はnvmでnode.js 6.10を導入します。
- nvmのインストール。実行後、環境変数を再読込 or ターミナルを再起動します。
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.1/install.sh | bash
- node.js 6.10をインストールします。
nvm install v6.10
GrovePiのライブラリをインストール
GrovePiとセンサーを利用するためのライブラリを導入します。
- GrovePiのライブラリをクローンしてインストール
$ mkdir /home/pi/grovepi
$ cd /home/pi/grovepi
$ sudo git clone https://github.com/DexterInd/GrovePi
$ cd /home/pi/grovepi/GrovePi/Script
$ sudo chmod +x install.sh
$ sudo ./install.sh
途中でインストールの継続を聞かれるのでY(Yes)を選択。
- GrovePi+の接続確認。 GrovePi+が接続されている状態で、以下のコマンドを実行します。
$ sudo i2cdetect -y 1
上記のように04が見えていれば、正常にGrovepi+を認識できています。
PIR Motion Sensorのデータ取得
- PIR Motion SensorをD8ポートに接続します。
接続ポートは他でもOKですが、次項のサンプルコードではD8前提となっています。
- センサデータ取得のサンプルコード
0.5秒毎にセンサデータの取得(動きを検知)して、1分ごとの集計結果をファイルに出力するサンプルです。デーモン化する前提ですので、不要な部分はコメントアウトしてあります。動作確認時はコメントアウトを外してprint部分をすれば、0.5秒おきの検知結果を標準出力へ出します。
#長期間運用する場合は、デーモン化してしまうのでsyslogが溢れないようにコメントアウトしたままのほうが良いと思います。
import os
import sys
import time
import grovepi
import datetime
def getMotionSensor():
pir_sensor = 8
motion=0
grovepi.pinMode(pir_sensor,"INPUT")
countPerMin = 0
i = 0
while True:
try:
if i >= 120:
d = datetime.datetime.today()
output = "{ " + "\"countPerMin\":" + str(countPerMin) + ",\"timestamp\":\"" +d.strftime("%Y-%m-%dT%H:%M+09:00")+ "\" }"
print output
f = open('/tmp/motionDetected.json', 'w')
f.write(output)
f.close()
i = 0
countPerMin = 0
else:
i = i + 1
# Sense motion, usually human, within the target range
motion=grovepi.digitalRead(pir_sensor)
if motion==0 or motion==1: # check if reads were 0 or 1 it can be 255 also because of IO Errors so remove those values
if motion==1:
countPerMin += 1
#print ('Motion Detected')
#print i
#else:
#print ('-')
time.sleep(.5)
except IOError:
print ("Error")
def fork():
pid = os.fork()
if pid > 0:
f = open('/var/run/motionsensor.pid','w')
f.write(str(pid)+"\n")
f.close()
sys.exit()
if pid == 0:
getMotionSensor()
if __name__=='__main__':
fork()
- テスト実行
- ハードウェアのPinを読みに行くため、root権限での実行が必要です。
- うまく動作すれば、/tmp/motionDetected.jsonへ以下のような内容が出力されます。
{ "countPerMin":0,"timestamp":"2017-05-05T14:47+09:00" }
こちらのセンサーは、もう少し短い間隔でデータを取得することも可能ですが、ネコ様がトイレに入って出てくるまでの動きを検知したいので、0.5秒おきに検知を行い、1分間(最大120回検知)の中でどのくらい動きがあったのかを返すためのデータ出力を行っています。最終的には、この検知回数を元に通知を行っていきます。
純粋にセンサーしかない場合はともかくとして、ラズパイやEdisonなどの処理能力があるものであれば、センサーの生データそのものよりも、少し加工して使いやすくしたものを作り出したほうがよいのでは、と思います。
データ取得スクリプトのデーモン化
データ取得のスクリプトをOS起動時やプロセス停止時に自動で起動するため、デーモン化します。
#最近はinit.dから変わっていますので、久しぶりに触る方はご注意ください。・・・一瞬、困ったのは私だけ?w
- 上記のgrove_pir_motion_sensor_d.pyを/usr/local/libに配置します。
- /etc/systemd/system に motionsensord.serviceを作成します。
[Unit]
Description=Pir Motion Sensor Daemon
[Service]
ExecStart=/usr/local/lib/grove_pir_motion_sensor_d.py
Restart=always
Type=forking
PIDFile=/var/run/motionsensor.pid
[Install]
WantedBy=multi-user.target
- サービス設定を読み込み、手動でデーモンを起動して動作確認します。
設定のリロード、手動起動
$ sudo systemctl daemon-reload
$ sudo systemctl start motionsensord
デーモンのステータスやファイル出力を確認
$ sudo systemctl status motionsensord
- 自動起動を有効にします。
$ sudo systemctl enable motionsensord
OS再起動後も動作していれば正常に設定できています。
AWS IoTの準備
1年ほど前と比較して、非常に簡単になっています。基本はAWS IoTに示されれる手順に沿って実行するだけです。
#この説明いらないのでは・・・と思いつつ、AWS IoT初学者の方もいらっしゃると思いますので、一通りの流れを紹介ということで。
デバイスの登録
Raspberry PiをAWSに接続するための設定を行います。
AMCからAWS IoTのコンソールを開き、Connect のページを選択します。Configuring a deviceの"Get started"を選択します。
接続までの流れ説明ページが表示されるので、”Get started”を選択します。
環境に応じてSDKをセットアップするためのKitが準備されていますので、利用したいものを選択します。今回はRaspbianなのでLinux/OSXを選択します。また、AWS IoTへの部分を記述する際の言語も選択します。今回はNode.jsを利用します。最後に”Next”を選択します。
AWS IoT上で管理するデバイスの名前を指定します。こちらは、管理者がどのデバイス(今回だとRaspberry Piのどのマシンなのか)が分かればよいため、任意の名前でOKです。指定後、"Next step"を選択
※どこかに設定した値と一致させなければならない、といったことはありません。
接続用Kitのダウンロードを行います。内容を確認後、"Next step"を選択します。
ちなみに、ここまでの操作により、以下の手順が自動的に行われています。(1年前は個別に行う手順でしたので関係が分かりやすかったのですが、今は便利になった反面、何が行われているのか少々分かり難くなっています。)
- デバイスがAWS IoTにデータを送る権限の定義(ポリシー作成)
- A policy to send and receive messages の部分です
- デバイスに配置する認証キーの作成
- A certificate and private key の部分です。Kitに含めれます。
- デバイスと認証キー、ポリシーの関連付け
- 3つを関連付け、特定のデバイスに権利を持たせて認証し、AWS IoTのトピック(MQTTでつかうチャネルのようなもの)へPublish/Subscribeできる状態とします。
デバイスの設定
ダウンロードしたファイルをデバイス(Raspberry Pi)へ転送し、示されている手順を実行します。
※こちらの画面は接続確認にそのまま利用しますので、開いたままにしておいてください。
start.shを実行すると、以下の処理が実行されます。
- 証明書の確認
- aws-iot-device-sdkをはじめ、各種モジュールのインストール
- AWS IoTとの接続テスト
start.sh
# stop script on error
set -e
# Check to see if root CA file exists, download if not
if [ ! -f ./root-CA.crt ]; then
printf "\nDownloading AWS IoT Root CA certificate from Symantec...\n"
curl https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem > root-CA.crt
fi
# install AWS Device SDK for NodeJS if not already installed
if [ ! -d ./node_modules ]; then
printf "\nInstalling AWS SDK...\n"
npm install aws-iot-device-sdk
fi
# run pub/sub sample app using certificates downloaded in package
printf "\nRuning pub/sub sample application...\n"
node node_modules/aws-iot-device-sdk/examples/device-example.js --host-name=av8pngo3zyi88.iot.ap-northeast-1.amazonaws.com --private-key=y_raspberrypi_01.private.key --client-certificate=y_raspberrypi_01.cert.pem --ca-certificate=root-CA.crtroot@raspberrypi:~/workspace/cat_healthcare/gitrepo/connect_device_package#
デバイスに接続し、start.shを実行すると、下記のようにconnectと表示されれば接続が成功しています。
さきほどのAWS IoTの画面に戻ると、Step3の下に、デバイスからPublishされたメッセージが届いているのが分かります。これはstart.shから実行された接続確認用のコードから送られています。
続いて、Step 4のテキストボックスに任意の文字列を入れ、Send messageを選択すると、デバイス(Raspberry Pi)側がSubscribeしているTopicにPublishすることができます。今回はテスト用に”test publish message AAAA”と送ってみました。すると、以下のようにデバイス側で受信することができています。
以上でデバイス側の設定は完了です。
センサデータ取得の連携設定
取得したセンサーのデータを実際にAWS IoTへ送信します。
AWS IoTへのPublish
サンプルですので、べた書き部分が多いですが、以下のようなスクリプトを作成します。今回はconnect_device_packageを利用しやすくるため、同ディレクトリに配置してしまいます。
var awsIot = require('aws-iot-device-sdk');
var fs = require('fs');
var clientId_suffix = 'MotionSensor01';
var deviceId = 'y_raspberrypi_01';
var sensor = 'motion_sensor';
var MQTT_clientID = deviceId + "_" + clientId_suffix;
var topicname = "topic/" + deviceId + "/sensor/" + sensor;
var device = awsIot.device({
keyPath: 'y_raspberrypi_01.private.key',
certPath: 'y_raspberrypi_01.cert.pem',
caPath: 'root-CA.crt',
clientId: MQTT_clientID,
region: 'ap-northeast-1'
});
device.on('connect', function () {
console.log('connect');
setInterval(function () {
var messageJson = JSON.parse(fs.readFileSync('/tmp/motionDetected.json', 'utf8'));
messageJson.clientId = MQTT_clientID;
messageJson.deviceId = deviceId;
var message = JSON.stringify(messageJson);
console.log("Publishing.. " + topicname);
device.publish(topicname, message);
}, 60000);
});
処理としては、モーションセンサのデータ取得結果(motionDetected.json)に対してdeviceIdやclientIdをデータとして含め、1分毎にAWS IoTへ送信しているだけになります。
注意点としては、MQTTの仕様として、同一のトピックに対して接続する際にclientIdごとにコネクションを確立しており、複数のデバイス間で重複していると、セッションの奪い合い(お互いに接続、接続断を繰り返してしまう)ことになります。そのため、一意になるようにclientIdを生成して上げる必要があります。今回はデバイスの名前にセンサーの名前を組み合わせて生成しました。
MQTTのトピック名についても、センサーの種類ごとに分けて生成していく想定になっています。センサーの種類を増やす際には、これらのスクリプトやトピックごと増やすアーキテクチャとなります。AWS IoT側からデータを利用する際は、このトピック名を対象として選択することになるため、複数種類のデータを取り扱う際には、十分に検討して設計が必要です。例えば、途中のパスによって値のグルーピングを行う等。
動作確認は、AWS IoTのテスト用コンソールを利用します。
AWS Iotの”Test”ページを開き、トピック名を入力して"Subscribe to topic"を選択します。
すると、Subscriptionsの中に指定したトピック名が表示されますので、選択します。1分毎にデータを送っているため、成功していれば、以下のようにメッセージを受信(Subscribe)できます。
以上でモーションセンサーで実際に取得したデータをAWS IoTへ連携することができるようになりました。
Publish用スクリプトのデーモン化
publish_data_pirmotion.jsもセンサデータ取得用のスクリプトと同様にデーモン化してしまいます。昔はnode.jsだとforeverなどを利用していた気がしますが、systemdの場合は、Pythonスクリプトと同様でOKです。
- /etc/systemd/system に publish_motionData.serviceを作成します。
[Unit]
Description=publish motion data
After=syslog.target network.target
[Service]
Type=simple
ExecStart=/root/.nvm/versions/node/v6.10.3/bin/node publish_data_pirmotion.js
WorkingDirectory=/root/workspace/cat_healthcare/gitrepo/connect_device_package
KillMode=process
Restart=always
[Install]
WantedBy=multi-user.target
#rootで作成しちゃっていますので、上記のような内容になっています。
#nodeのの実行コマンド部分は、もう少しキレイにやったほうがよいですね。
- サービス設定を読み込み、手動でデーモンを起動して動作確認します。
設定のリロード、手動起動
$ sudo systemctl daemon-reload
$ sudo systemctl start publish_motionData
デーモンのステータスやファイル出力を確認
$ sudo systemctl status publish_motionData
- 自動起動を有効にします。
$ sudo systemctl enable publish_motionData
OS再起動後もAWS IoTへデータが送られていることを確認できればOKです。
センサーの設置
Cat's Restroomにセンサーを設置します。
我が家では、以下のように個室風のトイレが設置されています。
少々わかりにくいのですが、上記の写真のようになっています。2つのカラーボックスを棚として開いている側を向き合わせて置き、片方の背板を出入り口用に切り取っています。中は市販のネコトイレ(すのこ式)です。
#ホワイトペレット最高!
その中に対して、以下のようにセンサーを設置しました。図は真横から見たときの断面図・・・絵心なさすぎてすいません。
入り口の背板(上部)の内側にモーションセンサーを取り付け、トイレ内部の動きを検知するようにしています。配線は2つのカラーボックスの間から通して、ネコ様の出入りの邪魔にならないようにしています。また、トイレ内の上部を中心に検知する配置のため、トイレ掃除等を誤検知しにくく・・・もなっているはずです。
#誤検知については、AWS IoT側で受け取ったデータを処理する際に条件付け(しきい値判定など)を行うことでも回避しています。
この配置で、0.5秒に一回のセンサーデータ取得を行うと、ネコ様がトイレに入るとバッチリ検知ができるようになりました。
おわりに
今回、センサデータを取得してAWS IoTへデータ連携を行うところまでできるようになりました。次回はAWS IoT側で受け取ったデータを活用して召使いへ連携していく部分について記載したいと思います。
また、他にもセンサーはあるのですが、長くなりすぎるため別立てして拡張編でも書いたほうがよさそうです。