Raspberry Pi を AWS IoT に接続する

 今回はRaspberry PiをAWS IoTに接続してみたいと思います。AWS IoTとは簡単に言うと、IoTデバイスをAWS上のプラットフォームに登録しておき、デバイスの状態を記録するとともに、複数のデバイス間やデバイスとAWSサービス間の通信のハンドリングを行うことができるサービスです。

docs.aws.amazon.com

 デバイスとAWS IoTプラットフォーム間のプロトコルとしては MQTT, HTTP, WebSocket に対応していて、AWSサービスとの連携としては Lambda や DynamoDB、Kinesis、SNSなどに対応しています。

 今のところまだどういった連携をしていくか決めていませんが、ひとまずは Raspberry Pi を AWS IoT に登録して、接続ができるところまでを行ってみたいと思います。

AWS側の環境構築

 AWS IoT コンソールからデバイスの登録等を行うにはAWS IoT関連の権限が付与されている必要があります。今回はテストということで、 AWSIoTFullAccess ポリシーをアタッチして、AWS IoTに関する全ての権限を付与しました。実際のサービス提供時は最低限の権限に絞って付与した方が良いかと思います。

docs.aws.amazon.com

 権限が付与されたら該当のユーザでAWSマネジメントコンソールにログインし、サービスのリストから AWS IoT を選択して AWS IoT のコンソールへ移動します。

f:id:akanuma-hiroaki:20170701120135p:plain

 今すぐ始める をクリックします。

f:id:akanuma-hiroaki:20170701120553p:plain

 AWS IoT コンソールが表示されますので、 「AWS IoT に接続する」の 接続オプションの表示 をクリックします。

f:id:akanuma-hiroaki:20170701121115p:plain

 今回は Raspberry Pi を接続するので、「デバイスの設定」の 今すぐ始める をクリックします。

f:id:akanuma-hiroaki:20170701121210p:plain

 ステップの案内ページが表示されますので、 今すぐ始める をクリックします。

f:id:akanuma-hiroaki:20170701121336p:plain

 プラットフォームとSDKの選択画面が表示されますので、今回は Linux と Python を選択して、画面右下の 次へ をクリックします。

f:id:akanuma-hiroaki:20170701133125p:plain

 モノ(IoTデバイス)の登録画面になりますので、名前を決めて入力し、 次のステップ をクリックします。

f:id:akanuma-hiroaki:20170701133847p:plain

 作成されるモノの情報と接続キットの情報が表示されますので、 Linux/OSX をクリックして接続キットをダウンロードします。ダウンロードすると画面右下の 次のステップ ボタンが活性化しますので、クリックして次へ進みます。

f:id:akanuma-hiroaki:20170701134046p:plain

 デバイス上でのテスト手順が表示されます。

f:id:akanuma-hiroaki:20170701134441p:plain

Raspberry Pi 側からの接続操作

 ダウンロードした接続キットをscp等で Raspberry Pi 上に転送し、上記手順を実行します。

pi@raspberrypi:~/aws_iot $ ls
connect_device_package.zip
pi@raspberrypi:~/aws_iot $ 
pi@raspberrypi:~/aws_iot $ unzip connect_device_package.zip 
Archive:  connect_device_package.zip
  inflating: raspberry_pi.private.key  
  inflating: raspberry_pi.public.key  
  inflating: raspberry_pi.cert.pem   
  inflating: start.sh                
pi@raspberrypi:~/aws_iot $ 
pi@raspberrypi:~/aws_iot $ ls
connect_device_package.zip  raspberry_pi.cert.pem  raspberry_pi.private.key  raspberry_pi.public.key  start.sh
pi@raspberrypi:~/aws_iot $ 
pi@raspberrypi:~/aws_iot $ chmod +x start.sh 
pi@raspberrypi:~/aws_iot $ 
pi@raspberrypi:~/aws_iot $ ls -l
total 20
-rw-r--r-- 1 pi pi 3620 Jul  1 02:05 connect_device_package.zip
-rw-r--r-- 1 pi pi 1224 Jul  1 02:01 raspberry_pi.cert.pem
-rw-r--r-- 1 pi pi 1679 Jul  1 02:01 raspberry_pi.private.key
-rw-r--r-- 1 pi pi  451 Jul  1 02:01 raspberry_pi.public.key
-rwxr-xr-x 1 pi pi  928 Jul  1 02:01 start.sh
pi@raspberrypi:~/aws_iot $ 
pi@raspberrypi:~/aws_iot $ ./start.sh 

Downloading AWS IoT Root CA certificate from Symantec...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1758  100  1758    0     0  12381      0 --:--:-- --:--:-- --:--:-- 12468

Installing AWS SDK...
Cloning into 'aws-iot-device-sdk-python'...
remote: Counting objects: 116, done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 116 (delta 3), reused 15 (delta 3), pack-reused 92
Receiving objects: 100% (116/116), 117.74 KiB | 0 bytes/s, done.
Resolving deltas: 100% (35/35), done.
Checking connectivity... done.
~/aws_iot/aws-iot-device-sdk-python ~/aws_iot
running install
running build
running build_py
creating build
creating build/lib.linux-armv7l-2.7
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK
copying AWSIoTPythonSDK/MQTTLib.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK
copying AWSIoTPythonSDK/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core
copying AWSIoTPythonSDK/core/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/exception
copying AWSIoTPythonSDK/exception/operationTimeoutException.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/exception
copying AWSIoTPythonSDK/exception/AWSIoTExceptions.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/exception
copying AWSIoTPythonSDK/exception/operationError.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/exception
copying AWSIoTPythonSDK/exception/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/exception
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/shadow
copying AWSIoTPythonSDK/core/shadow/shadowManager.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/shadow
copying AWSIoTPythonSDK/core/shadow/deviceShadow.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/shadow
copying AWSIoTPythonSDK/core/shadow/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/shadow
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/util
copying AWSIoTPythonSDK/core/util/sigV4Core.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/util
copying AWSIoTPythonSDK/core/util/offlinePublishQueue.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/util
copying AWSIoTPythonSDK/core/util/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/util
copying AWSIoTPythonSDK/core/util/progressiveBackoffCore.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/util
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol
copying AWSIoTPythonSDK/core/protocol/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol
copying AWSIoTPythonSDK/core/protocol/mqttCore.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol/paho
copying AWSIoTPythonSDK/core/protocol/paho/client.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol/paho
copying AWSIoTPythonSDK/core/protocol/paho/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol/paho
creating build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol/paho/securedWebsocket
copying AWSIoTPythonSDK/core/protocol/paho/securedWebsocket/securedWebsocketCore.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol/paho/securedWebsocket
copying AWSIoTPythonSDK/core/protocol/paho/securedWebsocket/__init__.py -> build/lib.linux-armv7l-2.7/AWSIoTPythonSDK/core/protocol/paho/securedWebsocket
running install_lib
creating /usr/local/lib/python2.7/dist-packages/AWSIoTPythonSDK
error: could not create '/usr/local/lib/python2.7/dist-packages/AWSIoTPythonSDK': Permission denied
pi@raspberrypi:~/aws_iot $ 
pi@raspberrypi:~/aws_iot $ sudo ./start.sh                                                                                                                                                                                                    

Running pub/sub sample application...
Traceback (most recent call last):
  File "aws-iot-device-sdk-python/samples/basicPubSub/basicPubSub.py", line 18, in <module>
    from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
ImportError: No module named AWSIoTPythonSDK.MQTTLib
pi@raspberrypi:~/aws_iot $ 

 AWSIoTPythonSDK がみつからないということなので、インストールします。

pi@raspberrypi:~/aws_iot $ sudo pip install AWSIoTPythonSDK                                                                                                                                                                                   
Downloading/unpacking AWSIoTPythonSDK
  Downloading AWSIoTPythonSDK-1.1.2.tar.gz (55kB): 55kB downloaded
  Running setup.py (path:/tmp/pip-build-06EpCW/AWSIoTPythonSDK/setup.py) egg_info for package AWSIoTPythonSDK
    
Installing collected packages: AWSIoTPythonSDK
  Running setup.py install for AWSIoTPythonSDK
    
Successfully installed AWSIoTPythonSDK
Cleaning up...
pi@raspberrypi:~/aws_iot $ 

 再度テスト用スクリプトを実行します。

pi@raspberrypi:~/aws_iot $ sudo ./start.sh                                                                                                                                                                                                    

Running pub/sub sample application...
2017-07-01 02:14:37,153 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Paho MQTT Client init.
2017-07-01 02:14:37,153 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - ClientID: basicPubSub
2017-07-01 02:14:37,153 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - Protocol: MQTTv3.1.1
2017-07-01 02:14:37,154 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Register Paho MQTT Client callbacks.
2017-07-01 02:14:37,154 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - mqttCore init.
2017-07-01 02:14:37,154 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Load CAFile from: root-CA.crt
2017-07-01 02:14:37,154 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Load Key from: raspberry_pi.private.key
2017-07-01 02:14:37,155 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Load Cert from: raspberry_pi.cert.pem
2017-07-01 02:14:37,155 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for backoff timing: baseReconnectTime = 1 sec
2017-07-01 02:14:37,155 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for backoff timing: maximumReconnectTime = 32 sec
2017-07-01 02:14:37,155 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for backoff timing: minimumConnectTime = 20 sec
2017-07-01 02:14:37,155 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for publish queueing: queueSize = -1
2017-07-01 02:14:37,156 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for publish queueing: dropBehavior = Drop Newest
2017-07-01 02:14:37,156 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for draining interval: 0.5 sec
2017-07-01 02:14:37,156 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Set maximum connect/disconnect timeout to be 10 second.
2017-07-01 02:14:37,156 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Set maximum MQTT operation timeout to be 5 second
2017-07-01 02:14:37,157 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - Connection type: TLSv1.2 Mutual Authentication
2017-07-01 02:14:37,507 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Connect result code 0
2017-07-01 02:14:37,510 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - Connected to AWS IoT.
2017-07-01 02:14:37,510 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Connect time consumption: 70.0ms.
2017-07-01 02:14:37,511 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Started a subscribe request 1
2017-07-01 02:14:37,559 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - _resubscribeCount: -1
2017-07-01 02:14:37,560 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Subscribe request 1 sent.
2017-07-01 02:14:37,561 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Subscribe request 1 succeeded. Time consumption: 50.0ms.
2017-07-01 02:14:37,562 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Recover subscribe context for the next request: subscribeSent: False
2017-07-01 02:14:39,565 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Try to put a publish request 2 in the TCP stack.
2017-07-01 02:14:39,566 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Publish request 2 succeeded.
Received a new message: 
New Message 0
from topic: 
sdk/test/Python
--------------


2017-07-01 02:14:40,568 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Try to put a publish request 3 in the TCP stack.
2017-07-01 02:14:40,569 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Publish request 3 succeeded.
Received a new message: 
New Message 1
from topic: 
sdk/test/Python
--------------


2017-07-01 02:14:41,571 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Try to put a publish request 4 in the TCP stack.
2017-07-01 02:14:41,572 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Publish request 4 succeeded.
Received a new message: 
New Message 2
from topic: 
sdk/test/Python
--------------


2017-07-01 02:14:42,575 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Try to put a publish request 5 in the TCP stack.
2017-07-01 02:14:42,576 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Publish request 5 succeeded.
Received a new message: 
New Message 3
from topic: 
sdk/test/Python
--------------


2017-07-01 02:14:43,578 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Try to put a publish request 6 in the TCP stack.
2017-07-01 02:14:43,579 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Publish request 6 succeeded.
Received a new message: 
New Message 4
from topic: 
sdk/test/Python
--------------

 エラーなく実行され、メッセージが送信され続けているようです。コンソール側を確認すると、 デバイスからのメッセージを待機中 となっていたところが下記のように変わり、メッセージが受信されていることが確認できます。

f:id:akanuma-hiroaki:20170701134928p:plain

 上記画面の「ステップ 4: デバイスにメッセージを送信する」フォームに「Hello, IoT!!」のように入力して メッセージの送信 をクリックすると、Raspberry Pi 側でメッセージが受信され、下記のように出力されます。

Received a new message: 
Hello, IoT!!
from topic: 
sdk/test/Python

 ここまででひとまずデバイスの登録から接続の確認までは完了です。AWS IoT コンソールのダッシュボードを表示すると、下記のように処理の状況が確認できます。

f:id:akanuma-hiroaki:20170701135615p:plain

f:id:akanuma-hiroaki:20170701135628p:plain

f:id:akanuma-hiroaki:20170701135643p:plain

 テスト用のサンプルはMQTTによる接続なので、プロトコルとしてMQTTがカウントされています。

 ちなみにダッシュボードの情報を表示するには CloudWatch の表示権限が必要になります。今回はテストということで、 CloudWatchFullAccess ポリシーをアタッチしました。実際のサービス提供時は最低限の権限に絞って許可した方が良いかと思います。

 ひとまず今回は接続まででしたが、ダッシュボード上でデバイスの通信状況が確認できるというのはとても便利そうに思えました。他のAWSとの連携や、SORACOM Beamを通しての接続も簡単にできるようなので、色々と組み合わせて動かしていってみたいと思います。