LoginSignup
10
6

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-09-17

背景

届いてから何ヶ月も開封していなかった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

注意

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

10
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6