普通のM5Stack Core2をAWS IoT Coreにつなぐ(MQTT)

GWの宿題シリーズ最終回。

普通のM5Stack Core2をAWS IoTにデバイスとして登録してMQTTでやりとりしてみました。

普通のというのは、実はAWSとM5Stackがコラボ(?)しているM5Stack Core2 ESP32 IoT Development Kit for AWSというモノがあるのだけれども、わたしが見たときには売り切れで入手できなかったのです。

というわけで普通のM5Stack Core2でやってみようということになったのでした。

進めるにあたり、以下のエントリが参考になります。

エントリを参考に、段取りは概ねこんな感じ。

  1. AWS IoT Coreをプロビジョニング
    • モノにデバイスを登録
    • Policy作成
    • 証明書作成・取得
  2. PlatformIOでプログラムを書く
    • 手元のWiFiや、↑で取得した証明書を書き込む
    • Run!

M5Stack Core2側のコードはこんな感じ(※注:C/C++初心者)。

#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <WiFi.h>
#include <M5Core2.h>

const String SSID = "CHANGEME";
const String WIFI_PASSWORD = "CHANGEME";

const String AWS_IOT_ENDPOINT = "CHANGEME-ats.iot.ap-northeast-1.amazonaws.com";
const int AWS_IOT_PORT = 8883;
const String AWS_IOT_DEVICE_NAME = "CHANGEME";

const String pubTopic = "CHANGEME";
const String subTopic = "CHANGEME";
const int subQoS = 0;

// AmazonRootCA1.cer
static const char AWS_IOT_ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
...CHANGEME...
-----END CERTIFICATE-----
)EOF";

// XXXXXXXXXX-certificate.pem.crt
static const char AWS_IOT_DEVICE_CERT[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
...CHANGEME...
-----END CERTIFICATE-----
)KEY";

// XXXXXXXXXX-private.pem.key
static const char AWS_IOT_DEVICE_PRIVATE_KEY[] PROGMEM = R"KEY(
-----BEGIN RSA PRIVATE KEY-----
...CHANGEME...
-----END RSA PRIVATE KEY-----
)KEY";

WiFiClientSecure httpsClient;
PubSubClient mqttClient(httpsClient);

void connect()
{

    while (WiFi.status() != WL_CONNECTED)
    {
        WiFi.begin(SSID.c_str(), WIFI_PASSWORD.c_str());
        Serial.println("Waiting for WiFi... ");
        while (WiFi.status() != WL_CONNECTED)
        {

            delay(1000);
        }
    }
    Serial.println("WiFi connected");
    Serial.printf("IP address: %s\n", WiFi.localIP().toString().c_str());

    Serial.println("[AWS IoT]");
    while (!mqttClient.connected())
    {
        if (!mqttClient.connect(AWS_IOT_DEVICE_NAME.c_str()))
        {
            Serial.print("Connect Failed. Error state=");
            Serial.print(mqttClient.state());
            Serial.println("");
            delay(10000);
        }
    }
    Serial.println("Connected.");
    if (mqttClient.connected())
    {
        if (mqttClient.subscribe(subTopic.c_str(), subQoS))
        {
            Serial.printf("Subscribed.\ntopic=%s\n", subTopic.c_str());
        }
        else
        {
            Serial.printf("Subscribe Failed.\nError state=%d\n", mqttClient.state());
        }
    }
}

void mqttCallback(char *topic, byte *payload, unsigned int length)
{
    DynamicJsonDocument doc(4096);
    deserializeJson(doc, payload);

    const char *message = doc["message"];
    Serial.println("-----");
    Serial.println(message);
    Serial.println("-----");
}

void loop()
{
    if (WiFi.status() != WL_CONNECTED || !mqttClient.connected())
    {
        connect();
    }
    mqttClient.loop();
    delay(10000);
}

void setup()
{
    M5.begin(true, true, false, true);
    Serial.begin(9600);

    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(2);
    M5.Lcd.clear(BLACK);
    M5.Lcd.println("Connect to AWS IoT Core");

    httpsClient.setCACert(AWS_IOT_ROOT_CA);
    httpsClient.setCertificate(AWS_IOT_DEVICE_CERT);
    httpsClient.setPrivateKey(AWS_IOT_DEVICE_PRIVATE_KEY);
    mqttClient.setServer(AWS_IOT_ENDPOINT.c_str(), AWS_IOT_PORT);
    mqttClient.setCallback(mqttCallback);

    connect();
}

困った話1:動作確認方法

AWS Management ConsoleにMQTTのテストクライアントがあるので便利に使えました。

amc-aws-iot-mqtt-client

vscode-pio-serial-out

困った話2:接続できないときのデバッグ

とっても難しい。 MQTTのクライアントから得られる情報が、、、ほぼない(わたしが知らないだけかも)。

というわけでCloudWatchを見ましょう。 なんとなく意味はわかりますね(?)

  • Connect.Success
  • Connect.AuthError
  • Subscribe.Success
  • Subscribe.AuthError

aws-iot-mqtt-cloudwatch

困った話3:AWS IoTに接続できない

  • mqttClient.connect()false
  • mqttClient.status()-1

CloudWatchを見ると Connect.AuthError になっているようでした。

→デバイスに対するPolicyを以下に変更して改善(123456789012はアカウントID)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/${iot:Connection.Thing.ThingName}"
      ]
    }
  ]
}

困った話4:トピックをサブスクライブできない

  • mqttClient.subscribe()true
  • でも実際はsubscribeできていない(MQTTが切断されてるっぽくて再接続しちゃう)

CloudWatchを見ると Connect.SuccessSubscribe.AuthError になってました。

→デバイスに対するPolicyにトピック関連を追加して改善(123456789012はアカウントID)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/${iot:Connection.Thing.ThingName}"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:topic/*",
        "arn:aws:iot:ap-northeast-1:123456789012:topicfilter/*"
      ]
    }
  ]
}

まとめ

なんと今日見たら在庫が復活してました
M5Stack Core2 for AWS - ESP32 IoT開発キット - SWITCH-SCIENCE

お好きなほうをどうぞ!!!


See also