M5Stack with 3G 拡張ボード で MQTT 通信

![titleImage.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218217/fc162265-994d-186e-ece5-3cf2a88f371c.jpeg)


この記事は [M5Stack Advent Calendar 2019](https://qiita.com/advent-calendar/2019/m5stack) 23日目の記事です．
22日目の記事は [M5Stackでジェスチャーセンサーを使ってみた。](https://qiita.com/MonoShobel/items/2971305866984ce5b8b1) 
24日目の記事は [M5Stackで画面キャプチャーを取得する](https://qiita.com/fukuebiz/items/8aff6cad2f6048f02f12)

[Rone Loreto](https://qiita.com/Rone_Loreto) です．
普段は [CPS Lab (実空間コンピューティング研究室)](https://cpslab.github.io/) という所で IoT システムの研究開発等をやっております．


# 概要

本記事では，

**[M5Stack用 3G 拡張ボード](https://soracom.jp/products/kit/3g_module_m5stack/) を用いて MQTT 通信すること**

を目的とします．


※本記事で用いたスケッチは[こちらのGitHubリポジトリ](https://github.com/RoneLoreto/M5StackAdvent20191223)にて公開しています．必要な方はご利用ください．

## 今回のゴール

**M5Stack** で **3G 回線**を用いて **MQTT 通信**をし，**計測したデータが送信できる**ことを確認する．


### 補足

3G 拡張ボードのチュートリアルについては，
「[プロトタイプ向けマイコンモジュール M5Stack と 3G 拡張ボードをセットアップする](https://dev.soracom.io/jp/start/m5stack/)」
にて SORACOM さんが詳しく記載されているので，割愛いたします．

また，MQTT 通信の詳細についても割愛させていただきます．
MQTT 初学者の方は，MQTT に関する解説を予めご覧頂けると幸いです．
私が公開した「[【IoT初級者へ】M5StickC ではじめる MQTT 通信](https://qiita.com/Rone_Loreto/items/6a8991e88c1a31cba924)」も参考の一助になると思います．
（該当記事は M5StickC を用いていますが，M5Stack でもほぼ同じ内容が動作可能です）


# 今回のシステム構成

![SystemImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218217/0ca8844e-4f8c-ce2f-3b76-2427d4d8413c.png)


## 【Pubisher】 M5Stack

皆さんご存知 M5Stack です．
今回は [M5Stack用 3G 拡張ボード](https://soracom.jp/products/kit/3g_module_m5stack/) を装着し，3G 通信が可能となっています．
拡張ボードに差し込む SIM には [SORACOM Air SIM](https://soracom.jp/services/air/) を用いています．


## 【Broker & Subscriber】 [ThingSpeak] (【Broker&Subcriber】 ThingSpeak)

お手軽 MQTT 通信でよくお世話になっているデータ可視化サービスです．
アカウントが必要です．持っていない方は[コチラ](https://thingspeak.com/users/sign_up)から．


# 実装

**[Arduino IDE](https://www.arduino.cc/en/main/software)** を用いて行います．
まず，本記事で使用したセンサと，導入が必要なライブラリについて説明します．


## 本記事で使用したセンサ

[M5Stack 用光センサユニット](https://www.switch-science.com/catalog/4051/) を使用しました．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218217/8c0f7035-feb3-88c9-8f7d-9b550da4f2f8.png)

明るさによって測定値が変わります．
本記事ではこの光センサを用います．

※データの送信確認には，光，距離，9軸など計測値を自分達で任意に変更しやすいセンサがオススメです．


## 導入が必要なライブラリ

実装に際して，以下の2つ（＋ [M5Stackのライブラリ](https://github.com/m5stack/M5Stack)）が必要です．


### [PubSubClient](https://github.com/knolleary/pubsubclient)

MQTT 通信に必要なライブラリです．
M5Stack・M5StickC を含む ESP32 系列のマイコン他，ESP8266 や Arduino に対応しています．

M5Stack に Publisher の役割を持たせるため使用します． 


### [TinyGsm](https://github.com/vshymanskyy/TinyGSM)

GSM（モバイル通信用グローバルシステム）モジュール用のライブラリです．

非常に多機能で，これひとつで通信周りの色々な事に対応出来ます．
今回は **[TinyGsmClient](https://github.com/vshymanskyy/TinyGSM/blob/master/src/TinyGsmClient.h)** をインクルードし，PubSubClient の引数に呼び出す他，3G 接続時に使用します．


## スケッチの作成

スケッチの作成……の前に，ThingSpeak の設定をしておきましょう．


### ThingSpeak側の設定

[【IoT初級者へ】M5StickC ではじめる MQTT 通信 - 2. ThingSpeak側の設定を行なう．](https://qiita.com/Rone_Loreto/items/6a8991e88c1a31cba924#2-thingspeak%E5%81%B4%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E8%A1%8C%E3%81%AA%E3%81%86)
を参考に，チャネルを作成し，

- **MQTT API Key**
- **Write API Key**
- **Channel ID**

をメモしてください．


### 光センサの値 を ThingSpeak に 3G 回線経由の MQTT 通信で送るスケッチ

実際のスケッチです．

```c++:m5stack3gMqtt.ino
#include <M5Stack.h>
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>
#include <PubSubClient.h>


// 3G 設定
TinyGsm modem(Serial2);   // 3G モデム
TinyGsmClient ctx(modem);


// MQTT 設定 (subscriber:ThingSpeak)
const char mqttUserName[] = "name";   // name→設定した名前に書き換える
const char mqttPass[] = "pass";       // pass→設定されたmqttパスに書き換える
const char writeApiKey[] = "apikey";  // apikey→設定されたwriteAPIキーに書き換える
const long channelId = 000000;        // 000000→設定されたチャネルIDに書き換える

static const char alphanum[] ="0123456789"
                              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                              "abcdefghijklmnopqrstuvwxyz";  // クライアントIDをランダム生成するための文字列

PubSubClient mqttClient(ctx);
const char* server = "mqtt.thingspeak.com";


// Publish 間隔の設定
unsigned long lastConnectionTime = 0; 
const unsigned long postingInterval = 30L * 1000L; // Publish の間隔を30秒にする


// 光センサ 設定
uint16_t lightVal = 0;
String lightValStr;


// 関数宣言
void init3G();
void lightSensing();
void reConnect();
void mqttPublish();



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

  init3G();
  mqttClient.setServer(server, 1883); // MQTT ブローカの詳細設定

  pinMode(26, INPUT); // 光センサの pin 宣言  
  
  M5.Lcd.clear(BLACK);
}


void loop() {
  if (!mqttClient.connected()) { // 接続が切れた際に再接続
    reConnect();
  }

  mqttClient.loop();   // MQTT 接続を確立する

  // 最後に Publish した時間から postingInterval が経過すると動作する
  if (millis() - lastConnectionTime > postingInterval) {
    lightSensing();
    mqttPublish();
  }
}



/* 
 * ----- 以下loop内で用いる関数 -----
 */
void init3G() { // 3G 接続の初期設定
  M5.Lcd.clear(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.println(F("M5Stack + 3G Module"));

  M5.Lcd.print(F("modem.restart()"));
  Serial2.begin(115200, SERIAL_8N1, 16, 17);
  modem.restart();
  M5.Lcd.println(F("done"));

  M5.Lcd.print(F("getModemInfo:"));
  String modemInfo = modem.getModemInfo();
  M5.Lcd.println(modemInfo);

  M5.Lcd.print(F("waitForNetwork()"));
  while (!modem.waitForNetwork()) M5.Lcd.print(".");
  M5.Lcd.println(F("Ok"));

  M5.Lcd.print(F("gprsConnect(soracom.io)"));
  modem.gprsConnect("soracom.io", "sora", "sora");
  M5.Lcd.println(F("done"));

  M5.Lcd.print(F("isNetworkConnected()"));
  while (!modem.isNetworkConnected()) M5.Lcd.print(".");
  M5.Lcd.println(F("Ok"));

  M5.Lcd.print(F("My IP addr: "));
  IPAddress ipaddr = modem.localIP();
  M5.Lcd.print(ipaddr);
  delay(2000);
}


void lightSensing() { // 光センサの値を計測する関数
  lightVal = analogRead(36);
  lightValStr = (String)lightVal;

  M5.Lcd.setTextSize(3);
  M5.Lcd.setTextColor(RED, BLACK);
  M5.Lcd.setCursor(0, 60);
  M5.Lcd.print("Light:");
  M5.Lcd.setCursor(160, 60);
  M5.Lcd.print(lightVal);
}


void reConnect() { // 再接続用の関数
  char clientId[10];

  // 接続まで繰り返す
  while (!mqttClient.connected()) 
  {
    Serial.print("Attempting MQTT connection...");
    // Generate ClientID
    for (int i = 0; i < 8; i++) {
        clientId[i] = alphanum[random(51)];
    }

    // MQTT broker に接続する
    if (mqttClient.connect(clientId, mqttUserName, mqttPass)) 
    {
      Serial.print("Connected with Client ID:  ");
      Serial.print(String(clientId));
      Serial.print(", Username: ");
      Serial.print(mqttUserName);
      Serial.print(" , Passwword: ");
      Serial.println(mqttPass);
    } else 
    {
      Serial.print("failed, rc=");
      // http://pubsubclient.knolleary.net/api.html#state に state 一覧が書いてある
      Serial.print(mqttClient.state());
      Serial.println("try again in 5 seconds");
      delay(5000);
    }
  }
}

void mqttPublish() {
  // ThingSpeak に Publish するための文字列データを作成する
  String data = ("field1=" + lightValStr);

  int length = data.length();
  char msgBuffer[length];
  data.toCharArray(msgBuffer, length+1);
  
  // topic 文字列を作成し ThingSpeak の channel feed にデータを Publish する
  String topicString ="channels/" + String(channelId) + "/publish/" + String(writeApiKey);
  length = topicString.length();
  char topicBuffer[length];
  topicString.toCharArray(topicBuffer,length+1);
 
  mqttClient.publish(topicBuffer, msgBuffer);

  lastConnectionTime = millis();
}
```

#### 主な書き換え場所 - MQTT 設定（12〜16行目）

```c++
// MQTT 設定 (subscriber:ThingSpeak)
const char mqttUserName[] = "name";
const char mqttPass[] = "pass";
const char writeApiKey[] = "apikey";
const long channelId = 000000;
```

を先ほどメモした内容にそれぞれ書き換えてください．

たとえば，

- 「Channel Name」: M5Stack_3G_MQTT
- 「MQTT API Key」: HOGEAHOGEAHOGEAB
- 「Write API Key」 : HUGAAHUGAAHUGAB
- 「Channel ID」: 000000

ならば

```c++
// MQTT 設定 (subscriber:ThingSpeak)
const char mqttUserName[] = "M5Stack_3G_MQTT";
const char mqttPass[] = "HOGEAHOGEAHOGEAB";
const char writeApiKey[] = "HUGAAHUGAAHUGAB";
const long channelId = 000000;
```

のようにしてください．




実装できたら，M5Stack へ書き込みましょう．


## 実行結果

3G 接続後，数十秒待ちます．
すると，ThingSpeak のチャネル側に……

![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218217/67d3a5ee-e18b-975b-8a04-8e7baad8fa51.png)

計測結果が表示されました！
30秒ごとに計測した光センサの値が送信されています．


# まとめ

3G 拡張ボード で MQTT 通信を実装した例が無かったので，今回のアドベントカレンダーのネタとさせて頂きました．

「**M5Stack で 3G通信がやりたい！！**」という時，
以前は [3GIM](https://www.switch-science.com/catalog/2607/) や [Wio LTE](https://www.switch-science.com/catalog/5442/) 等を M5Stack と接続しｺﾞﾆｮｺﾞﾆｮする必要がありましたが，とても簡潔になりました．

3G 通信が可能となるだけで IoT システムの構築の幅が広がるので，未経験の方にも是非オススメしたいです！！

今回はこの辺りで終わりとさせてさせていただきます．
みなさんも快適な 3G & MQTT 通信ライフを！


## あとがき

「3G 通信が可能となるだけで IoT システムの構築の幅が広がる」と書きましたが，
最近になって **LPWA** が実用レベルになってきました．
IoT 向けで 3G 通信が用いられていたところは LPWA に置換されていきそうですが，実際どうなるんでしょうか……

M5Stack で LPWA 通信できるようになった際には，実装し記事にまとめたいと思います．