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 の出す位置を変えてみると突然動くようになったり、変数の呼び出すタイミングを変えると動いたりするなど不可解な現象で困りました。
2021/01/24 現在、ボードマネージャで M5Stack のバージョンは 1.0.7。
ライブラリのバージョンは 0.0.2 です。
結論としては Serial.begin(115200); をコメントアウトしたら動いた
なるほどー 唐突にではあるが M5Stack Core2 でバグるときのクセが見えてきた気がする。解消方法がだいたい見えた。
— Tanaka Seigo (@1ft_seabass) January 23, 2021
かれこれ、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()は引数によってSerial初期化をします。https://t.co/6QRBkL7uj7
Serial.beginは複数回呼ばれると問題があるようなので、M5.begin()した場合はSerial.begin()しないか、M5.begin(true,true,false,true);としてからSerial.begin()するとよいと思われます。— GOB (@GOB_52_GOB) January 25, 2021
そうしたら、ちょうど素晴らしいアドバイスをいただきました!
- M5.begin(true,true,false,true);としてからSerial.begin()する
というのも、先ほどの挙動を見るにの呼び出しが2重にならないので良いやり方ですね。
こうやって、他の方からしっかりとしたアドバイスをいただけると、考え方があってたんだなって、うれしくなります!
ありがとうございました!