SlideShare a Scribd company logo
AWS IoTで
家庭内IoTやってみた
土持 昌志
自己紹介
• 土持昌志
• @pampitter
• 株式会社鈴木商店
• JAWS-UG大阪
• 好きなAWSサービス:Lambda、S3
AWS IoTをやってみたきっかけ
AWS IoTをやってみたきっかけ
• 2015年10月に子供が生まれました
• 2015年10月のre:InventでAWS IoT発表
• 風邪・インフルエンザ対策に湿度データを収
集してみよう
AWS IoTの概要
Device Gateway
Device
Gateway
(Broker)
Publisher Subscriber
Publisher
Publisher Subscriber
Subscriber
MQTT
HTTP
WebSocket
MQTT
HTTP
WebSocket
Rule Engine
Action
SELECT humidity
FROM '#'
WHERE humidity <= 40
Rule Query
Device
Gateway
{"temp": 20.0}
{"humidity": 40.0}
{"temp": 22.0}
{"humidity": 70.0}
{"temp": 21.0}
{"humidity": 20.0}
{"humidity": 40.0}
{"humidity": 20.0}
Demo 1
湿度センサー
MQTT
Rule EngineTopic
センサーデータを収集
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from sense_hat import SenseHat
import datetime
import json
sense = SenseHat()
hum = sense.get_humidity()
now = datetime.datetime.now()
hum_json = {
'timestamp': now.strftime("%Y-%m-%d %H:%M:%S"),
'humidity': hum
}
print(json.dumps(hum_json))
var execSync = require('child_process').execSync;
module.exports = {
sensor: function() {
var data = "" + execSync('python ' + __dirname + '/sensor.py');
return JSON.parse(data);
}
}
NodeからPythonへのブリッジ
var awsIot = require('aws-iot-device-sdk');
var bridge = require('./bridge.js');
var device = awsIot.device({
keyPath: './certs/private.pem.key',
certPath: './certs/certificate.pem.crt',
caPath: './certs/root-CA.crt',
clientId: 'pi2_01',
region: 'ap-northeast-1'
});
device
.on('connect', function() {
console.log('connect');
setInterval(function() {
var humidity = bridge.sensor();
console.log(humidity)
device.publish('pi2_01', JSON.stringify(humidity));
}, 1000);
});
AWS IoTにパブリッシュ
Demo 1
Device Shadow
Device Device Shadow
App
• desired:管理アプリなどから指定されたあるべき状態
• reported:デバイスが報告した現在の状態
• delta:desiredとreportedの差分
Device Shadow
{
"desired": {
"color": "white"
},
"reported": {
"color": "white"
}
}
{
"desired": {
"color": "red"
},
"reported": {
"color": "red"
}
}
{
"desired": {
"color": "red"
},
"reported": {
"color": "white"
},
"delta": {
"color": "red"
}
}
Demo 2
LED
Device Shadow Console
LEDを点灯
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from sense_hat import SenseHat
import sys
sense = SenseHat()
argv = sys.argv
colors = {
"red": (255, 0, 0),
"yellow": (255, 215, 0),
"green": (50, 205, 50),
"blue": (0, 0, 255),
"light_blue": (91, 192, 222),
"white": (255, 255, 255),
"black": (0, 0, 0)
}
if colors.has_key(argv[1]):
color = colors[argv[1]]
else:
color = colors['black']
pixels = []
for pix in range(0, 64) :
pixels.append(color)
sense.set_pixels(pixels)
var execSync = require('child_process').execSync;
module.exports = {
sensor: function() {
var data = "" + execSync('python ' + __dirname + '/sensor.py');
return JSON.parse(data);
},
led: function(color) {
execSync('python ' + __dirname + '/led.py ' + color);
return;
}
}
NodeからPythonへのブリッジ
var awsIot = require('aws-iot-device-sdk');
var bridge = require('./bridge.js');
var thingShadows = awsIot.thingShadow({
keyPath: './certs/private.pem.key',
certPath:
'./certs/certificate.pem.crt',
caPath: './certs/root-CA.crt',
clientId: 'pi2_01',
region: 'ap-northeast-1'
});
var clientTokenGet;
var clientTokenUpdate;
thingShadows.on('connect', function() {
thingShadows.register('pi2_01');
setTimeout(function() {
clientTokenGet =
thingShadows.get('pi2_01');
}, 2000);
});
Device ShadowからLEDを操作
thingShadows.on('status', function(thingName, stat,
clientToken, stateObject) {
console.log('received ' + stat + ' on ' + thingName +
': ' + JSON.stringify(stateObject));
if('delta' in stateObject.state && 'color' in
stateObject.state.delta){
var delta_state = stateObject.state.delta.color;
bridge.led(delta_state);
console.log('received delta ' + ' on ' + thingName
+ ': ' + JSON.stringify(stateObject));
clientTokenUpdate = thingShadows.update('pi2_01',
{ "state": { "reported": { "color": delta_state } } });
}
});
thingShadows.on('delta', function(thingName, stateObject)
{
var state = stateObject.state.color;
bridge.led(state);
console.log('received delta ' + ' on ' + thingName +
': ' + JSON.stringify(stateObject));
clientTokenUpdate = thingShadows.update('pi2_01', {
"state": { "reported": { "color": state } } });
});
thingShadows.on('timeout', function(thingName,
clientToken) {
console.log('received timeout ' + ' on ' + operation +
': ' + clientToken);
});
Demo 2
Demo 3
湿度センサー
Rule EngineTopic
LED
Device Shadow
var awsIot = require('aws-iot-device-sdk');
var bridge = require('./bridge.js');
var thingShadows = awsIot.thingShadow({
keyPath: './certs/private.pem.key',
certPath: './certs/certificate.pem.crt',
caPath: './certs/root-CA.crt',
clientId: 'pi2_01',
region: 'ap-northeast-1'
});
var clientTokenGet;
var clientTokenUpdate;
thingShadows.on('connect', function() {
thingShadows.register( 'pi2_01' );
setTimeout( function() {
clientTokenGet = thingShadows.get('pi2_01');
}, 2000 );
setInterval(function() {
var humidity = bridge.sensor();
console.log(humidity)
thingShadows.publish('pi2_01',
JSON.stringify(humidity));
}, 1000);
});
センサーデータを収集/
Device ShadowからLEDを操作
thingShadows.on('status', function(thingName, stat,
clientToken, stateObject) {
console.log('received ' + stat + ' on ' + thingName +
': ' + JSON.stringify(stateObject));
if('delta' in stateObject.state && 'color' in
stateObject.state.delta){
var delta_state = stateObject.state.delta.color;
bridge.led(delta_state);
console.log('received delta ' + ' on ' + thingName
+ ': ' + JSON.stringify(stateObject));
clientTokenUpdate = thingShadows.update('pi2_01',
{ "state": { "reported": { "color": delta_state } } });
}
});
thingShadows.on('delta', function(thingName, stateObject)
{
var state = stateObject.state.color;
bridge.led(state);
console.log('received delta ' + ' on ' + thingName +
': ' + JSON.stringify(stateObject));
clientTokenUpdate = thingShadows.update('pi2_01', {
"state": { "reported": { "color": state } } });
});
thingShadows.on('timeout', function(thingName,
clientToken) {
console.log('received timeout ' + ' on ' + operation +
': ' + clientToken);
});
センサーデータを収集/
Device ShadowからLEDを操作
// データの送受信のみの場合はdevice.publish
device.publish('pi2_01', JSON.stringify(humidity));
// DeviceShadow使用時はthingShadows.publish
thingShadows.publish('pi2_01', JSON.stringify(humidity));
# -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import
from __future__ import division
import json
import boto3
print('Loading function')
iot = boto3.client('iot-data')
def lambda_handler(event, context):
print("Received event: " + json.dumps(event))
humidity = event['humidity']
if humidity < 40:
color = 'blue'
elif humidity >= 40 and humidity <= 70:
color = 'green'
else :
color = 'red'
DeviceShadowを操作する
Lambda Function
try:
shadow_stream = response = iot.get_thing_shadow(
thingName='pi2_01'
)
shadow_string =
shadow_stream['payload'].read().decode('utf-8')
shadow = json.loads(shadow_string)
desired_color = shadow['state']['desired']['color']
if color != desired_color:
payload = {
"state": {
"desired": {
"color": color,
}
}
}
iot.update_thing_shadow(
thingName='pi2_01',
payload=json.dumps(payload)
)
print('Update DeviceShadow delta:
{}'.format(color))
else:
print('DeviceShadow has been updated')
return
except Exception as e:
print(e)
print('Error')
raise e
Demo 3
気をつけたいところ
• DynamoDBにデータが入らない!?
• AWS SDKにIoT関連のものが2つある
DynamoDBにデータが入らない!?
DynamoDBにデータが入らない!?
• Python
• Node.js
• Java
AWS SDKにIoT関連のものが2つある
まとめ
• AWS IoTで簡単にセンサーデータをアップで
きるようになった。
• Device Shadowでリモートからのデバイス制
御がやりやすくなった。
• この冬は風邪を引かなかった!
まとめ
• 家庭の課題解決からIoTを始めよう!

More Related Content

AWS IoTで家庭内IoTをやってみた【JAWS DAYS 2016】

  • 2. 自己紹介 • 土持昌志 • @pampitter • 株式会社鈴木商店 • JAWS-UG大阪 • 好きなAWSサービス:Lambda、S3
  • 4. AWS IoTをやってみたきっかけ • 2015年10月に子供が生まれました • 2015年10月のre:InventでAWS IoT発表 • 風邪・インフルエンザ対策に湿度データを収 集してみよう
  • 6. Device Gateway Device Gateway (Broker) Publisher Subscriber Publisher Publisher Subscriber Subscriber MQTT HTTP WebSocket MQTT HTTP WebSocket
  • 7. Rule Engine Action SELECT humidity FROM '#' WHERE humidity <= 40 Rule Query Device Gateway {"temp": 20.0} {"humidity": 40.0} {"temp": 22.0} {"humidity": 70.0} {"temp": 21.0} {"humidity": 20.0} {"humidity": 40.0} {"humidity": 20.0}
  • 9. センサーデータを収集 # -*- coding: utf-8 -*- from __future__ import division from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from sense_hat import SenseHat import datetime import json sense = SenseHat() hum = sense.get_humidity() now = datetime.datetime.now() hum_json = { 'timestamp': now.strftime("%Y-%m-%d %H:%M:%S"), 'humidity': hum } print(json.dumps(hum_json))
  • 10. var execSync = require('child_process').execSync; module.exports = { sensor: function() { var data = "" + execSync('python ' + __dirname + '/sensor.py'); return JSON.parse(data); } } NodeからPythonへのブリッジ
  • 11. var awsIot = require('aws-iot-device-sdk'); var bridge = require('./bridge.js'); var device = awsIot.device({ keyPath: './certs/private.pem.key', certPath: './certs/certificate.pem.crt', caPath: './certs/root-CA.crt', clientId: 'pi2_01', region: 'ap-northeast-1' }); device .on('connect', function() { console.log('connect'); setInterval(function() { var humidity = bridge.sensor(); console.log(humidity) device.publish('pi2_01', JSON.stringify(humidity)); }, 1000); }); AWS IoTにパブリッシュ
  • 14. • desired:管理アプリなどから指定されたあるべき状態 • reported:デバイスが報告した現在の状態 • delta:desiredとreportedの差分 Device Shadow { "desired": { "color": "white" }, "reported": { "color": "white" } } { "desired": { "color": "red" }, "reported": { "color": "red" } } { "desired": { "color": "red" }, "reported": { "color": "white" }, "delta": { "color": "red" } }
  • 16. LEDを点灯 # -*- coding: utf-8 -*- from __future__ import division from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals from sense_hat import SenseHat import sys sense = SenseHat() argv = sys.argv colors = { "red": (255, 0, 0), "yellow": (255, 215, 0), "green": (50, 205, 50), "blue": (0, 0, 255), "light_blue": (91, 192, 222), "white": (255, 255, 255), "black": (0, 0, 0) } if colors.has_key(argv[1]): color = colors[argv[1]] else: color = colors['black'] pixels = [] for pix in range(0, 64) : pixels.append(color) sense.set_pixels(pixels)
  • 17. var execSync = require('child_process').execSync; module.exports = { sensor: function() { var data = "" + execSync('python ' + __dirname + '/sensor.py'); return JSON.parse(data); }, led: function(color) { execSync('python ' + __dirname + '/led.py ' + color); return; } } NodeからPythonへのブリッジ
  • 18. var awsIot = require('aws-iot-device-sdk'); var bridge = require('./bridge.js'); var thingShadows = awsIot.thingShadow({ keyPath: './certs/private.pem.key', certPath: './certs/certificate.pem.crt', caPath: './certs/root-CA.crt', clientId: 'pi2_01', region: 'ap-northeast-1' }); var clientTokenGet; var clientTokenUpdate; thingShadows.on('connect', function() { thingShadows.register('pi2_01'); setTimeout(function() { clientTokenGet = thingShadows.get('pi2_01'); }, 2000); }); Device ShadowからLEDを操作 thingShadows.on('status', function(thingName, stat, clientToken, stateObject) { console.log('received ' + stat + ' on ' + thingName + ': ' + JSON.stringify(stateObject)); if('delta' in stateObject.state && 'color' in stateObject.state.delta){ var delta_state = stateObject.state.delta.color; bridge.led(delta_state); console.log('received delta ' + ' on ' + thingName + ': ' + JSON.stringify(stateObject)); clientTokenUpdate = thingShadows.update('pi2_01', { "state": { "reported": { "color": delta_state } } }); } }); thingShadows.on('delta', function(thingName, stateObject) { var state = stateObject.state.color; bridge.led(state); console.log('received delta ' + ' on ' + thingName + ': ' + JSON.stringify(stateObject)); clientTokenUpdate = thingShadows.update('pi2_01', { "state": { "reported": { "color": state } } }); }); thingShadows.on('timeout', function(thingName, clientToken) { console.log('received timeout ' + ' on ' + operation + ': ' + clientToken); });
  • 21. var awsIot = require('aws-iot-device-sdk'); var bridge = require('./bridge.js'); var thingShadows = awsIot.thingShadow({ keyPath: './certs/private.pem.key', certPath: './certs/certificate.pem.crt', caPath: './certs/root-CA.crt', clientId: 'pi2_01', region: 'ap-northeast-1' }); var clientTokenGet; var clientTokenUpdate; thingShadows.on('connect', function() { thingShadows.register( 'pi2_01' ); setTimeout( function() { clientTokenGet = thingShadows.get('pi2_01'); }, 2000 ); setInterval(function() { var humidity = bridge.sensor(); console.log(humidity) thingShadows.publish('pi2_01', JSON.stringify(humidity)); }, 1000); }); センサーデータを収集/ Device ShadowからLEDを操作 thingShadows.on('status', function(thingName, stat, clientToken, stateObject) { console.log('received ' + stat + ' on ' + thingName + ': ' + JSON.stringify(stateObject)); if('delta' in stateObject.state && 'color' in stateObject.state.delta){ var delta_state = stateObject.state.delta.color; bridge.led(delta_state); console.log('received delta ' + ' on ' + thingName + ': ' + JSON.stringify(stateObject)); clientTokenUpdate = thingShadows.update('pi2_01', { "state": { "reported": { "color": delta_state } } }); } }); thingShadows.on('delta', function(thingName, stateObject) { var state = stateObject.state.color; bridge.led(state); console.log('received delta ' + ' on ' + thingName + ': ' + JSON.stringify(stateObject)); clientTokenUpdate = thingShadows.update('pi2_01', { "state": { "reported": { "color": state } } }); }); thingShadows.on('timeout', function(thingName, clientToken) { console.log('received timeout ' + ' on ' + operation + ': ' + clientToken); });
  • 22. センサーデータを収集/ Device ShadowからLEDを操作 // データの送受信のみの場合はdevice.publish device.publish('pi2_01', JSON.stringify(humidity)); // DeviceShadow使用時はthingShadows.publish thingShadows.publish('pi2_01', JSON.stringify(humidity));
  • 23. # -*- coding: utf-8 -*- from __future__ import print_function from __future__ import unicode_literals from __future__ import absolute_import from __future__ import division import json import boto3 print('Loading function') iot = boto3.client('iot-data') def lambda_handler(event, context): print("Received event: " + json.dumps(event)) humidity = event['humidity'] if humidity < 40: color = 'blue' elif humidity >= 40 and humidity <= 70: color = 'green' else : color = 'red' DeviceShadowを操作する Lambda Function try: shadow_stream = response = iot.get_thing_shadow( thingName='pi2_01' ) shadow_string = shadow_stream['payload'].read().decode('utf-8') shadow = json.loads(shadow_string) desired_color = shadow['state']['desired']['color'] if color != desired_color: payload = { "state": { "desired": { "color": color, } } } iot.update_thing_shadow( thingName='pi2_01', payload=json.dumps(payload) ) print('Update DeviceShadow delta: {}'.format(color)) else: print('DeviceShadow has been updated') return except Exception as e: print(e) print('Error') raise e
  • 28. • Python • Node.js • Java AWS SDKにIoT関連のものが2つある
  • 29. まとめ • AWS IoTで簡単にセンサーデータをアップで きるようになった。 • Device Shadowでリモートからのデバイス制 御がやりやすくなった。 • この冬は風邪を引かなかった!

Editor's Notes

  1. ラズパイを充電器を接続して起動状態にし三脚にセットする MacとラズパイをポケットWi-Fiにつないでsshしておく 黒曜石を接続して電源を入れておく Macで画面を見せるためにDynamoDBとAWS IoTの画面を開いておく このセッションではAWS IoTで家庭内IoTやってみた、という内容で発表させていただきます。よろしくお願いします。
  2. では本編始めていきます。まず軽く自己紹介させていただきます。名前は土持昌志といいます。普段は大阪で鈴木商店っていうちょっと変わった名前の会社でエンジニアやってます。今朝夜行バスに乗ってこちらに到着しました。AWSサービスではLamdaとS3が最近のお気に入りです。
  3. IoTをやってみたきっかけですが
  4. どういうことをやったのかという前にAWS IoTの概要について確認したいと思います。
  5. IoTの世界ではセンサーから吐き出される小さいけども大量のデータやり取りするためMQTTという軽量・省電力なプロトコルを使うのが主流になっています。メッセージを出す人がパブリッシャー、メッセージを受け取る人がサブスクライバーというんですがMQTTのメッセージのやり取りにはブローカーという中継サーバーが必要ですがAWS IoTではデバイスゲートウェイがこのブローカーの役割をしてくれています。マネージドサービスなのでやりとりするメッセージが増えても自動的にスケーリングしてくれるようです。デバイスゲートウェイではMQTTに加えてHTTP、WebSocketにも対応しています。
  6. モノからデバイスゲートウェイで受け取ったデータをAWSのサービスに流す役割をするのがルールエンジンです。受け取ったデータをRule QueryというところでSQLライクな構文で流す内容を設定し、Actionでどんな処理を行うのかを設定します。例えばデバイス側で温度と湿度を取得していて、湿度が40%以下の時にだけ実行したい処理がある場合、クエリーににそのような条件を記載することで条件にあった場合のみアクションをトリガーすることができます。
  7. そんな感じで最初に作った構成です。ハードウェアはみんな大好きラズベリーパイを使いました。ラズパイに湿度センサーを接続して、センサーのデータをデバイスゲートウェイのMQTTトピックに送信します。ルールエンジンでは受け取ったデータをDynamoDBに流すように設定しました。
  8. ラズパイには幾つかスクリプトを作成しました。資料はまた後日公開します。まずセンサーからデータを読み取るpythonスクリプトです。実行��ると湿度センサーの値を出力します。
  9. 2つ目は先ほどのpythonスクリプトを呼び出すためのnode.jsのスクリプトです。メインのスクリプトから呼び出すためにmoduleにしています。
  10. 最後にAWS IoTにデータをパブリッシュするためのスクリプトです。AWS IoTのデバイスSDKと先ほどのPythonを呼び出すためのブリッジモジュールを読み込んでいます。デバイスSDKっていうのはAWS IoTへのMQTT通信を簡単に行えるようにしてくれるライブラリです。スクリプトの内容としてはAWS IoTに接続できたら一定間隔でデータを収集してパブリッシュするようにしています。
  11. ということでデモをやってみたいと思います。
  12. というわけで湿度センサーの値がDynamoDBに流れるところを見てもらいました。で、次にAWS IoTの肝っぽいDeviceShadowを触ってみます。DeviceShadowというのはモノの仮想コピーをAWS IoT上に作る機能です。たとえモノ自体に通信ができない状態が発生しても仮想コピーに対して通信できるのでアクションを実行できるようになってます。IoTっていうのは通信環境であったりとか、電源であったりとかの問題で常にオンライン状態が期待できるわけではないのでかなり画期的な機能なんじゃないかと思ってます。
  13. デバイスシャドウを使った状態管理の流れを見てみます。デバイスシャドウの状態パラメータにはdesiredという管理アプリケーションなどから指定したこういう状態にしてくださいというパラメータ、デバイスが報告したreportedというパラメータがまずあります。最初の状態では差分がないので何も起こりません。次に真ん中の状態、管理アプリケーションが色を赤くしてくださいという命令を出したとします。デバイスが報告した状態と差ができるので差分ステータスとしてdeltaという項目が増えます。デバイスがデバイスシャドウを見に来たタイミングでdeltaがあるとデバイスはdeltaの内容で自分の状態を更新し、同じようにDeviceShadowも更新します。差分がなくなったのでdeltaは削除されます。このサイクルが繰り返されることでシャドウとデバイスの状態が同期されるようになります。
  14. では実際にやってみます。二つ目のデモはAWS IoTのマネジメントコンソールからDeviceShadowの状態を更新して、デバイス側のLEDを光らせてみたいと思います。
  15. 今度はLEDを点灯させるためのスクリプトを用意しました。フルカラーLEDなので色のパラメータを幾つか用意していて、引数から持ってきた色の名前があればその色で光るようになっています。
  16. 先ほども出てきたNode.jsからPythonへのブリッジモジュールにはLEDを点灯させる関数を追加しました。
  17. 最後はDeviceShadowから状態を受け取るスクリプトです。Demo1ではデバイスクラスをSDKから読んでいたんですが、DeviceShadowを操作するときはthingShadowクラスを使います。DeviceShadowとthingShadowと二つ名前があるんですがなんで名前が分かれているのか知っている方がいれば後でこっそり教えてください。
  18. ということでデモをやってみたいと思います。
  19. 最後に今実際に自宅で動かしてる構成なんですが、Demo1、2を組み合わせてみたいと思います。湿度センサーの値に合わせてLambdaからDeviceShadowを更新してラズパイのLEDの色を変更して湿度の通知にしています。
  20. 今度はセンサーデータをアップしつつ、デバイスシャドウの状態を見てLEDを操作するスクリプトです。もうちっちゃくてわかんないと思うので雰囲気だけみてください。
  21. ちなみにthingshadowクラスはdeviceクラスも内包しているので同じような形でpublishすれば送信されます。
  22. デバイスからの報告された湿度のデータによってDeviceShadowのLEDの色を変更するLambdaFunctionはこんな感じになってます。個人的にJavascriptよりPythonの方が好きなのでPythonで書いてみました。SDKを読み込んで出してます
  23. ということでデモをやってみたいと思います。
  24. まず最初にいくらデータを送ってもDynamoDBに保存されないというトラブルがありました。原因はIAM Roleの設定が間違ってました。Actionを設定するときに「Create a new Role」を選ぶとIAM Roleの作成画面に飛んでその場でロールが作れますが
  25. ここから作るとリソースにきっちりActionで使うテーブル名が指定されているんですね。ポリシーの中身をちゃんと確認しておけばよかったんですが、何回かテーブルを作り直したのにロールを使い回していたのでテーブルへのアクセス権がなくデータが入らないというオチでした。ちゃんとロールを個別に作るなりリソースの指定を緩くするなりすれば大丈夫でした
  26. これはデバイスSDKじゃない普通のSDKの方の話なんですがIoT関連でサービスが2つに分かれています。一例でLambdaが対応している言語のものを出してみました。IoTとだけついている方はAWS IoT自体のリソース操作、モノやルールの追加、編集、削除を行う時に使います。Dataがついている方はモノの操作に使用します。DeviceShadowを更新したりですとかトピックにpublishしたりするのに使えるので覚えておいてください。