M5Stack5 Core2 で M5Stack Basic と同じ AWS IoT コードが動かなかったが修正できたメモ

M5Stack5 Core2 で M5Stack Basic と同じ AWS IoT コードが動かなかったが修正できたメモです。

M5Stack Basic と同じ AWS IoT コードというのは、こちら 記事です。似た状況に遭遇して、もしも参考にされる方がもしいましたら「こういう解決方法もあるかもしれない」というくらいで試してみてください。

起きていた現象

とりあえず上記のコードを動かしてみたら、起動した瞬間に動かなくなるという状況で、とてもこまりました。

シリアルモニタで見ていると

M5Core2 initializing...axp: vbus limit off
axp: gpio1 init
axp: gpio2 init
axp: rtc battery charging enabled
axp: esp32 power voltage was set to 3.35v
axp: lcd backlight voltage was set to 2.80v
axp: lcd logic and sdcard voltage preset to 3.3v
axp: vibrator voltage preset to 2v
OK

このログが出て起動の瞬間で止まりました。

こうなってしまうと、 Serial.print で探ることができないので結構お手上げ。さらに、Serial.print の出す位置を変えてみると突然動くようになったり、変数の呼び出すタイミングを変えると動いたりするなど不可解な現象で困りました。

image

2021/01/24 現在、ボードマネージャで M5Stack のバージョンは 1.0.7。

image

ライブラリのバージョンは 0.0.2 です。

結論としては Serial.begin(115200); をコメントアウトしたら動いた

かれこれ、3か月くらい悩んでいたのですが、

  • コードを見ると最初のところでうまくいかないので M5.begin か Serial.begin で怪しむ
  • M5関数を調整しても再現ができないのであきらめる
  • Serial.begin と Serial.print を全消しすると、解消する
  • Serial.print が使えないとデバッグツラすぎるのでもう少し追い込んでみる
  • 結局のところ Serial.begin をコメントアウトすると動くようになる
  • その状態で追加でコードを加えても今回のエラーは今のところ遭遇していない

本家のサンプルで確認してみると、

たしかに、どこにも Serial.begin が書かれてなくて、いきなり Serial.print してる!対応があっている模様・・・。

もちろん、AWS IoT や MQTT 接続という複雑なことをしないコードの場合 Serial.begin 入れてても動くので、めっちゃ不可解だなという印象です。

エラーが起きているコード

現時点で、エラーが起きているコードはこちらです。AWS IoT にシンプルにつなげるもので、もちろん、M5Stack 用に書き換えると動きます。というか、以前の記事そのままにしてもちゃんと動きます。

AWS IoT Amazon Root CA 1・AWS IoT Device Certificate・AWS IoT Device Private Key の部分は こちら を参考に、以前のように、改行を処理しなくてもそのまま入ります。あとは、Wi-Fi Settingsを設定すると動くものです。

static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
)EOF";

これに、

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

仮にこんな鍵ファイルの情報を入れると

static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-----END CERTIFICATE-----
)EOF";

こんな風になります

というわけで、しかるべき設定をおこなって、このコードをそのまま書き出すと、最初に説明したようなエラーでます。

そして、先述の通り Serial.begin(115200); をコメントアウトすると動くという状況です。 Serial.begin(115200); をコメントアウトしさえすれば、あとは、追加でコードを加えても今回のエラーは今のところ遭遇していないです。

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

// Wi-Fi Settings
char *ssid = "Wi-Fi SSID";
char *password = "Wi-Fi PASSWORD";

// AWS IoT Settings
const char *endpoint = "AWS IoT Endpoint";
const int port = 8883;
char *pubTopic = "/sample123";
char *subTopic = "/sample123";

// AWS IoT Amazon Root CA 1
static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
)EOF";

// AWS IoT Device Certificate
static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
)KEY";

// AWS IoT Device Private Key
static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
)KEY";

WiFiClientSecure httpsClient;
PubSubClient mqttClient(httpsClient);

WiFiMulti WiFiMulti;

void setup()
{
    M5.begin(true, true, true, true);
    
    Serial.begin(115200);  // ここをコメントアウトする
    delay(10);

    M5.Lcd.setTextColor(WHITE);
    M5.Lcd.setTextSize(2);
    M5.Lcd.clear(BLACK);
    M5.Lcd.println("begin....");
    
    Serial.println();
    Serial.println("begin....");

    // We start by connecting to a WiFi network
    WiFiMulti.addAP(ssid, password);

    M5.Lcd.println("");
    M5.Lcd.print("[Wi-Fi] ");
    Serial.println();
    Serial.println("Waiting for WiFi... ");

    while(WiFiMulti.run() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }
    
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    M5.Lcd.println(" Connected.");

    //
    Serial.println("Connected.");
    Serial.println(pubTopic);
    Serial.println(subTopic);
   
    // Configure MQTT Client
    
    httpsClient.setCACert(AWS_CERT_CA);
    httpsClient.setCertificate(AWS_CERT_CRT);
    httpsClient.setPrivateKey(AWS_CERT_PRIVATE);
    mqttClient.setServer(endpoint, port);
    mqttClient.setCallback(mqttCallback);
    
    delay(500);
    
    connectAWSIoT();

    // INFO
    M5.Lcd.println("");
    M5.Lcd.printf("sub: ");
    M5.Lcd.println(subTopic);
    M5.Lcd.printf("pub: ");
    M5.Lcd.println(pubTopic);
  
}
  
void connectAWSIoT() {
  M5.Lcd.println("");
    Serial.println("connectAWSIoT");
    M5.Lcd.print("[AWS IoT] ");
    while (!mqttClient.connected()) {
      Serial.print(".");
      if (mqttClient.connect("ESP32_device")) {
          Serial.println("Connected.");
          M5.Lcd.println("Connected.");
          int qos = 0;
          mqttClient.subscribe(subTopic, qos);
          Serial.println("Subscribed.");
      } else {
          Serial.print("Failed. Error state=");
          Serial.print(mqttClient.state());
          // Wait 5 seconds before retrying
          delay(5000);
      }
    }
}
  
void mqttCallback (char* topic, byte* payload, unsigned int length) {
  
    String str = "";
    Serial.print("Received. topic=");
    Serial.println(topic);
    for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
        str += (char)payload[i];
    }
    Serial.print("\n");
}

void mqttLoop() {
    if (!mqttClient.connected()) {
        connectAWSIoT();
    }
    mqttClient.loop();
}

void loop() {
  mqttLoop();
}

ここにヘトヘトになってたどり着いたあと、Twitterで調べてみたら似たような現象で踏み抜いた方がいて、同じ結論になっていました。

もしかしたら、今後ライブラリ側で対策されるかもしれないし、公式で何か情報で出たら、このナレッジも必要なくなるかもしれませんが、いったんこれで対応していこうと思います。

事象のみから発見した感じで、めっちゃ対症療法みたいで苦しいけど、ひとまず先に進めそうでよかったです。

追記:つっこんで調べてみた

M5.begin まわりで起こっているので、M5Core2/M5Core2.cpp で内容を見ていたところ、

void M5Core2::begin(bool LCDEnable, bool SDEnable, bool SerialEnable, bool I2CEnable, mbus_mode_t mode) {
  // Correct init once
  if (isInited == true) {
    return;
  } else {
    isInited = true;
  }

  // UART
  if (SerialEnable == true) {
    Serial.begin(115200);
    Serial.flush();
    delay(50);
    Serial.print("M5Core2 initializing...");
  }

begin の 3 番目の引数 SerialEnable でシリアルポートの有効化をしているようでした。しかも、begin()指定だと自動的に true の模様。 Serial.begin(115200); の呼び出しが2重になっているのが、不具合を引き起こしていそうですね。

ということで、

  • M5.begin() でも、今回の記事で紹介した手法でOK
  • M5.begin(true, true, true, true); でも、今回の記事で紹介した手法でOK

ということが分かりました。

そうしたら、ちょうど素晴らしいアドバイスをいただきました!

  • M5.begin(true,true,false,true);としてからSerial.begin()する

というのも、先ほどの挙動を見るにの呼び出しが2重にならないので良いやり方ですね。

こうやって、他の方からしっかりとしたアドバイスをいただけると、考え方があってたんだなって、うれしくなります!

ありがとうございました!