NB-IoTを利用したMQTTデータ送受信実験

# 追記
Arduinoの標準のMQTTライブラリーであるPubSubclient libraryの標準はkeep aliveが15秒となっているが、Alibaba Cloud IoT plat formは30秒より短いKeep aliveを許容しない。以前別のタイプのボード触っていて気づき、自分の環境は修正済みだったので気づかずに記載漏れしていました。
Arduino IDEからマクロ定義する方法を知らないので、とりあえずヘッダーファイルを直接修正してMQTT_KEEPALIVEの値をデフォルトの15から30に変える必要あり。

```c:libraries\PubSubClient\src\PubSubClient.h
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 30
#endif
```

# NB-IoTの現状
2019/2/17現在、NB-IoT SIMを提供しているのはSoftBankのみで、SoftBankの**法人契約**としてのみNB-IoTが利用できるSIMを購入することができる。
本件では「Wio LTE M1/NB1(BG96) SoftBank Edition」というハードウエアを使うが、これはSeeed社が製造したSoftBankのNB-IoTの検証を目的とする非売品であるようだ。

**Wio LTE M1/NB1(BG96) SoftBank Edition**
https://www.softbank.jp/biz/set/data/iot/partner-program/device/shared/spec_iot_644.pdf

# 準備
## Arduino SDK
以下を参考に必要なソフトウエア、ドライバーをインストールする。
https://github.com/SeeedJP/Wiki/blob/master/Wio_cell_lib_for_Arduino/setup-ja.md

## MQTTのパラメータ
本件ではAlibaba Cloud IoT Platformを利用するが、基本的にはmosqittoなどの他のMQTT Brokerでも利用できると思われる。ちなみにMQTT over TLSはまだ成功していない。。。。

以下のMQTTパラメータを準備する。
1. Broker Address
2. Client ID(TLSを使わないバージョンで作成)
3. UserName
4. Password
5. TOPIC

以下のURL参照
https://qiita.com/makotaka/items/388fa1ee0eb1f0237012

また、SoftBankのネットワークへ接続するためにSoftBankのSIMに紐づいた下記情報も必要になる

1. APN
2. Username
3. Password

## Source code
```c++
#include <WioCellLibforArduino.h>
#include <WioCellularClient.h>
#include <PubSubClient.h>		// https://github.com/SeeedJP/pubsubclient
#include <stdio.h>


#define APN               "***"  //SoftBankのSIMのAPN
#define USERNAME          "***"  //SoftBankのSIMのuser
#define PASSWORD          "***"  //SoftBankのSIMのpassword

#define MQTT_SERVER_HOST  "***"  //MQTTのBroker Address
#define MQTT_SERVER_PORT  (1883)

#define ID                "***"  //MQTTのClientID
#define OUT_TOPIC         "***"  //MQTTのTOPIC(publish用)
#define IN_TOPIC          "***"  //MQTTのTOPIC(subscribe用)
#define MQUSER            "***"  //MQTTのUsername
#define MQPASS            "***"  //MQTTのPassword 

#define INTERVAL          (60000)

WioCellular Wio;
WioCellularClient WioClient(&Wio);
PubSubClient MqttClient;

void callback(char* topic, byte* payload, unsigned int length) {
  SerialUSB.print("Subscribe:");
  for (int i = 0; i < (int)length; i++) SerialUSB.print((char)payload[i]);
  SerialUSB.println("");
}

void setup() {
  delay(200);

  SerialUSB.begin(115200);
  SerialUSB.println("");
  SerialUSB.println("--- START ---------------------------------------------------");
  SerialUSB.println("### I/O Initialize.");
  Wio.Init();
  
  SerialUSB.println("### Power supply ON.");
  Wio.PowerSupplyCellular(true);
  delay(500);

  SerialUSB.println("### Turn on or reset.");
#ifdef ARDUINO_WIO_LTE_M1NB1_BG96
  Wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_NB1);
  Wio.SetSelectNetwork(WioCellular::SELECT_NETWORK_MODE_MANUAL_IMSI);
#endif
  if (!Wio.TurnOnOrReset()) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Connecting to \"" APN "\".");
  if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Connecting to MQTT server \"" MQTT_SERVER_HOST "\"");
  MqttClient.setServer(MQTT_SERVER_HOST, MQTT_SERVER_PORT);
  MqttClient.setCallback(callback);
  MqttClient.setClient(WioClient);
  if (!MqttClient.connect(ID,MQUSER,MQPASS)) {
    SerialUSB.println("### ERROR! ###");
    SerialUSB.println(MqttClient.state());
    return;
  }
  MqttClient.subscribe(IN_TOPIC);

  SerialUSB.println("### Setup completed.");
}

void loop() {
  char data[100];
  sprintf(data, "{\"uptime\":%lu}", millis() / 1000);
  SerialUSB.print("Publish:");
  SerialUSB.print(data);
  SerialUSB.println("");
  MqttClient.publish(OUT_TOPIC, data);
  
  unsigned long next = millis();
  while (millis() < next + INTERVAL)
  {
    MqttClient.loop();
  }
}

```
# テスト
先ほどSubscribeしたTopicにAlibaba Cloud IoT Platformのコンソールから"pub test"とpublishしてみる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132165/29c1e996-e933-0325-73b9-0cd39901c573.png)

シリアルコンソールで"pub test"と表示され、正しく動いていることを確認
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132165/3cae00e6-2d88-faee-083e-9a09c82b1d16.png)


# ATコマンド
このハードウエアはQuectelのBG96を利用していて、"SerialModule"というオブジェクト経由でATコマンド使えるようだ。ATコマンドを利用する参考コードを書いたけど、さすがにもう少しスマートなツールがある気がする。

```c++
#include <WioCellLibforArduino.h>

WioCellular Wio;

void setup() {
  delay(200);

  SerialUSB.begin(115200);
  SerialUSB.println("");
  SerialUSB.println("--- START ---------------------------------------------------");
  SerialUSB.println("### I/O Initialize.");
  Wio.Init(); 
  SerialUSB.println("### Power supply ON.");
  Wio.PowerSupplyCellular(true);
  delay(500);

  SerialUSB.println("### Turn on or reset.");
#ifdef ARDUINO_WIO_LTE_M1NB1_BG96
  Wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_M1);
  Wio.SetSelectNetwork(WioCellular::SELECT_NETWORK_MODE_MANUAL_IMSI);
#endif
  if (!Wio.TurnOnOrReset()) {
    SerialUSB.println("### ERROR! ###");
    return;
  }
  SerialUSB.println(F(">> Waiting for AT command"));
}
void loop() {
  if (SerialModule.available()) {
    SerialUSB.write(SerialModule.read());
  }
  if (SerialUSB.available()) {
    SerialModule.write(SerialUSB.read());
  }
}
```
ATコマンドにてネットワークやハードウエアの情報を直接取得できる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/132165/12e8d56a-1142-4e02-b012-4c33a30a8763.png)

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