
17: CM re:Growth 2014でIntel EdisonとSQSの話をしてきた #cmdevio
よく訓練されたアップル信者、都元です。昨日のエントリーはこちらです。昨日2014/12/16、弊社イベントCM re:Growth 2014 TOKYOを実施し、そこでIntel Edisonのお話をしましたので、本日はそのご紹介。
セッションレポート
セッションタイトルは「体で覚えるSQS!」という何とも中身を想像し難いと評判の悪いものでした。まぁネタっぽいのでご容赦。
SQS
まずSQSのご紹介。QueueなのでFIFOバッファ、producer-consumerパターンの実装がSQSです。メッセージを発生させるproducerと、メッセージを逐次処理するconsumerが居ます。
スケーラビリティと可用性を担保するために、SQSは分散キューとして実装されています。そのため、SQSには以下に挙げるような癖があります。
- メッセージは必ずしもキューに登録された順番では出てこない。たまに順序が逆転することもある。
- 1回のメッセージ入力は、必ずしも1回だけ出力されるとは限らない。最低1回は出力するが、たまに2回同じメッセージを出してしまうことがある。
Intel Edison
一方話は変わりまして、Intel Edisonのご紹介。こういったハードウェアは、環境から値を取得する「センサー」と、(あまり聞き慣れないかもしれませんが)環境に対して情報を出力する「アクチュエーター」が0個〜複数個接続されます。アクチュエーターってのは要するに、LEDとかモーターとかブザーとか、そういったヤツです。で、この仲介をするプログラムを走らせるわけです。
Edisonはなかなかのスペックを持っているのにとても小さい、というのは大きな特徴で、ご存知の通りかと。
Amazon SQSとEdisonを組み合わせる
で、SQSとEdisonを絡めるとしたらどうなるか。ぱっと思いつくのは、センサーで得た値をSQSに流し込んで、サーバサイドで何らかの処理をしよう、と。要するにEdisonがproducer側になるパターンです。
でもでも、Edisonがconsumer側になったっていいじゃない? 何するかは置いといて。
デモ1
ということで、3台のEdisonを用意して、デモをしました。SQSにledというメッセージを送ると、どれか1つ(場合によっては2つ?)のLEDが光る、という単純なヤツです。さらにled buzzerというメッセージを送ると、LEDが光りつつ、ブザーがプーーっと鳴る、という仕組みも。
皆様に見えるように掲げてもらうべく、被験者アシスタントを募集しました。ちなみに「健康な成人男性」というのは、薬の治験アルバイトの募集要項だったりして。
デモ2 〜 本番
でもね。光って音鳴って、じゃ面白くないじゃないですか。弊社スタッフにおかれましては、もうちょっと体で覚えて頂き度と思って、こんなものを用意しました。
これを3台。突然のお電話にもかかわらず、2時間足らずで在庫を取寄せて頂きました秋葉原の島山無線商会様にはこの場を借りて御礼申し上げます、ハイ。
これを、こうして、ハンダ付け。とか金曜日の夜酔っ払いながらやってました。
動作確認風景。両方の電極に触るとビリっと来ます。
箱詰め。
あとはもうお分かりですねー。私がSQSでled buzzer shockというメッセージを送ると、アシスタント被験者の誰か1人が感電します。ただしキューはSQSなので、運が悪いと2人感電します!
ご協力頂きました、能登さん、suzuki.ryoさん、せーのさん (@chao2suke)、どうもありがとうございました。ビリっと来たのは能登さんでした。
超怖かった。。。 #cmdevio
— chao2suke (@chao2suke) 2014, 12月 16
この仕組みを作っている間の約3日。何度もLEDを光らせたりして実験しましたが、2機が光ったことは1回しかありませんでした。SQSのメッセージが重複する可能性は、無くはないので考慮しなければならないのだけど、かなりの低確率であることが身にしみてご理解頂けたのではないかとおもいました、まる。
おまけ
セッションではお話しなかった点をいくつか。
EdisonのGPIOから電子びっくり箱を起動する部分には、GROVEのリレーを使いました。このリレーって結構な電力コントロールできるんですねぇ。パトランプを回す、とか簡単にできそうです。
The peak voltage capability is 250V at 10 amps.
また、Edison内アプリケーションの実装はnode.jsで行いました。あまりjs経験無いのでコードはイマイチだと思いますが。あと、AWSの認証まわりとしてはAmazon CognitoのUnAuthRoleを利用しています。
console.log("start BiriBiriWorkerBox"); | |
// ==== config | |
var accountId = "123456789012"; | |
var cognitoRoleArn = "arn:aws:iam::123456789012:role/Cognito_WorkerBoxUnauth_DefaultRole"; | |
var cognitoIdentityPoolId = "us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; | |
var queueUrl = "https://sqs.ap-northeast-1.amazonaws.com/123456789012/workerbox"; | |
// ==== mraa | |
var mraa = require('mraa'); | |
console.log('MRAA Version: ' + mraa.getVersion()); | |
var shockPin = new mraa.Gpio(5); | |
shockPin.dir(mraa.DIR_OUT); | |
var ledPin = new mraa.Gpio(6); | |
ledPin.dir(mraa.DIR_OUT); | |
var buzzerPin = new mraa.Gpio(7); | |
buzzerPin.dir(mraa.DIR_OUT); | |
var onBoardLedPin = new mraa.Gpio(13); | |
onBoardLedPin.dir(mraa.DIR_OUT); | |
onBoardLedPin.write(0); | |
function outputPinLow(pin) { | |
if (pin === null) return; | |
pin.write(0); | |
} | |
function outputPinHigh(pin) { | |
if (pin === null) return; | |
pin.write(1); | |
} | |
function pinHighTimeout(pin, duration) { | |
outputPinHigh(pin); | |
setTimeout(function() { outputPinLow(pin); }, duration); | |
} | |
function errorHandled(dataHandler) { | |
return function(err, data) { | |
if (err) { | |
onBoardLedPin.write(1); | |
console.log(err, err.stack); | |
setTimeout(function(){onBoardLedPin.write(0);}, 2000); | |
} else if (typeof dataHandler === 'function') { | |
dataHandler(data); | |
} else { | |
console.log("unknown handler: " + dataHandler); | |
} | |
}; | |
} | |
// ==== AWS | |
var AWS = require('aws-sdk'); | |
var awsRegion = "us-east-1"; | |
var cognitoParams = { | |
AccountId: accountId, | |
RoleArn: cognitoRoleArn, | |
IdentityPoolId: cognitoIdentityPoolId | |
}; | |
AWS.config.region = awsRegion; | |
AWS.config.credentials = new AWS.CognitoIdentityCredentials(cognitoParams); | |
AWS.config.credentials.get(errorHandled(function() { | |
console.log("Cognito Identity Id: " + AWS.config.credentials.identityId); | |
})); | |
var sqs = new AWS.SQS({region: 'ap-northeast-1'}); | |
function readMessage() { | |
console.log("readMessage"); | |
sqs.receiveMessage({ | |
QueueUrl: queueUrl, | |
MaxNumberOfMessages: 1, | |
VisibilityTimeout: 30, | |
WaitTimeSeconds: 20 | |
}, errorHandled(function(data) { | |
if (data && data.Messages && typeof data.Messages[0] !== 'undefined' && typeof data.Messages[0].Body !== 'undefined') { | |
var message = data.Messages[0]; | |
console.log("message = " + message.Body); | |
if (message.Body.indexOf('led') > -1) { | |
pinHighTimeout(ledPin, 2000); | |
} | |
if (message.Body.indexOf('buzzer') > -1) { | |
pinHighTimeout(buzzerPin, 2000); | |
} | |
if (message.Body.indexOf('shock') > -1) { | |
pinHighTimeout(shockPin, 1000); | |
} | |
sqs.deleteMessage({ | |
QueueUrl : queueUrl, | |
ReceiptHandle: message.ReceiptHandle | |
}, errorHandled(function(){})); | |
} | |
readMessage(); | |
})); | |
} | |
readMessage(); |
UnAuthRoleのポリシーとしては、下記の通りsqs:ReceiveMessageとsqs:DeleteMessageを許可している感じです。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "sqs:ReceiveMessage", "sqs:DeleteMessage" ], "Resource": "arn:aws:sqs:ap-northeast-1:123456789012:workerbox" } ] }
資料
以下、slideshareに上げた資料です。
[slideshare id=42755502&doc=20141216-cm-regrowth-sqs-static-141216065345-conversion-gate01]
明日のエントリーはこちらです。