投稿日 2019-08-28

AWS LambdaからRaspberry Piで音を出す ~ SORACOM Kryptonでできること ~

ソラコムでインターンをしているmarinです。
インターンでは、ソラコムのサービスを使ったデモ作りをしています。

ところで初対面の人とのコミュニケーションで、誰かが話題を提供してくれると助かると思ったことはありませんか?
そこで私は、LTE-M Buttonを押してRaspberry Piでニュースを喋らせるデモを作りました。

実装内容

  1. 3時間に1回ニュースサイトなどをスクレイピングしてRaspberry Piに保存しておく。
    (ニュースサイトなどから情報を取得する際は、二次利用の規約などを確認するようにしてください。)

    f:id:mm-1107:20190827163312p:plain

  2. ボタンを押してLambdaを実行し、Raspberry Piに接続されたスピーカーから音を出す。

    f:id:mm-1107:20190827114834p:plain

この記事では、デモを作る際に悩んだ、大量生産を考慮しつつセキュアにAWSからRaspberry Piの音を出す方法について紹介します。

まずはじめに、AWSからIoTデバイスを操作するには、AWSのIoT Coreというサービスを利用します。
IoT Coreにデバイスを登録することで、AWS Lambdaからデバイスへメッセージを送ることもできます。
通常は以下の手順でAWSへデバイスの認証を行います。

  1. AWSで証明書を発行
  2. 発行した証明書をローカルのPCにダウンロード
  3. ローカルPCからデバイスに証明書をコピーする

f:id:mm-1107:20190827114926p:plain

(詳しい手順はこちら)

デバイスが1つの場合なら、これでもいいかもしれません。
しかし、デバイスが10個100個…と増えていくと上記の手順(特に証明書をダウンロードしてデバイスに転送)はとても大変です。
そこで、SORACOM Kryptonというサービスを使えば、デバイスが多数の場合の認証が簡単かつセキュアに行えます。

  1. デバイスで証明書の発行をリクエスト
  2. SORACOM KryptonがAWSに証明書の発行をリクエスト
  3. AWSで証明書を発行
  4. SORACOM Kryptonからデバイスに証明書を送信

f:id:mm-1107:20190827115043p:plain

Kryptonを使ったAWSへのデバイス認証方法

Kryptonは証明書を作る権限を付与するとデバイス登録を代行してくれるサービスです。
大まかな流れは以下のようになります。
(SIMの設定は済んでいるものとします。)

  1. AWS IoT操作の実行をデバイスに許可するポリシー(権限)を作成
  2. 証明書を作成可能なポリシーをもつユーザを作成
  3. Kryptonに手順2のユーザを登録
  4. デバイス側で証明書を発行するコマンドを実行 → IoT Coreにデバイスが自動的に登録される(!)

デバイスが増えても、手順4だけでデバイス登録が完了します。

1. AWS IoT操作の実行をデバイスに許可するポリシー(権限)を作成

AWSのIoT Core → 安全性 → ポリシー へ移動し「作成」をクリックします。

f:id:mm-1107:20190827115239p:plain

以下のように埋めて、作成をクリックします。

f:id:mm-1107:20190827124949p:plain

作成したポリシーが一覧に表示されればOKです。

f:id:mm-1107:20190827125007p:plain

2. AWS IAMで証明書を作成可能なポリシーをもつユーザを作成

AWSのIAM → ユーザー へ移動し「ユーザーを追加」をクリックします。

f:id:mm-1107:20190827125022p:plain

以下のように入力し、「次のステップ」をクリックします。

f:id:mm-1107:20190827125037p:plain

「既存のポリシーを直接アタッチ」→ 「AWSIoTThingsRegistration」にチェックを入れます。
(「次のステップ」はまだクリックしないで下さい。)

f:id:mm-1107:20190827125053p:plain

「ポリシーの作成」をクリックします。(別ウィンドウが開きます。)

f:id:mm-1107:20190827125107p:plain

「サービス」に 「IoT」を設定

f:id:mm-1107:20190827125124p:plain

「アクション」に「CreateKeysAndCertificate」を設定し「ポリシーの確認」をクリックします。

f:id:mm-1107:20190827125140p:plain

「名前」と「説明」を入力し、「ポリシーの作成」をクリックします。

f:id:mm-1107:20190827125157p:plain

「ユーザーを追加」ウィンドウに移動して、先ほど作ったポリシー名を探してチェックを入れます。
「次のステップ」をクリックしてください。
(見つからない場合はリロードボタンを押してください。)

f:id:mm-1107:20190827125212p:plain

タグの追加があれば行い、「次のステップ」をクリックします。

f:id:mm-1107:20190827125228p:plain

内容に間違いがないか確認したら、「ユーザの作成」をクリックします。

f:id:mm-1107:20190827125243p:plain

表示されたアクセスキーIDとシークレットアクセスキーをメモしておいてください。

f:id:mm-1107:20190827125306p:plain

3. Kryptonに手順2のユーザを登録

ソラコムのユーザーコンソール → 「セキュリティ」をクリックします。

f:id:mm-1107:20190827125324p:plain

「認証情報ストア」→ 「認証情報を登録」をクリックします。

f:id:mm-1107:20190827125338p:plain

手順2で作ったユーザのアクセスキーIDとシークレットアクセスキーを入力して登録をクリックしてください。

f:id:mm-1107:20190827125352p:plain

SIMグループの設定から「SORACOM Krypton設定」をONにします。
プラスマークをクリックして「AWS IoT」を選んでください。

f:id:mm-1107:20190827125406p:plain

「AWS リージョン」にはAWSで利用しているリージョン名を入力してください
(オレゴンなら「us-west-2」です。)

「認証情報」には先ほどソラコムの認証情報ストアで作ったものを選択してください。
(この場合は「krypton-aws-iot-publisher」)

「Policy name」は手順1で作成したポリシー名を指定してください。
(この場合は「mything_policy」です。)

「Thing name pattern」には$imsiが含まれている必要があります。($imsiはアクセス元のSIMのIMSIに置換されます。)

「ホスト名」にはAWS IoT → 設定にあるエンドポイントを入力してください。

「ルート認証局証明書」には何も入力しなくてOKです。

f:id:mm-1107:20190827125423p:plain
f:id:mm-1107:20190827125457p:plain

これで手順3は完了です。

4. デバイスで証明書を発行するコマンドを実行

デバイス(Raspberry Piなど)にログインして以下のコマンドを実行します。
(SORACOM Napterを利用するとデバイスにsshできます。)

[/home/pi $] curl -O https://s3-ap-northeast-1.amazonaws.com/soracom-files/kryptonExamples.zip
[/home/pi $] unzip kryptonExamples.zip
[/home/pi $] rm kryptonExamples.zip
[/home/pi $] cd kryptonExamples
[/home/pi/kryptonExamples $] npm install
[/home/pi/kryptonExamples $] ln -sf krypton-iot-cellular krypton-iot
[/home/pi/kryptonExamples $] node iot-bootstrap.js

「connect」と表示されれば設定は完了です。
AWS IoT → 「管理」→ 「モノ」を確認するとデバイスが登録されているはずです。

f:id:mm-1107:20190827125556p:plain

これでSORACOM Kryptonを使ったAWS IoTへのデバイス登録は完了です。

AWS LambdaからRaspberry Piのコマンドを実行させる

AWS IoTへのデバイス登録が済んだところで、本題のLambdaからRaspberry Piで音を出してみます。
手順は以下の通りです。

  1. Raspberry Pi側のコードに少し追記する
  2. AWS LambdaにRaspberry Piのコマンドを実行する処理を実装する

1. Raspberry Piにログインしてサンプルコードに少し追記する

[~ $] ssh -p xxx pi@xxx.xxx.xx.x
[/home/pi $] cd kryptonExamples
[/home/pi/kryptonExamples $] vi iot-bootstrap.js

[Before]

getDeviceBootstrapConfiguration().then((config) => {
console.log(config);
const device = awsIot.device(config);
//
// Device is an instance returned by mqtt.Client(), see mqtt.js for full
// documentation.
//
device
.on('connect', function() {
console.log('connect');
device.subscribe('topic_1');
device.publish('topic_2', JSON.stringify({ test_data: 1}));
});
device
.on('message', function(topic, payload) {
console.log('message', topic, payload.toString());
});
device
.on('error', function(error) {
console.error('error', error);
});
});

以下のように書き換えてください。

[After]

getDeviceBootstrapConfiguration().then((config) => {
console.log(config);
const device = awsIot.device(config);
// add
const execSync = require('child_process');
//
// Device is an instance returned by mqtt.Client(), see mqtt.js for full
// documentation.
//
device
.on('connect', function() {
console.log('connect');
device.subscribe('topic_1');
device.publish('topic_2', JSON.stringify({ test_data: 1}));
});
device
.on('message', function(topic, payload) {
console.log('message', topic, payload.toString());
// add
const jsonData = JSON.parse(payload.toString('utf-8'));
// add
execSync.exec(jsonData.message);
});
device
.on('error', function(error) {
console.error('error', error);
});
});

(※普通のインターネット接続では、ネットワークから持ってきた文字列をそのままコマンドとして実行するのは抵抗があるかもしれませんが、ソラコムのサービスを使うと安全です。ただし、あくまでもデモですので本当に安全かどうかよく検討してください。)

書き換えたら以下のコマンドをRaspberry Piで実行させてください。

[/home/pi/kryptonExamples $] node iot-bootstrap.js

これでRaspberry Pi側の準備は完了です。

2. AWS LambdaにRaspberry Piのコマンドを実行する処理を実装する

※LambdaからAWS IoTを実行するために、こちらを参考にして、関数のロールにAWSIoTFullAccessというポリシーをアタッチしておいてください。以下のようになっていればOKです。

f:id:mm-1107:20190827125843p:plain

Lambdaからスピーカーが繋がれたRaspberry Piでサンプルを再生するなら以下のようにcommand = "aplay /usr/share/sounds/alsa/Front_Center.wav"と指定します。(写真を撮るコマンドや録音するコマンドを実行すれば使い方が広がります。)

import sys
from boto3 import client
import json
def lambda_handler(event, context):
iot = client('iot-data')
# 実行したいコマンド  
command = "aplay /usr/share/sounds/alsa/Front_Center.wav"
payload = {
"message": command
}
try:
iot.publish(
topic='topic_1',
qos=0,
payload=json.dumps(payload, ensure_ascii=False)
)
return "Succeeded"
except Exception as e:
print(e)
return "Failed"

これで「テスト」をクリックしてRaspberry Piから音が出れば完了です!
お疲れ様でした!