Edited at

ESP32 〜 Alibaba Cloud IoT Platform をMQTT接続(Arduino Core版)


背景

届いてから何ヶ月も開封していなかったESP32をいじって見よう、Arduino Core使えるから楽だよね、とりあえずAlibaba Cloud IoT PlatformへMQTT接続してpublishして遊ぼう、とやってみたら休日1日潰すレベルでハマったので忘備録。

IoT Platformを単純なMQTTのBrokerとしてみると結構クセありますね。原因はAlibaba CloudのIoT PlatformはMQTT brokerとしては30秒より短いKeep aliveを受け付けないんだけど、Arduinoの PubSubClientライブラリのデフォルトが15秒なので接続できない。なのでこの値を30秒以上にする必要がある。

Arduinoのエラーが2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier だった。普通に読むとClientIDが被疑箇所で、しかもClientIDの扱いが後述するけど、微妙に複雑なので、何度も何度も見直しててハマった。でも本当の原因はKEEP_ALIVEの値ですた。


必要な情報


Alibaba Cloud IoT Platform上のパラメータ

以下の情報をAlibaba Cloud Consoleから確認し控えておく。ここからESPがMQTT接続するArduinoコードを書く上で必要な各パラメータを生成する。

1. ProductKey

2. deviceName

3. deviceSecret

4. clientId

5. RegionId


1. ProductKey

image.png


2. deviceName / 3. deviceSecret

image.png


4. ClientId

ClientIdは任意。ここでは"arduino"。

MQTTプロトコル上のClientIDとは異なるので注意


5. RegionId

AlibabaCloud上のリージョンID。日本であれば"ap-northeast-1"


MQTTクライアントの接続に必要なパラメータ

MQTTプロトコルで接続するArduinoコードを書くための以下の4つの情報が必要になる。既述のAlibabaCloud IoT Platformの5つのパラメータから合成する。

1. Broker Address

2. ClientID(MQTTプロトコル上の)

3. UserName

4. Password


1. Broker Address

[ProductKey].iot-as-mqtt.[RegionId].aliyuncs.com

日本なら

[ProductKey].iot-as-mqtt.ap-northeast-1.aliyuncs.com


2. ClientID(MQTTプロトコル上)

Alibaba CloudのClientIdから合成。

TLS(sha1)を使う場合は

[ClientID]|securemode=2,signmethod=hmacsha1|

ここでは

arduino|securemode=2,signmethod=hmacsha1|

TLSを使わない場合は

[ClientID]|securemode=3,signmethod=hmacsha1|


3. UserName

[deviceName]&[ProductKey]

ここでは

testdev2&[ProductKey]


4. password

パスワード生成toolにproductKey、deviceName、deviceSecret、clientIdを入力して生成。

https://files.alicdn.com/tpsservice/471c155376d6a88a29c9ad66784e94f0.zip?spm=a21mg.p38356.a3.10.7b39255bbByw5k&file=471c155376d6a88a29c9ad66784e94f0.zip

ツールイメージ

image.png

またはこちらを参照して生成

https://jp.alibabacloud.com/help/doc-detail/86706.htm?spm=a21mg.p38356.b99.149.57d37b3frlA1t8


テスト

ここまで準備ができれば一般的なMQTT clientソフトウエアで接続できる。例えばLinuxのmosquittoならこんなコマンドでpublishできるはず。

# TLSなし、生MQTTの場合

mosquitto_pub -h [1. Broker Address] \
-i “[2. ClientID(MQTTプロトコル上)]” \
-u “[3. UserName]” \
-P “[4. password]” \
-t “[TOPIC]” \
-m '{id:”0001”,data:”helo world”}’

もしESP32ではなくてラズパイのようなlinux環境でやるならこのmosquittoでバッチ書くか、あるいはpaho-mqttあたりを使ってスクリプト書くのが王道なのかな?


ESP32


 機器調達

Alibaba CloudなのでAliexpressへ注文。usbで接続できるやつでも500円ぐらいから色々ある。技適マークがあり合法的に使えるやつを選ぼう。ほとんどがそうなはず。

同様に使えそうな一世代前のESP8266は安いけど技適マークつきのものが少ないので注意。

https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180916220315&SearchText=esp32

image.png

Aliexpressは安いけど1-4週間ほとかかる。そんな待てない、時間がもったいないという人はっちょっと高いけどAmazonから。

https://www.amazon.co.jp/s/ref=nb_sb_noss_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&url=search-alias%3Daps&field-keywords=esp32

image.png


ソースコード

#include <WiFiClientSecure.h>

#include <PubSubClient.h>

#define pin 2

const char* ssid = "[WIFI_SSID]";
const char* password = "[WIFI_PASSWORD]";

const char* server = "[1. Broker Address]";
const int port = 1883;
const char* clientid = "[2. ClientID(MQTTプロトコル上の)]";
const char* mqttuser = "[3. UserName]";
const char* mqttpass = "[4. password]";
// TOPICはデフォルトのものを利用
const char* mqtttopic = "/[ProductKey]/[deviceID]/update";
int cnt = 0;
char msg[50];

WiFiClientSecure client;
PubSubClient mqttClient(client);

void connAlibabaIoT() {
while (!mqttClient.connected()) {
if (mqttClient.connect(clientid, mqttuser, mqttpass)) {
Serial.println("Connected.");
} else {
Serial.print("Failed. Error state=");
Serial.println(mqttClient.state());
Serial.println(clientid);
// Wait 5 seconds before retrying
delay(5000);
}
}
}

void setup() {
pinMode(pin, OUTPUT);

Serial.begin(115200);
delay(10);
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
delay(1000);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());

mqttClient.setServer(server, port);
connAlibabaIoT();
}

void loop() {
// publishするタイミングを確認するためにLチカ。不要。
digitalWrite(pin, HIGH);
delay(1000);
digitalWrite(pin, LOW); voltage LOW
delay(1000);

if (!mqttClient.connected()) {
connAlibabaIoT();
}
mqttClient.loop();
++cnt;
snprintf (msg, 50, "{id:\"%05d\",data:\"hello world\"}", cnt);
Serial.print("Publish message: ");
Serial.println(msg);
mqttClient.publish(mqtttopic, msg);
}

ヘッダーファイルのMQTT_KEEPALIVEの値をデフォルトの15から30に変える。


libraries\PubSubClient\src\PubSubClient.h

#ifndef MQTT_KEEPALIVE

#define MQTT_KEEPALIVE 30
#endif


結果

無事データがTable Storeへ格納されることを確認

*publishしたデータをTable Storeに格納するまでの方法はここでは割愛。

image.png


 その他


他のハマりどころ

Arduino IDEの書き込みスピード(serial portのUpLoad speed)が初期値で512000になってたけど115200ぐらいまで落とさないと書き込みに失敗する。私の環境だけかも。


参考文献

https://qiita.com/networkyohan69/items/4f028500d520dbf14be1

https://pubsubclient.knolleary.net/api.html


注意

これは私の趣味の世界です。所属する団体の考え方は全く反映していません。