M5StickCでMQTTのQOS2をやってみました


# 1 はじめに  
M5stickCでMQTTのQOS2をやってみました  
pubsubclientライブラリはQOS0と1のサポートのようなので
QOS2を指定できるASyncMqttClientライブラリを利用します  


~~~
pubsubclientのLimitationsの抜粋
It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
~~~

# 2 準備  
* PlatformmIOで開発します
　導入方法や設定は別ページを参照願います  
* PlatformIOのライブラリから[ASyncMqttClient](https://github.com/marvinroger/async-mqtt-client)をadd to projestします  
* ブローカーは[mosquitto](https://mosquitto.org/)を利用します
　導入方法や設定は別ページを参照願います

# 3 振る舞い　　

* Publish
Aボタンを押すとQOSを0→1→2の順番で変更しながらメッセージを送信します

* Subscribe
受信するとシリアルにデータを表示します

# 4 コード
exmaples/FullyFeatured-ESP8266.inoを参考にしています

## 4.1 ユーザー毎の変更点
### 4.1.1 SSID  

```cpp:main.cpp
// Wi-FiのSSID
const char *ssid = "****";
// Wi-Fiのパスワード
const char *password = "****";
```

### 4.1.2 brokerのアドレス  

```cpp:main.cpp  
#define MQTT_HOST IPAddress(**, **, **, **)
```

## 4.2 コード全体  

```cpp:main.cpp
#include <M5StickC.h>
#include <WiFi.h>
#include <AsyncMqttClient.h>

// Wi-FiのSSID
const char *ssid = "****";
// Wi-Fiのパスワード
const char *password = "****";

// デバイスID
const char *deviceID = "M5Stickc_1";  // デバイスIDは機器ごとにユニークにします
// メッセージを知らせるトピック
const char *pubTopic = "/pub/M5Stickc_1";

#define MQTT_HOST IPAddress(**, **, **, **)
#define MQTT_PORT 1883

#define BTN_A_PIN 37
#define BTN_B_PIN 39

#define BTN_ON  LOW
#define BTN_OFF HIGH

AsyncMqttClient mqttClient;

void connectToMqtt() {
  Serial.println("Connecting to MQTT...");
  mqttClient.connect();
}

void onMqttConnect(bool sessionPresent) {
  M5.Lcd.println("Connected to MQTT.");
  Serial.println("Connected to MQTT.");
  Serial.print("Session present: ");
  Serial.println(sessionPresent);
  uint16_t packetIdSub = mqttClient.subscribe(pubTopic, 2);
  Serial.print("Subscribing at QoS 2, packetId: ");
  Serial.println(packetIdSub);

  mqttClient.publish("test/lol", 0, true, "test 1");
  Serial.println("Publishing at QoS 0");
  uint16_t packetIdPub1 = mqttClient.publish("test/lol", 1, true, "test 2");
  Serial.print("Publishing at QoS 1, packetId: ");
  Serial.println(packetIdPub1);
  uint16_t packetIdPub2 = mqttClient.publish("test/lol", 2, true, "test 3");
  Serial.print("Publishing at QoS 2, packetId: ");
  Serial.println(packetIdPub2);
}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  Serial.println("Disconnected from MQTT.");

  if (WiFi.isConnected()) {
    delay(1000);
    connectToMqtt();
  }
}

void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
  Serial.println("Subscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
  Serial.print("  qos: ");
  Serial.println(qos);
}

void onMqttUnsubscribe(uint16_t packetId) {
  Serial.println("Unsubscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
  Serial.println("Publish received.");
  Serial.print("  topic: ");
  Serial.println(topic);
  Serial.print("  qos: ");
  Serial.println(properties.qos);
  Serial.print("  dup: ");
  Serial.println(properties.dup);
  Serial.print("  retain: ");
  Serial.println(properties.retain);
  Serial.print("  len: ");
  Serial.println(len);
  Serial.print("  index: ");
  Serial.println(index);
  Serial.print("  total: ");
  Serial.println(total);
}

void onMqttPublish(uint16_t packetId) {
  Serial.println("Publish acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

void setup() {
  
  Serial.begin(115200);

  // Initialize the M5Stack object
  M5.begin();
     
  pinMode(BTN_A_PIN, INPUT_PULLUP);
  pinMode(BTN_B_PIN, INPUT_PULLUP);

  M5.lcd.init();

  // START
  M5.lcd.fillScreen(BLACK);
  M5.lcd.setCursor(10, 10);
  M5.lcd.setTextColor(WHITE);
  M5.lcd.setTextSize(1);
  M5.lcd.println("START");

  // Start WiFi
  Serial.println("Connecting to ");
  Serial.print(ssid);
  WiFi.begin(ssid, password);
   
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print("*");
  }

  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  mqttClient.onSubscribe(onMqttSubscribe);
  mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onMessage(onMqttMessage);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  connectToMqtt();
}

int publish_cnt = 0;
int publish_qos = 0;
void loop() {

  if(digitalRead(BTN_A_PIN) == BTN_ON) {
    Serial.println("press A");
    switch(publish_qos) {
      case 0:
      {
        mqttClient.publish(pubTopic, 0, true, "test 1");
        Serial.println("Publishing at QoS 0");
      }
      break;
      case 1:
      {
        uint16_t packetIdPub1 = mqttClient.publish(pubTopic, 1, true, "test 2");
        Serial.print("Publishing at QoS 1, packetId: ");
        Serial.println(packetIdPub1);
      }
      break;
      case 2:
      {
        uint16_t packetIdPub2 = mqttClient.publish(pubTopic, 2, true, "test 3");
        Serial.print("Publishing at QoS 2, packetId: ");
        Serial.println(packetIdPub2);
      }
      break;
      default:
      break;
    }
    publish_qos++;
    publish_qos %= 3;
  } else {
    //Serial.println("idle");
  }

  delay(100);
}
~~~


# 5 結果  
## 5.1 シリアル  

```
Connecting to 
KAONM-A5DF7*Connecting to MQTT...
Connected to MQTT.
Session present: 0
Subscribing at QoS 2, packetId: 1
Publishing at QoS 0
Publishing at QoS 1, packetId: 2
Publishing at QoS 2, packetId: 3
Subscribe acknowledged.
  packetId: 1
  qos: 2
Publish acknowledged.
  packetId: 2
Publish acknowledged.
  packetId: 3
press A
Publishing at QoS 0
Publish received.
  topic: /pub/M5Stickc_1
  qos: 0
  dup: 0
  retain: 0
  len: 6
  index: 0
  total: 6
press A
Publishing at QoS 1, packetId: 4
Publish acknowledged.
  packetId: 4
Publish received.
  topic: /pub/M5Stickc_1
  qos: 1
  dup: 0
  retain: 0
  len: 6
  index: 0
  total: 6
press A
Publishing at QoS 2, packetId: 5
Publish acknowledged.
  packetId: 5
Publish received.
  topic: /pub/M5Stickc_1
  qos: 2
  dup: 0
  retain: 0
  len: 6
  index: 0
  total: 6
press A
Publishing at QoS 0
Publish received.
  topic: /pub/M5Stickc_1
  qos: 0
  dup: 0
  retain: 0
  len: 6
  index: 0
  total: 6
press A
Publishing at QoS 1, packetId: 6
Publish acknowledged.
  packetId: 6
Publish received.
  topic: /pub/M5Stickc_1
  qos: 1
  dup: 0
  retain: 0
  len: 6
  index: 0
  total: 6
press A
Publishing at QoS 2, packetId: 7
Publish acknowledged.
  packetId: 7
Publish received.
  topic: /pub/M5Stickc_1
  qos: 2
  dup: 0
  retain: 0
  len: 6
  index: 0
  total: 6
```

## 5.2 パケット  
QOS設定が反映されているかパケットを確認します

---
### 5.2.1 QOS0  

~~~
+++ publisherからbrokerへPUBLISH +++
10450,548.113687872,192.168.0.21,192.168.0.22,MQTT,79,137,42,Publish Message [/pub/M5Stickc_1]
+++ brokerからsubscriberへPUBLISH +++
10451,548.113921727,192.168.0.22,192.168.0.21,MQTT,79,42,162,Publish Message [/pub/M5Stickc_1]
~~~

~~~plantuml
publisher -> broker:10450/PUBLISH

broker -> subscriber:10451/PUBLISH 
~~~

QOS0なのでPUBLISHのみ送信されています  

---

### 5.2.2 QOS1  

~~~
+++ publisherからbrokerへPUBLISH +++
10596,556.107138644,192.168.0.21,192.168.0.22,MQTT,81,162,67,Publish Message (id=4) [/pub/M5Stickc_1]
+++ brokerからpublisherへPUBACK +++
10597,556.107508385,192.168.0.22,192.168.0.21,MQTT,58,67,189,Publish Ack (id=4)

+++ brokerからsubscriberへPUBLISH +++
10599,556.115019813,192.168.0.22,192.168.0.21,MQTT,81,71,189,Publish Message (id=1) [/pub/M5Stickc_1]
+++ subscriberからbrokerへPUBACK +++
10604,556.359008912,192.168.0.21,192.168.0.22,MQTT,60,189,98,Publish Ack (id=1)
~~~

~~~plantuml
publisher -> broker:10596/PUBLISH
broker -> publisher:10597/PUBACK

broker -> subscriber:10599/PUBLISH 
subscriber -> broker:10604/PUBACK
~~~

QOS1のPUBACKを送信しているのでQOS１になっているようです  

---

### 5.2.3 QOS2  

~~~
+++ publisherからbrokerへPUBLISH +++
11353,579.125283591,192.168.0.21,192.168.0.22,MQTT,81,288,193,Publish Message (id=7) [/pub/M5Stickc_1]
+++ brokerからpublisherへPUBREC +++
11355,579.125435414,192.168.0.22,192.168.0.21,MQTT,58,193,315,Publish Received (id=7)
+++ publisherからbrokerへPUBREL +++
11367,579.160742235,192.168.0.21,192.168.0.22,MQTT,60,315,197,Publish Release (id=7)
+++ brokerからpublisherへPUBCOMP +++
11368,579.160872548,192.168.0.22,192.168.0.21,MQTT,58,197,319,Publish Complete (id=7)

+++ brokerからsubscriberへPUBLISH +++
11382,579.175784206,192.168.0.22,192.168.0.21,MQTT,81,201,319,Publish Message (id=4) [/pub/M5Stickc_1]
+++ subscriberからbrokerへPUBREC +++
11389,579.201432249,192.168.0.21,192.168.0.22,MQTT,60,319,228,Publish Received (id=4)
+++ brokerからsubscriberへPUBREL +++
11390,579.201582666,192.168.0.22,192.168.0.21,MQTT,58,228,323,Publish Release (id=4)
+++ subscriberからbrokerへPUBCOMP +++
11392,579.209742013,192.168.0.21,192.168.0.22,MQTT,60,323,232,Publish Complete (id=4)
~~~

~~~plantuml
publisher -> broker:11353/PUBLISH
broker -> publisher:11355/PUBREC
publisher -> broker:11367/PUBREL
broker -> publisher:11368/PUBCOMP

broker -> subscriber:11382/PUBLISH 
subscriber -> broker:11389/PUBREC
broker -> subscriber:11390/PUBREL 
subscriber -> broker:11392/PUBCOMP
~~~

QOS2のPUBCOMPを送信しているのでQOS２になっているようです  

終わりです