AWSで遊ぶ 〜M5StickC (ESP32) とAWS IoTのMQTT双方向通信〜

プログライズライターが概ね好評を頂いてひと安心し、また現在、(ちょっとしたものはあれども)特段作りたいものもないので、自分のものづくりの幅を広げるために、しばらくお勉強フェーズに入ります。ということで、AWS IoTとM5StickCをMQTTで繋いで双方向通信させてみました。ESP32(←M5StickCは正確にはESP32-picoですが)を使用しているということで、既に色々なところで情報が出ている話だとは思いますが、自分の勉強も兼ねて、改めて自分なりにまとめてみます。

自分のAWS(←言わずもがな、Amazon Web Services)の利用歴ですが、「仕事でとてもよく耳にするけれど、自分ではほぼ触ったことがないので、その場ではわかっている雰囲気で会話している」というレベルです。そこから自分の勉強のために色々調べながら書いていっておりますので、間違ったことを書いてしまっていたら大変申し訳ありません、ご了承ください。

やりたいことのイメージ

イメージはこんな感じです。M5StickCとAWSのどちらからもメッセージの送信が可能な状態にします。トピックを一つにまとめても良いのですが、そうすると、どちらも自分がPublishしたものをSubscribeによって自分でReceiveすることになってしまい、それがあまり好きではないので、「デバイスからの送信用」と「クラウドからの送信用」でトピックを分けるようにしています。MQTTのことは正確には理解できていないので、何か定石から外れているようなら申し訳ありません。

AWS側の作業

前提条件(IAM)

AWSのアカウント自体は2014年頃に作成していたのですが、そこから一度も使っていなかったので、まずは管理者用と開発者用のIAM (Identitiy and Access Management) ユーザの作成からスタートしました。ルートユーザや管理者用IAMユーザで諸々の作業を進めていくのは、何でもやれ過ぎてお作法的に宜しくない、とのことなので。確かに、よくわからないサービスをよくわからないままに使い始めてものすごい費用請求がくる、とかはごめんこうむりたいですので、素直にお作法に従います。

ルートユーザでログインしている状態から管理者用IAMユーザ&グループを作成する手順は、AWSの公式リファレンスの通りなので省略します。

管理者用IAMユーザでログインし直してから、今度は開発者用IAMグループ&ユーザを作成します。とりあえず開発者用グループを作成して、以下の2つのポリシーをアタッチしました。

  • AWSIoTFullAccess
  • CloudWatchFullAccess

いきなり”FullAccess”にしてしまって良いのかは気になるところですが、今回は一旦これで。今回の目的である、AWS IoT CoreでMQTT通信を行うだけなら、この2つのポリシーで十分です。CloudWatchの方は、これも設定しておかないとAWS IoT Coreのコンソールのモニタリング画面で何も表示されなくなってしまうから設定したもので、気にならない人は設定しなくて良いと思います。多分。ただ、CloudWatchはAWSを使っていくなら後で必ず使うことになると思うので、設定しておいて良いとも思います。

以下の作業は、上記で作成した開発者用グループに所属させた新規ユーザでログインした状態で進めます。

AWS IoT Coreの設定

コンソールにログインしたら、”IoT Core”とかで検索して、IoT Coreの管理画面にいきます。後々「モノ」にアタッチすることになる「ポリシー」を先に作ってから、「モノ」を作っていきます。「モノ」の作成の中で、併せて「証明書」も作成していきます。

ポリシーの作成

「安全性」→「ポリシー」からポリシーを作成します。

ここで「アクション」「リソースARN」「効果」の3つを設定していくことになるのですが、色々調べていると「まずはお試しで」ということで、

アクション iot:*
リソースARN *
効果 許可

と設定されていることが多い気がします。要するに全許可設定で、まずはこのポリシー設定で「確かに繋がるね」ということを確認することは(繋がらないときの問題切り分けのために)非常に重要なのですが、このまま進んでしまうと結局ポリシーとはなんだったのかというのがよくわからないままですし、最終的なプロダクトを作る際に全許可設定はありえないので、もう少し細かく設定してみます。ちなみに自分はこの記事の執筆時点で「ARNとは何ぞや?」とか言っているレベルです。Amazon Resource Name、AWS上のリソースを一意に特定するための名前、ということですね。

AWS IoTのポリシーですが、「このアクションはどのリソースに対する許可か」を理解していないと設定が難しいです。このへんのことは、公式ドキュメントのこちらに正式な記載がありますが、とりあえず今回使用する範囲での自分の理解は以下になります。

アクション リソース 内容
iot:Connect モノのARN モノに対して、ブローカーへの接続可否を定める
iot:Publish トピックのARN モノに対して、Publish(メッセージ送信)を許可するトピックを定める
iot:Receive トピックのARN モノに対して、Receive(メッセージ受信)を許可するトピックを定める
iot:Subscribe トピックフィルターのARN モノに対して、Subscribe(メッセージの監視)を許可するトピックフィルターを定める

Connectがモノに対する許可であるのは良いとして、まだよくわかっていないのは、PublishとReceiveがトピックの指定であるのに対し、Subscribeがトピックフィルターの指定になっている点です。このあたりの考え方は、いずれわかり次第追記できればと思っていますが、一旦こういうものとして進めていきます。

ここで作成しようしているポリシーは、これからAWS IoTに接続しようとしている「モノ(= M5StickC)」に対する設定なので、

上図の赤枠の範囲内でポリシーを考えていきます。これを踏まえて、とりあえず以下のようにポリシーを作成しました。ポリシーの作成はコンソール上でベーシックモードとアドバンストモードを選んで入力できますが、以下はベーシックモードでの入力を想定しています。

ポリシー名:

MqttOnly

ステートメント1:

アクション iot:Connect
リソースARN arn:aws:iot:[region]:[AWS account ID]:client/myM5StickC
効果 許可

アクションで”iot:Connect”を入力すると、リソースARNには”arn:aws:iot:[region]:[AWS account ID]:client/replaceWithAClientId”までが自動入力されるので、”replaceWithAClientId”の部分を、任意の文字列に置き換えます。ここでは”myM5StickC”にしていますが、ここで設定したクライアントIDをデバイス側のソースコード内に記述するクライアントIDと一致させる必要があるので注意してください。

ベーシックモードでは、「ステートメントを追加」を押すと、追加のステートメントを入力できるようになります。

ステートメント2:

アクション iot:Publish
リソースARN arn:aws:iot:[region]:[AWS account ID]:topic/myTopic/fromDevice
効果 許可

メッセージをPublishするのは、”fromDevice”の方のトピックだけです。

ステートメント3:

アクション iot:Receive
リソースARN arn:aws:iot:[region]:[AWS account ID]:topic/myTopic/fromCloud
効果 許可

メッセージをReceiveするのは、”fromCloud”のトピックからだけです。

ステートメント4:

アクション iot:Subscribe
リソースARN arn:aws:iot:[region]:[AWS account ID]:topicfilter/myTopic/fromCloud
効果 許可

メッセージをSubscribeするのも、”fromCloud”のトピックからだけです。ただし、“topic”ではなく”topicfilter”として許可していることに注意です。

入力できたら、「作成」を押してポリシーの作成を完了させます。

なお、このあたりのポリシーの設定サンプルは、こちらの公式ドキュメントに記載があります。

モノの作成・登録

続いて、「管理」→「モノ」からモノを作成します。

今回は「単一のモノ」を作成します。

「Thing Registryにデバイスを追加」では色々入力項目がありますが、とりあえず名前だけ入れておけばOKです。ここでは”myM5StickC”としましたが、先のポリシーのクライアントIDと特に一致させる必要はありません。

「モノに証明書を追加」では、「1-Click証明書作成(推奨)」を選びます。

これだけで、

  • このモノの証明書(xxxxxxxxxx-certificate.pem.crt)
  • パブリックキー(xxxxxxxxxx-public.pem.key)
  • プライベートキー(xxxxxxxxxx-private.pem.key)

の3つのファイルが作成されるので、ダウンロードして保存しておきます。このうち、「モノの証明書」と「プライベートキー」の内容は、デバイス側のソースコード内に記述する必要があります(パブリックキーは使用しません)。

また、同じページで「AWS IoT のルート CA をダウンロードする必要があります。」とありますので、その下にある「AWS IoT のルート CA ダウンロード」をクリックすると

こんなページに飛びますので、「サービス認証用の CA 証明書」のリンク先に飛んで、

ここの「Amazon Trust Services エンドポイント (推奨)」の「RSA 2048 ビットキー: Amazon ルート CA 1」のリンク先の文字列を”AmazonRootCA1.pem”などの名前で保存しておきます。これも、後でデバイス側のソースコード内に記述する必要があります。

ルートCAの保存済んだら「有効化」をクリックし、証明書が正常に有効化されたことが通知されたら、「ポリシーをアタッチ」に進みます。

ここで、先ほど作成していたポリシー”MqttOnly”を選択して、モノの作成・登録は完了です。

 

以上でAWS側の作業は一旦終了です。お疲れ様でした。続いてデバイス側の作業に入ります。

デバイス側の作業

前提条件

M5StickCの開発環境は構築済みとします。一応、自分の方でも過去に環境構築した記録がありましたので、必要に応じてご参照ください。

それから、追加でMQTT実装のためのライブラリ”PubSubClient”が必要になります。こちらは、ダウンロード頂いたのち、ソースコードを一部修正する必要があります(MQTT_MAX_PACKET_SIZEの変更)。このあたりは以下を参照させて頂きました。

ソースコード作成

M5StickCの機能要件としては以下になります:

  • Aボタン(「M5」のボタン)を押すと、整数値をAWSに送信・カウントアップ
  • AWS側から受信したメッセージを表示(JSONはパースして表示)

以下、ソースコードの全文です。一部(ボタン処理など)、M5StickCに特化した記述が含まれていますが、こういう記事を読む人なら、おそらく純粋なESP32開発ボードやM5Stack用に修正するのは容易かと思います。

#include <M5StickC.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <Arduino_JSON.h>

// M5StickCのディスプレイ用定義
#define DISP_X         2
#define DISP_TITLE_Y   0
#define DISP_STATUS_Y 10
#define DISP_TX_Y     30
#define DISP_RX_Y     50

// WiFi設定情報(自分の無線LAN環境)
const char* SSID = "xxxxxxxxxxxxxx";
const char* PASSWORD = "xxxxxxxxxxxxx";

// AWS IoT設定情報
const char* AWS_ENDPOINT = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.amazonaws.com";
const int   AWS_PORT     = 8883;
const char* PUB_TOPIC    = "myTopic/fromDevice";
const char* SUB_TOPIC    = "myTopic/fromCloud";
const char* CLIENT_ID    = "myM5StickC";

const char* ROOT_CA = "-----BEGIN CERTIFICATE-----\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END CERTIFICATE-----\n";

const char* CERTIFICATE = "-----BEGIN CERTIFICATE-----\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END CERTIFICATE-----\n";

const char* PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" \
"-----END RSA PRIVATE KEY-----\n";

// MQTT設定
#define QOS 0 // 届こうが届くまいが1回だけメッセージをPublishする(0か1を指定可)
WiFiClientSecure httpsClient;
PubSubClient mqttClient(httpsClient);

// M5StickCのAボタンを押すたびに加算した値をPublishする
int  pubValue = 0;
char pubMessage[128];

void setup_wifi(){
  Serial.print("Connecting to ");
  Serial.println(SSID);

  // ESP32でWiFiに繋がらなくなるときのための対策
  WiFi.disconnect(true);
  delay(1000);

  WiFi.begin(SSID, PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void setup_awsiot(){
  httpsClient.setCACert(ROOT_CA);
  httpsClient.setCertificate(CERTIFICATE);
  httpsClient.setPrivateKey(PRIVATE_KEY);
  mqttClient.setServer(AWS_ENDPOINT, AWS_PORT);
  mqttClient.setCallback(mqttCallback);
}

void connect_awsiot() {
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect(CLIENT_ID)) {
      Serial.println("Connected.");
      mqttClient.subscribe(SUB_TOPIC, QOS);
      Serial.println("Subscribed.");
      // ディスプレイ表示
      M5.Lcd.setCursor(DISP_X,DISP_STATUS_Y);
      M5.Lcd.print("Connected to AWS.");
    } else {
      Serial.print("Failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" Try again in 5 seconds");
      // 5秒後にリトライ
      delay(5000);
    }
  }
}

void mqttCallback (char* topic, byte* payload, unsigned int length) {
    Serial.print("Received. topic=");
    Serial.println(topic);

    char subMessage[length];
    for (int i=0; i < length; i++) {
      subMessage[i] = (char)payload[i];
    }
    Serial.println(subMessage);

    // JSON前提でパースする
    JSONVar obj = JSON.parse(subMessage);

    // ディスプレイへ受信内容を表示
    M5.Lcd.setCursor(DISP_X,DISP_RX_Y);
    M5.Lcd.printf("RX: %s", (const char*)obj["message"]);
}

void setup(){
  M5.begin();
  M5.Lcd.setRotation(1); // M5StickCのBボタンが上になるような向き
  M5.Lcd.setCursor(DISP_X,DISP_TITLE_Y);
  M5.Lcd.println("AWS IoT MQTT Test");
  M5.Lcd.setCursor(DISP_X,DISP_TX_Y);
  M5.Lcd.print("TX:");
  M5.Lcd.setCursor(DISP_X,DISP_RX_Y);
  M5.Lcd.print("RX:");

  setup_wifi();
  setup_awsiot();
}

void loop(){
  // ボタン全体の状態更新
  M5.update();

  if (!mqttClient.connected()) {
    connect_awsiot();
  }

  // データの取得待ち
  mqttClient.loop();

  if(M5.BtnA.wasPressed()){
    // Publishするメッセージの作成
    sprintf(pubMessage, "{\"message\": \"%d\"}", pubValue);
    Serial.print("Publishing message to topic ");
    Serial.println(PUB_TOPIC);
    Serial.println(pubMessage);
    mqttClient.publish(PUB_TOPIC, pubMessage);
    Serial.println("Published.");
    // ディスプレイへ送信内容を表示。併せて受信内容が既にあれば適当にクリア
    M5.Lcd.setCursor(DISP_X,DISP_TX_Y);
    M5.Lcd.printf("TX: %d", pubValue);
    M5.Lcd.setCursor(DISP_X,DISP_RX_Y);
    M5.Lcd.print("RX:                              ");

    pubValue++;
  }

  delay(5) ;
}

ポイントはL.18〜L.93 AWS IoT設定情報のところです。ここと、冒頭の無線LAN情報だけ、環境に合わせて書き換える必要があります。

  • AWS_ENDPOINTについては、AWS IoTメニューの左下「設定」から参照できる「カスタムエンドポイント」の内容を記載してください。
  • AWS_PORTは固定値です。
  • PUB_TOPICとSUB_TOPICの名前は、AWSのポリシーで記載したトピック名と必ず一致させてください。
  • CLIENT_IDは、AWSのポリシーで記載したクライアントIDと必ず一致させてください。
  • ROOT_CAは、先で保存しておいたAmazon ルートCA1の内容を転記してください。
  • CERTIFICATEは、先で保存しておいた”xxxxxxxxxx-certificate.pem.crt”の内容を転記してください。
  • PRIVATE_KEYは、先で保存しておいた”xxxxxxxxxx-private.pem.key”の内容を転記してください。

ソースコードが作成できたらM5StickCにプログラムを書き込んで、デバイス側の準備も完了です。

動作検証

M5StickCを起動して数秒〜10秒ぐらい待って、”Connected to AWS.”とディスプレイに表示されれば、接続成功です。おめでとうございます。うまくいかない場合は、クライアントIDやトピック名がポリシーとソースコードの間で異なっていないか、エンドポイントや証明書、プライベートキーの転記にミスがないかなどを確認してみてください。

続いて、AWS IoTのコンソール画面の「テスト」をクリックし、「トピックのサブスクリプション」の入力欄に”myTopic/fromDevice”と入力し、「トピックへのサブスクライブ」をクリックしてください。ちなみにこのとき、画面右上に「iotconsole-xxxxxxxxxxxxx-xとして接続」のように表示されていると思いますが、これがMQTTコンソールのクライアントIDのようです。テスト画面に入り直すたびに異なるIDが発行されます(←クリックして切断した後、自分で指定することも可能)。

画面が遷移したあと、M5StickCのAボタンを押してください。M5StickCのディスプレイの”TX”に表示されている整数と同じ値がメッセージとして表示されれば、M5StickC側からのメッセージ送信は成功です。

続いて、AWS側からのメッセージ送信のテストです。「トピックへの発行」をクリックしてください。

入力欄に”myTopic/fromCloud”と入力し、「トピックに発行」をクリックしてください。M5StickCのディスプレイの”RX”に”Hello from AWS IoT Console”と表示されれば、AWS側からのメッセージ送信は成功です。

最後に、M5StickC側の動作検証の様子を動画で載せておきます。

 

というわけで、M5StickCとAWSの双方向通信のトライアルでした。実際やってみた感想ですが、「めっちゃ反応早いな!」というのが正直なところです。宅内でダイレクトに通信してるのかと思うぐらい早かったです。MQTTという通信プロトコルの特性もあるのでしょうが、とりあえず片道分だけの通信に限って見れば、ものすごく早いと感じました。

ただ、ここで終わるとあまり実用的な感じではなさそうなので、次はデバイスから送ったメッセージをAWSのLambdaで処理する、ぐらいのことをやってみたいと思います。が、予定は未定です。

 

 

以下、自分がAWSの勉強用に読んでいる本です。資格をとるつもりはありませんが、AWSのサービスの概要を知るのには便利です。