FRDM-K64FでAWS IoTのデバイスシャドウのサンプルコードを動かす


FRDM-K64Fは AWS Partner Device Catalog のAWS IoT Coreのサポートには入ってないですが、NXPのSDKに実装例が収録されています。
AWS IoTのハンズオンとして、これを動かしていきたいと思います。
今回はMQTTでスイッチ状態のシャドウとコミュニケーションするサンプルです。

機材

  • FRDM-K64F
  • USBケーブル
  • イーサネットケーブル

WiFiシールドは持っていないので、イーサネットを使用します。
書き込み、デバッグはSWDで行います。

開発環境構築

K64FのOpenOcd環境はこれまでうまくいかなかったことが多いので、MCUXpressoを使います。
ちょっと前までは、この手のEclipseベースのベンダ製IDEはおまけのような印象しかなかったですが、随分使いやすくなったと感じます。ハンズオンには十分です。
SDKとExamplesのダウンロードもMCUXpressoで行えます。本体をダウンロードしてインストールしたら、Welcomeページで"Download and install SDKs"を選択します。

ボードの一覧が表示されるので、K64Fを選択してSDKをインストールします。
Welcomeページを閉じて、左下のQuickstartPanelから"Import SDK example(s)"を選択します。これもウィザードに従って、"aws_examples"の"aws_shadow_enet"をインストールします。

インストールできたら、"Build Project"でビルドできることを確認します。

フラッシュが355K、RAMが150Kのプログラムです。
ここまでできたら、次はK64Fに対応するAws IoT Core側のモノの設定を行います。

AWS IoT Core側のモノの設定

マネジメントコンソールで、AWS IoT Coreにアクセスします。
"管理->モノ->モノの登録->単一のモノを登録"を選択して、Thing RegistryとThing Shadowへの登録を行います。
名前は今回 lightbulb1 としました。タイプとグループはそのままにしておきます。

次に、デバイスの認証をクライアント証明書で行うための証明書を作成します。"1-Click 証明書作成"から作成できるので、作成したファイルはすべてダウンロードしておきます。
ここで 有効化 ボタンで有効化しておく必要があります。

証明書には、認可、拒否するアクション、リソースを記述したポリシーをアタッチする必要がありますが、まだポリシーを作成していないので、一旦AWS IoT Coreのトップページに戻ります。

ポリシーの作成は、"管理->安全性->ポリシー->ポリシーの作成"から行います。
このデモではどんなアクション、リソースを使用するかまだわかりませんので、とりあえずすべてのアクション、リソースに許可するようにします。

作成されたポリシーは、下記のように、IAMのポリシーのようなJSONで記述されています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": "arn:aws:iot:ap-northeast-1:XXXXXXXXXXXX:*"
    }
  ]
}

作成したポリシーは証明書にアタッチする必要があります。
"管理->証明書"から作成した証明書を選択し、作成したポリシー lightbulb-shadow をアタッチします。

以上で、AWS IoT側のモノの設定は完了です。

デバイス側のモノの設定

もう一度MCUXpressoに戻ります。AWS IoT Coreで設定したモノ lightbulb1 をK64F側に設定します。
設定はヘッダファイル中で行うようになっていて、必要な設定は以下の3つです。

  • モノの名前
  • MQTTエンドポイント
  • 証明書

amazon-freertos/demos/include/aws_clientcredential.h を開き、モノの名前のとエンドポイントを設定します。
エンドポイントは、"モノ->lightbulb1->操作"から確認できます。

aws_clientcredential.h
/*
 * @brief MQTT Broker endpoint.
 *
 * @todo Set this to the fully-qualified DNS name of your MQTT broker.
 */
#define clientcredentialMQTT_BROKER_ENDPOINT         "xxxxxxxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com"

/*
 * @brief Host name.
 *
 * @todo Set this to the unique name of your IoT Thing.
 */
#define clientcredentialIOT_THING_NAME               "lightbulb1"

amazon-freertos/demos/include/aws_clientcredential_keys.h を開き、証明書を設定します。

PEMを貼り付けるだけですが、面倒くさい場合は、amazon-freertosのツールを使うと、ヘッダファイルを生成してくれます。
ファイルを選択してヘッダファイルを作成出来ますが、PEMをコピペするだけなので、直接編集しても良いと思います。
証明書(xxxxxxxxxx-certificate.pem.crt)とプライベートキー(xxxxxxxxxx-private.pem.key)を設定します。

aws_clientcredential_keys.h
#define keyCLIENT_CERTIFICATE_PEM \
"-----BEGIN CERTIFICATE-----\n"\
"(略)"\
"-----END CERTIFICATE-----"


#define keyCLIENT_PRIVATE_KEY_PEM \
"-----BEGIN RSA PRIVATE KEY-----\n"\
"(略)"\
"-----END RSA PRIVATE KEY-----"

以上で、デバイス側のモノの設定は完了です。もう一度"Build Project"しておきます。

動作確認

ログを確認するためにシリアルターミナルを開きます。
中央下ペインの Terminal で接続方法を選べるので、このようにシリアルのパラメータを設定して開きます。

"Run->Debug Configurations..."を開いて C/C++ (NXP Semiconductors) MCU Application を選択してデバッグを開始します。
main() でブレークしますが、一度シリアルターミナルに戻って Resume してみます。
MQTTのログが流れて、以下のようになったらデモ動作は正常終了です。

269 80871 [iot_thread] [INFO ][Shadow][80871] Shadow library cleanup done.
270 80871 [iot_thread] [INFO ][MQTT][80871] MQTT library cleanup done.
271 80871 [iot_thread] [INFO ][DEMO][80871] Demo completed successfully.
272 80873 [iot_thread] [INFO ][INIT][80873] SDK cleanup done.

もし、MQTT が Waiting for operation completion. からタイムアウトする場合は、ポリシーによる認可の設定にミスがある可能性が高いです。

では、もう一度、このデモで何をしているのか確認してみます。

まず DEMO タグのログですが、シャドウ lightbulb1 を作成し、powerOn という値をトグルするようにシャドウの更新を20回行っています。

6 6343 [iot_thread] [INFO ][DEMO][6343] Successfully initialized the demo. Network type for the demo: 4
9 6343 [iot_thread] [INFO ][DEMO][6343] Shadow Thing Name is lightbulb1 (length 10).
42 18174 [iot_thread] [INFO ][DEMO][18174] Successfully cleared Shadow of lightbulb1.
43 18174 [iot_thread] [INFO ][DEMO][18174] Sending Shadow update 1 of 20: {"state":{"desired":{"powerOn":1}},"clientToken":"018174"}
51 18377 [iot_thread] [INFO ][DEMO][18377] lightbulb1 changing state from 0 to 1.
53 18377 [iot_thread] [INFO ][DEMO][18377] lightbulb1 sent new state report.
55 18382 [iot_thread] [INFO ][DEMO][18382] Successfully sent Shadow update 1 of 20.
56 18396 [iot_thread] [INFO ][DEMO][18396] Shadow was updated!
57 18464 [iot_thread] [INFO ][DEMO][18464] Shadow was updated!
59 21382 [iot_thread] [INFO ][DEMO][21382] Sending Shadow update 2 of 20: {"state":{"desired":{"powerOn":0}},"clientToken":"021382"}
62 21455 [iot_thread] [INFO ][DEMO][21455] lightbulb1 changing state from 1 to 0.
64 21456 [iot_thread] [INFO ][DEMO][21456] lightbulb1 sent new state report.
65 21458 [iot_thread] [INFO ][DEMO][21458] Successfully sent Shadow update 2 of 20.
66 21463 [iot_thread] [INFO ][DEMO][21462] Shadow was updated!
68 21536 [iot_thread] [INFO ][DEMO][21536] Shadow was updated!

(略)

239 77046 [iot_thread] [INFO ][DEMO][77046] Sending Shadow update 20 of 20: {"state":{"desired":{"powerOn":0}},"clientToken":"077046"}
242 77124 [iot_thread] [INFO ][DEMO][77124] Successfully sent Shadow update 20 of 20.
243 77128 [iot_thread] [INFO ][DEMO][77128] lightbulb1 changing state from 1 to 0.
245 77128 [iot_thread] [INFO ][DEMO][77128] lightbulb1 sent new state report.
246 77136 [iot_thread] [INFO ][DEMO][77135] Shadow was updated!
248 77236 [iot_thread] [INFO ][DEMO][77236] Shadow was updated!
263 80509 [iot_thread] [INFO ][DEMO][80509] Successfully cleared Shadow of lightbulb1.
272 80563 [iot_thread] [INFO ][DEMO][80563] Demo completed successfully.

今回、クライアントは1台だけですが、シャドウの状態は、AWSコンソールからも確認することができました。
"管理->モノ->lightbulb1->シャドウ" をみると、下記のように更新の様子がわかります。

シャドウの取得、更新はMQTTのトピックを通じて行われるということなので、このときのMQTTのログを見てみます。
はじめにいくつかのトピックをSUBSCRIBE, UNSUBSCRIBEして、以降PUBLISHがしばらく続きます。ここでシャドウを更新しているようです。

7 6343 [iot_thread] [INFO ][MQTT][6343] MQTT library successfully initialized.
10 17582 [iot_thread] [INFO ][MQTT][17582] Establishing new MQTT connection.
11 17586 [iot_thread] [INFO ][MQTT][17586] Anonymous metrics (SDK language, SDK version) will be provided to AWS IoT. Recompile with AWS_IOT_MQTT_ENABLE_METRICS set to 0 to disable.
12 17587 [iot_thread] [INFO ][MQTT][17587] (MQTT connection 0x2000eb80, CONNECT operation 0x20013400) Waiting for operation completion.
13 17699 [iot_thread] [INFO ][MQTT][17698] (MQTT connection 0x2000eb80, CONNECT operation 0x20013400) Wait complete with result SUCCESS.
14 17700 [iot_thread] [INFO ][MQTT][17700] New MQTT connection 0x20011188 established.
17 17700 [iot_thread] [INFO ][MQTT][17700] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
18 17700 [iot_thread] [INFO ][MQTT][17700] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013a80) Waiting for operation completion.
19 17769 [iot_thread] [INFO ][MQTT][17769] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013a80) Wait complete with result SUCCESS.
23 17770 [iot_thread] [INFO ][MQTT][17770] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
24 17770 [iot_thread] [INFO ][MQTT][17770] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013400) Waiting for operation completion.
25 17841 [iot_thread] [INFO ][MQTT][17841] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013400) Wait complete with result SUCCESS.
27 17842 [iot_thread] [INFO ][MQTT][17842] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
28 17842 [iot_thread] [INFO ][MQTT][17842] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013938) Waiting for operation completion.
29 17915 [iot_thread] [INFO ][MQTT][17914] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013938) Wait complete with result SUCCESS.
30 17915 [iot_thread] [INFO ][MQTT][17915] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
31 17915 [iot_thread] [INFO ][MQTT][17915] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013510) Waiting for operation completion.
32 17982 [iot_thread] [INFO ][MQTT][17982] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013510) Wait complete with result SUCCESS.
33 17983 [iot_thread] [INFO ][MQTT][17983] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
36 18047 [iot_thread] [INFO ][MQTT][18047] (MQTT connection 0x2000eb80) UNSUBSCRIBE operation scheduled.
37 18047 [iot_thread] [INFO ][MQTT][18047] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x20013510) Waiting for operation completion.
38 18112 [iot_thread] [INFO ][MQTT][18112] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x20013510) Wait complete with result SUCCESS.
39 18113 [iot_thread] [INFO ][MQTT][18113] (MQTT connection 0x2000eb80) UNSUBSCRIBE operation scheduled.
40 18113 [iot_thread] [INFO ][MQTT][18113] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x20013510) Waiting for operation completion.
41 18173 [iot_thread] [INFO ][MQTT][18173] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x20013510) Wait complete with result SUCCESS.
44 18174 [iot_thread] [INFO ][MQTT][18174] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
45 18174 [iot_thread] [INFO ][MQTT][18174] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013618) Waiting for operation completion.
46 18238 [iot_thread] [INFO ][MQTT][18238] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013618) Wait complete with result SUCCESS.
47 18238 [iot_thread] [INFO ][MQTT][18238] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
48 18239 [iot_thread] [INFO ][MQTT][18239] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013510) Waiting for operation completion.
49 18301 [iot_thread] [INFO ][MQTT][18301] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013510) Wait complete with result SUCCESS.
50 18303 [iot_thread] [INFO ][MQTT][18303] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
52 18377 [iot_thread] [INFO ][MQTT][18377] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
60 21383 [iot_thread] [INFO ][MQTT][21383] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
63 21456 [iot_thread] [INFO ][MQTT][21456] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.

(略)

240 77047 [iot_thread] [INFO ][MQTT][77047] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
244 77128 [iot_thread] [INFO ][MQTT][77128] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
249 80130 [iot_thread] [INFO ][MQTT][80130] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
250 80130 [iot_thread] [INFO ][MQTT][80130] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013750) Waiting for operation completion.
251 80210 [iot_thread] [INFO ][MQTT][80210] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013750) Wait complete with result SUCCESS.
252 80210 [iot_thread] [INFO ][MQTT][80210] (MQTT connection 0x2000eb80) SUBSCRIBE operation scheduled.
253 80211 [iot_thread] [INFO ][MQTT][80210] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013400) Waiting for operation completion.
254 80285 [iot_thread] [INFO ][MQTT][80284] (MQTT connection 0x2000eb80, SUBSCRIBE operation 0x20013400) Wait complete with result SUCCESS.
255 80286 [iot_thread] [INFO ][MQTT][80286] (MQTT connection 0x2000eb80) MQTT PUBLISH operation queued.
257 80368 [iot_thread] [INFO ][MQTT][80368] (MQTT connection 0x2000eb80) UNSUBSCRIBE operation scheduled.
258 80368 [iot_thread] [INFO ][MQTT][80368] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x200137b0) Waiting for operation completion.
259 80447 [iot_thread] [INFO ][MQTT][80447] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x200137b0) Wait complete with result SUCCESS.
260 80448 [iot_thread] [INFO ][MQTT][80448] (MQTT connection 0x2000eb80) UNSUBSCRIBE operation scheduled.
261 80448 [iot_thread] [INFO ][MQTT][80448] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x20013400) Waiting for operation completion.
262 80507 [iot_thread] [INFO ][MQTT][80507] (MQTT connection 0x2000eb80, UNSUBSCRIBE operation 0x20013400) Wait complete with result SUCCESS.
264 80509 [iot_thread] [INFO ][MQTT][80509] (MQTT connection 0x2000eb80) Disconnecting connection.
265 80509 [iot_thread] [INFO ][MQTT][80509] (MQTT connection 0x2000eb80, DISCONNECT operation 0x20013400) Waiting for operation completion.
266 80511 [iot_thread] [INFO ][MQTT][80509] (MQTT connection 0x2000eb80, DISCONNECT operation 0x20013400) Wait complete with result SUCCESS.
267 80511 [iot_thread] [INFO ][MQTT][80511] (MQTT connection 0x2000eb80) Connection disconnected.
268 80511 [iot_thread] [INFO ][MQTT][80511] (MQTT connection 0x2000eb80) Network connection closed.
269 80563 [iot_thread] [INFO ][MQTT][80563] (MQTT connection 0x2000eb80) Network connection destroyed.
271 80563 [iot_thread] [INFO ][MQTT][80563] MQTT library cleanup done.

では、シャドウはどんなトピックを通じてやり取りされるのか。
Device Shadow サービスのデータフロー
こちらにシャドウが使用するトピックが説明されていました。

AWSコンソールから、テスト用のMQTTクライアントで接続できるので、シャドウの更新のトピックをサブスクライブして確認します。
"テスト->MQTTクライアント" の "サブスクリプション" に $aws/things/lightbulb1/shadow/update/accepted$aws/things/lightbulb1/shadow/update/delta というトピックを入力してサブスクライブし、もう一度実行します。
すると、それぞれ以下のようにMQTTのメッセージを確認することができました。


まとめ

今回は、FRDM-K64FでSDKに付属のデバイスシャドウのサンプルをそのまま動かすだけで終わりました。
シャドウAPIを使っている箇所を読み込むところまでいけませんでしたが、ここを確認して、
次回はこのサンプルの実装に沿って、シャドウとlightbulbの代わりのLEDを連動させる実装をしてみたいと思います。