AWS
RaspberryPi
mosquitto
IoT
OpenJTalk
OriginalTISDay 23

AWS IoTとOpen JTalkではじめる、IoT入門

近年、インターネットと接続して新たな価値が付いたモノ (製品) の登場により、日常生活においても 「IoT」 が身近となりました。

新たな価値が付いたモノの最たる例は、スマートスピーカーです。元々、スピーカーは CD プレイヤーなどと接続し、接続元から送られた音声を発するだけのものでした。そのスピーカーが、インターネットに接続したことにより、様々なことができるようになりました。

開発者の目線では、「AWS IoT」 「Azure IoT Hub」 などのクラウドサービスが登場した後、これらのクラウドサービスを使ってIoTを簡単に開発できるようになったと言われています。

簡単な音声システムで IoT に触れてみよう

今回は、私が講師を行っている IoT のワークショップで使用しているサンプルを使い、これから IoT の開発を始める人に向けて、クラウドサービスを利用した IoT の開発の基礎を解説したいと思います。

サンプルは、AWS IoT を介してサーバと Raspberry Pi を接続し、Raspberry Pi にインストールした Opem JTalk がメッセージを読み上げるという、簡単な音声システムです。

image.png
image.png

今回紹介する IoT のサンプルは、Raspberry Pi と、Raspberry Pi に接続できる 3.5mm ミニプラグのスピーカーがあれば、すぐに試してみることができます。Raspberry Pi とスピーカー以外のものは、インターネット上のサービスになりますので、本記事を読みながらでもすぐに用意することができます。

この記事をお読みになってサンプルを試された方が、IoT の理解を深め、興味を持ち、IoT の開発や企画に携わるきっかけとなれば、これ以上の喜びはございません。

(ご注意) Raspberry Pi に差し込む、OS インストール済の SD カードを準備する手順は、本記事では割愛しております。参考リンクを紹介させていただきます。

本記事をベースにしたワークショップを開催いたしました

2017/12/22に、Tech-Circle の第26回イベントとして、ワークショップを開催いたしました。

このワークショップでは事前に用意した環境を使いましたが、本記事では1から環境を構築する手順を紹介しております。

まずは IoT の基本を押さえる

サンプルを試す前に、IoT の考え方を簡単に説明します。この考え方が頭に入っていると、この後、サンプルを試していただく際の理解がより深まると思います。

IoT

IoT の定義と考え方は様々なところで語られていますが、簡単に考えると、以下の図のようになります。以降の説明のため、以下の図で書かれている「デバイス制御」「データの受信/蓄積」「データの分析/集計」を、「IoT の三要素」と呼ぶことにします。

image.png

図を使って IoT の本質的な処理の流れを説明すると、「センサが読み取ったデータをデバイスが受け取り、デバイスがネットワーク機器を通じてサーバにデータを送り、データベースに蓄えられたデータをアプリケーションで分析して可視化する」となります。この流れを簡単に表した図が、以下となります。

image.png

この流れが分かると、後に、先に述べた「クラウドサービスを使って IoT を簡単に開発できるようになった」という説明を聞いた時の納得感が増すのではないかと思われます。この流れをクラウドサービスで実現できるようにしたものが 「AWS IoT」 「Azure IoT Hub」 などのクラウドサービスと、これらの周囲にある別のクラウドサービス群 (例えば、LambdaやS3など) になります。

また、デバイスやアプリは 「MQTT サーバ」を介してデータ (メッセージ) を送り合います。メッセージを送り合う時には 「MQTT」 というプロトコルを使います。 MQTT で送るメッセージは、軽量 (プロトコルヘッダが最小2バイト) で、短時間/大量のメッセージ送信に適し、デバイスの消費電力を抑えられるメリットもあります。

メッセージを送り合うモデルは 「Publish/Subscribe モデル」と言われるものです。MOTT サーバにはメッセージのトピックがあり、送信者がトピックにメッセージを送信 (Publish) し、受信者がトピックからメッセージを購読 (Subscribe) します。以下の図のような形です。送信者が受信者に直接メッセージを送るのではない点がポイントです。

image.png

このモデルでは、送信者は MQTT サーバにメッセージを送りさえすれば、トピックを購読している受信者にメッセージが届きます。MQTT サーバの先にどれだけ多くのデバイスや、複雑な転送先があろうとも、MQTT サーバの宛先さえ分かれば、メッセージを送ることができます。

AWS IoT

先ほども述べたように、 「AWS IoT」 と、AWS内の他のクラウドサービスによって、IoT の三要素がAWSで実現できるようになりました。

image.png

AWS IoTのようなクラウドサービスがなかった時のことを考えてみると、AWS IoT の最大のメリットが分かります。IoT では、非常に多くのデバイスの間でメッセージを送り合うことになりますが、この時、非常に多くのメッセージが飛び交います。AWS IoT のようなクラウドサービスがなければ、負荷に耐えるインフラを自前で作らなければなりません。AWS であれば、巨大なデータセンタ (インフラ) の上で動く 「IoT の三要素」 を短期間で用意することができます。

IoT のサンプルを動かし、IoT を理解する

さて、IoT の仕組みを、冒頭で述べた 「IoT のサンプル」 を作って試してみましょう。まず、サンプルの「音声通知システム」の構成図を示します。次に、構成図中に登場する AWS IoT の設定、EC2 の設定、Raspberry Pi の設定を行い、指示スクリプトと指示確認スクリプトを作っていきます。

構成図

今回作る「音声通知システム」の構成は以下の通りです。

image.png

AWS IoTを設定する

AWS IoT を設定するには、AWS のアカウントが必要です。以下のリンク先の手順に従って、AWS のアカウントを作成してください。アカウントの作成が終わったら、次の手順に進みます。

https://aws.amazon.com/jp/register-flow/

まず、AWS アカウントにログインし、AWS IoT の管理画面に進みます。

https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/dashboard

image.png

次に、ポリシーを作ります。ポリシーによって、AWS IoT へのアクセス権限を制御することができます。

image.png

ポリシーの名前は好きな文字列で構いません。今回はサンプルなので、全てのアクションとリソースARN を許可します。許可するアクションを具体的に定義すれば、アクセス権限を必要最小限にすることができます。リソースARN を具体的に定義すれば、特定のトピックだけでメッセージを送り合えるようにできます。最後に「許可」にチェックを入れ、作成ボタンを押します。

image.png

次にモノを設定します。モノを設定することによって、AWS IoT に接続するためのエンドポイントの URL が発行されます。

image.png

「単一のモノを作成する」を押します。

image.png

今回は名前だけを入力し、次に進みます。

image.png

次に証明書を作成します。この証明書がないと AWS IoT に接続できません。作成した証明書を、AWS IoT
に接続するデバイスにコピーして使います。

image.png

作成した証明書をダウンロードします。ダウンロードした証明書の名前 (例の場合、e8b3251d9c-private.pem.key の 「e8b3251d9c」 の部分) を、以降 [AWS IoTの証明書のID] と表記します。また、同じ画面にリンクがある 「ルートCA」 も併せてダウンロードします。

証明書とルートCA のダウンロードが終わったら、画面上で有効化ボタンを押します。有効化したら、「ポリシーのアタッチ」を押します。

image.png

証明書にポリシーをアタッチ(紐付け)します。この証明書を使って AWS IoT に接続する場合は、先ほど定義したポリシーの権限が許可されることになります。

image.png

モノの設定が終わると、エンドポイントの URL が発行されます。モノの一覧画面から、モノごとの詳細画面に移動して URL を確認します。確認した URL をメモしておきます。確認したURLの中に含まれる文字列(例の場合、a1hr5qxj6otrw8.iot.ap-northeast-1.amazonaws.com の 「a1hr5qxj6otrw8」の部分) を、以降 [AWS IoTエンドポイントのID] と表記します。

image.png

以上で AWS IoT の設定は完了です。

EC2 (送信側) を設定する

今回は送信側を EC2 (AWSで作成できる仮想サーバ) で開発します。Raspberry Pi を2個用意しても良いのですが、手軽さを重視しました。Amazon Linux の t2.micro インスタンスで、EC2 を作成します。(注: Amazon Linux 2 では以降の手順を実施できませんのでご注意ください。)

今回、手順を省略しますが、EC2 を作成するときに、作成先のサブネットは以下の条件を満たしていることが必要です。あらかじめ確認しておきます。

・VPC に Internet Gateway がアタッチされていること
・Route Table でデフォルトゲートウェイ (0.0.0.0/0) が上記の Internet Gateway であること

作成したEC2 には、Elastic IP (固定グローバルIP) を作成してアタッチしておきます。この後説明するコマンドで、Elastic IP が入る部分を [Elastic IPの値] と表記します。

また、EC2 を作成するときも証明書が発行されます。証明書をダウンロードして、作業中のPCに保存しておきます。この後説明するコマンドで、作業中のPCに保存した証明書のパスが入る部分を [EC2の証明書のパス] と表記します。

EC2 が起動したら、SSH で EC2 にログインします。Macの場合、コマンドの例は以下の通りです。作業中の PC の OS が Windows の場合は、Tera Term などのクライアントを使ってログインしてください。

Command
$ ssh -i [EC2の証明書のパス] ec2-user@[Elastic IPの値]

ログインしたら、指示スクリプトと AWS IoT の証明書を配置するためのディレクトリを作成します。今回は以下のようにします。

Command
$ mkdir Publisher
$ cd Publisher
$ mkdir pem

作業中の PC から、SCP などを使い、上記で作成した pem ディレクトリに AWS IoT の証明書とルートCA を置いてください。この時、ルートCA の名前をスペースを含まない簡潔な文字列にするため
rootCA.pem と変更します。(名前を変えておかないと、スクリプトにコマンドを書く時、少々面倒なことになります。)

置いていただくのは以下の3点です。

・ (名前略).pem.crt
・ (名前略).pem.key
・ rootCA.pem

次に、AWS IoT にメッセージを送るために、EC2 に 「Mosquitto」 をインストールします。Mosquitto は Amazon Linux の yum リポジトリに存在しないため、CentOS 6 向けの Mosquitto のリポジトリを追加してからインストールします。以下のコマンドとなります。

Command
$ sudo curl http://download.opensuse.org/repositories/home:/oojah:/mqtt/CentOS_CentOS-6/home:oojah:mqtt.repo -o /etc/yum.repos.d/mqtt.repo
$ sudo yum install mosquitto-clients

Mosquitto のインストールが完了したら、いよいよ指示スクリプトを作成します。

Command
$ cd
$ pwd
/home/ec2-user
$ cd Publisher
$ vi publisher.sh

スクリプトの中では、mosquitto_pub というコマンドで、AWS IoT にメッセージを送ります。mosquiito_pub のヘルプは以下の通りです。

Usage: mosquitto_pub [-h host] [-k keepalive] [-p port] [-q qos] [-r] {-f file | -l | -n | -m message} -t topic
                     [-A bind_address]
                     [-i id] [-I id_prefix]
                     [-d] [--quiet]
                     [-M max_inflight]
                     [-u username [-P password]]
                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]
                     [{--cafile file | --capath dir} [--cert file] [--key file]
                      [--ciphers ciphers] [--insecure]]
                     [--psk hex-key --psk-identity identity [--ciphers ciphers]]
                     [--proxy socks-url]
       mosquitto_pub --help

(中略)
 -d : enable debug messages.
(中略)
 -h : mqtt host to connect to. Defaults to localhost.
(中略)
 -m : message payload to send.
(中略)
 -p : network port to connect to. Defaults to 1883.
(中略)
 -q : quality of service level to use for all messages. Defaults to 0.
(中略)
 -t : mqtt topic to publish to.
(中略)
 --cafile : path to a file containing trusted CA certificates to enable encrypted
            communication.
(中略)
 --cert : client certificate for authentication, if required by server.
 --key : client private key for authentication, if required by server.
(中略)
See http://mosquitto.org/ for more information.

以下の内容で publisher.sh を作成します。 -h オプションで AWS IoT のエンドポイントを指定します。-p オプションで指定している 8883 ポートを使って、AWS IoT に MQTT でメッセージを送ります。-t オプションで送信先のトピックを指定しています。ちなみに、AWS IoT でトピックを設定する (作る) という概念はなく、送信側と受信側で任意に共通のトピック名を指定してコマンドを実行すれば、メッセージを送り合うことができます。

-d オプションはお好みで入れてください。コマンドの実行結果が見たい方は入れましょう。

publisher.sh
#!/bin/bash

MESSAGE=$1
TLS_DIR=/home/ec2-user/Publisher/pem/

mosquitto_pub --cafile $TLS_DIR'rootCA.pem' \
    --cert $TLS_DIR'[AWS IoTの証明書のID]-certificate.pem.crt' \
    --key $TLS_DIR'[AWS IoTの証明書のID]-private.pem.key' \
    -h [AWS IoTエンドポイントのID].iot.ap-northeast-1.amazonaws.com \
    -p 8883 \
    -q 1 \
    -d \
    -t topic/aws-iot-hands-on \
    -m '{"message":"'${MESSAGE}'"}'

publisher.sh を実行してみましょう。mosquitto_pub-d オプションが書かれていれば、実行結果が表示されます。以下のように表示されていれば、メッセージの送信に成功しています。

$ ./publisher.sh "こんにちは"
Client mosqpub|3270-ip-172-31- sending CONNECT
Client mosqpub|3270-ip-172-31- received CONNACK
Client mosqpub|3270-ip-172-31- sending PUBLISH (d0, q1, r0, m1, 'topic/aws-iot-hands-on', ... (29 bytes))
Client mosqpub|3270-ip-172-31- received PUBACK (Mid: 1)
Client mosqpub|3270-ip-172-31- sending DISCONNECT 

以上で EC2 (送信側) の設定は完了です。

Raspberry Pi (購読側) を設定する

EC2 から AWS IoT に送信された指示を確認 (購読) する Raspberry Pi の設定を進めていきます。Raspberry Pi の OS がインストールされている SD カードを、事前に用意しておいてください。また、以下の手順を実施する際には、Raspberry Pi のネットワークの設定が完了してインターネットに接続されている前提での作業となりますので、ご注意ください。

以降、Raspberry Pi の LAN 側 IP アドレス[ラズパイのIPアドレス] と表記します。

EC2 の時と同じく、SSH で Raspberry Pi にログインします。

Command
$ ssh pi@[ラズパイのIPアドレス]

ログインしたら、指示確認スクリプトと AWS IoT の証明書を配置するためのディレクトリを作成します。今回は以下のようにします。

Command
$ mkdir Subscriber
$ cd Subscriber
$ mkdir pem

EC2 の時と同じく、SCP などを使い、pem ディレクトリに AWS IoT の証明書とルートCA を置いてください。置いていただくのは以下の3点です。

・ (名前略).pem.crt
・ (名前略).pem.key
・ rootCA.pem

Raspberry Pi にも 「Mosquitto」 をインストールします。

Command
$ sudo apt-get install mosquitto-clients

後に配置する指示確認スクリプトの中で使用している 「jq」 もインストールします。JSON形式のメッセージの解析に使用します。

Command
$ sudo apt-get install jq

次に、メッセージを読み上げてくれる 「Open JTalk」 と、Open JTalk の音声データをインストールします。

Command
$ sudo apt-get install open-jtalk open-jtalk-mecab-naist-jdic hts-voice-nitech-jp-atr503-m001

さて、ここまで作業を終えた段階で進めても良いのですが、このまま進めると、Open JTalk の声がおじさんのような声となり、おじさんの声が聞こえて作業終了となります。きっと私の好みの問題ですが、女性の声が聞こえる方が、やる気が出ますし、作業の達成感も上がるので、可愛らしい女性の声に変えてしまうことにします。

MMDAgent のサイトから、女性の音声ファイルを入手します。

Command
$ wget https://sourceforge.net/projects/mmdagent/files/MMDAgent_Example/MMDAgent_Example-1.7/MMDAgent_Example-1.7.zip

ダウンロードした音声ファイルを解凍し、mei ディレクトリの中身を /usr/share/hts-voice/ にコピーします。これで可愛らしい女性の声が準備できました。

Command
$ unzip MMDAgent_Example-1.7.zip
$ sudo cp -R MMDAgent_Example-1.7/Voice/mei /usr/share/hts-voice/

最後に、指示確認スクリプトを作成します。

Command
$ cd
$ pwd
/home/pi
$ cd Subscriber
$ vi subscriber.sh

スクリプトの中では、mosquitto_sub というコマンドで、AWS IoT からメッセージを購読します。mosquiito_sub のヘルプは以下の通りです。

Usage: mosquitto_sub [-c] [-h host] [-k keepalive] [-p port] [-q qos] [-R] -t topic ...
                     [-C msg_count] [-T filter_out]
                     [-A bind_address] [-S]
                     [-i id] [-I id_prefix]
                     [-d] [-N] [--quiet] [-v]
                     [-u username [-P password]]
                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]
                     [{--cafile file | --capath dir} [--cert file] [--key file]
                      [--ciphers ciphers] [--insecure]]
                     [--psk hex-key --psk-identity identity [--ciphers ciphers]]
                     [--proxy socks-url]
       mosquitto_sub --help

(中略)
 -d : enable debug messages.
 -h : mqtt host to connect to. Defaults to localhost.
(中略)
 -p : network port to connect to. Defaults to 1883.
(中略)
 -q : quality of service level to use for the subscription. Defaults to 0.
(中略)
 -t : mqtt topic to subscribe to. May be repeated multiple times.
(中略)
 --cafile : path to a file containing trusted CA certificates to enable encrypted
            certificate based communication.
(中略)
 --cert : client certificate for authentication, if required by server.
 --key : client private key for authentication, if required by server.
(中略)
See http://mosquitto.org/ for more information.

以下の内容で subscriber.sh を作成します。

subscriber.sh
#!/bin/bash

TLS_DIR=/home/pi/Subscriber/pem/

mosquitto_sub --cafile $TLS_DIR'rootCA.pem' \
    --cert $TLS_DIR'[AWS IoTの証明書のID]-certificate.pem.crt' \
    --key $TLS_DIR'[AWS IoTの証明書のID]-private.pem.key' \
    -h [AWS IoTエンドポイントのID].iot.ap-northeast-1.amazonaws.com \
    -p 8883 \
    -q 1 \
    -t topic/aws-iot-hands-on | while read line
do
    echo $line | jq ".message" | sed "s/\"//g" > voice.txt
    open_jtalk -m /usr/share/hts-voice/mei/mei_happy.htsvoice \
        -x /var/lib/mecab/dic/open-jtalk/naist-jdic/ \
        -ow test.wav voice.txt
    aplay test.wav
done

以上で Raspberry Pi (購読側) の設定は完了です。

Open JTalk に喋ってもらう

EC2 (送信側) が AWS IoT に送ったメッセージを、Raspberry Pi (購読側) が AWS IoT から購読し、Open JTalk がメッセージを読み上げます。Raspberry Pi にスピーカーを繋いでおきましょう。

image.png

まず SSH で Raspberry Pi (購読側) にログインした後、以下のコマンドを実行します。実行した結果、何も表示されませんが、スクリプトの処理の中で、メッセージを待ち受けているような状態となります。

Command
$ cd
$ pwd
/home/pi
$ cd Subscriber
$ ./subscriber.sh

この状態で別の SSH クライアントを起動し、EC2 (送信側) に SSH でログインした後、以下を実行します。

Command
$ cd
$ pwd
/home/ec2-user
$ cd Publisher
$ ./publisher.sh "こんにちは。"

聞こえましたでしょうか?

最後に

AWS IoT を使った簡単なサンプルを通じて、IoT の仕組みを解説させていただきました。いかがでしたでしょうか。今回のサンプルは非常に簡単なものですが、このサンプルの開発に取り組むだけでも、IoT のイメージが頭に入り、もやもやしていた IoT の理解が少しはっきりするのではないかと思われます。

私が実務で関わった開発では、AWS IoT がメッセージを受け取った後、受け取ったメッセージを別のクラウドサービス (AWS Lambda など) に送信し、クラウドサービス側で処理をさせることがあります。このように設計するアーキテクチャが、個々のサービスを連携させるマイクロサービスアーキテクチャであり、EC2 を使わないのであればサーバレスアーキテクチャとも言えます。細かい設計思想の話は別の方の記事にお譲りしますが、本サンプルで取り組んだ仕組みを応用させることで、これらのアーキテクチャの理解へと、裾野を広げていくこともできます。

記事は以上になります。繰り返しになりますが、この記事をお読みになってサンプルを試された方が、IoT の理解を深め、興味を持ち、IoT の開発や企画に携わるきっかけとなれば幸いです!

参考文献

2018/1/23追記 crash.academy様にワークショップの動画を掲載いただきました

crash.academy様の講座として、2017/12/22に開催されたワークショップの動画を公開いただきました。どなたでも無料でご覧いただくことができます。
https://crash.academy/class/201