🤖

SIM7080G搭載 CAT-M/NB-IoT+GNSSユニットでM5Core2でSORACOMにつないで位置情報をMQTTで送信する

2022/07/24に公開

はじめに

M5Stack用 SIM7080G搭載 CAT-M/NB-IoT+GNSSユニットの技適が通って国内でも販売されるようになりました。SIM7080G搭載でCAT-M/NB IoT対応でGNSSまで付いてる。まさにIoTをやるためのモジュール。MQTTやGNSSをATコマンドで操れるのでMCU側の負担も軽くて最高なやつです。

この記事ではM5Stack Core2とユニットを接続してSORACOMでネットにつないでユニットのGNSSセンサで取得した位置情報をMQTTでパブリックなMQTTブローカーに送信してみました。SORACOMへのつなぎ方やユニットの安定動作のさせかたは以下の記事を参考にさせていただきました。

SIM7080G搭載 CAT-M/NB-IoT+GNSSユニットが動かない時に確認したい事と、対応SIM(LTE-M)

使ったSORACOMのSIMはSORACOM 特定地域向け IoT SIM plan-D。ユニットへは19V 3AのDCアダプタで給電しました。M5Stack Core2とユニットは付属のGroveコネクタでPORT.Cにつなげています。上記記事では外部給電の場合は5vのポートが出力に変わるので5vポートを繋げないほうが良いと書かれていますが、当方では問題なく動作しています。しかしながら、心配な方は対策してください。

やってみた

ソースコードは以下。いろいろと試しながら書いたので無駄な処理も入っていますが動きます。
M5_SIM7080GのサンプルコードのMQTTのやつとGNSSのやつを混ぜてSORACOM接続を追加した感じです。

#include "M5Core2.h"
#include "M5GFX.h"
#include "M5_SIM7080G.h"

M5GFX display;
M5Canvas canvas(&display);

M5_SIM7080G device;

String readstr;
bool DEBUG = true;
bool SORACOM_CONNECTED = false;
bool MQTT_CONNECTED = false;
bool MQTT_SUBSCRIBED = false;
bool GNSS_EXECED = false;
String GNSS_LAT;
String GNSS_LNG;
String MQ_MSG;

void log(String str) {
    Serial.print(str);
    canvas.print(str);
    canvas.pushSprite(0, 0);
}

void debug(String str) {
  if (DEBUG) Serial.print(str + "\r\n");
}

bool isIpActive(String str) {
    bool result = false;
    int start = str.indexOf("+CNACT:");
    String subStr = str.substring(start+10, start+11);
    if (subStr == "1") result = true;
    return result;
}

String getIpAddress(String str) {
  int total = str.length();
  int start = str.indexOf("+CNACT:");
  String buf = str.substring(start+13, total);
  int term = buf.indexOf("\r");
  String result = buf.substring(0, term-1);
  return result;
}

void connectSoracom() {
  log(String("Connecting to SORACOM...\r\n"));
  device.sendMsg("AT+GSN\r\n");  // Request TA Serial Number Identification(IMEI)
  readstr = device.waitMsg(1000);
  debug(readstr);
  device.sendMsg("AT+CFUN=0\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+CGDCONT=1,\"IP\",\"soracom.io\"\r\n");
  readstr = device.waitMsg(1000);
  debug(readstr);
  device.sendMsg("AT+CFUN=1\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+CGNAPN\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+CMNB?\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+CPSI?\r\n"); // Inquiring UE System Information
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+CNACT=0,1\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+CNACT?\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  if (isIpActive(readstr) == true) {
    log(String("IP: ") + getIpAddress(readstr) + String("\r\n"));
    SORACOM_CONNECTED = true;
    log(String("Connected Successfuly!.\r\n"));
  }
}

void connectMqtt() {
  log(String("Connecting to MQTT...\r\n"));
  device.sendMsg("AT+SMCONF=\"URL\",\"broker.emqx.io\",\"1883\"\r\n");
  readstr = device.waitMsg(1000);
  debug(readstr);
  device.sendMsg("AT+SMCONF=\"KEEPTIME\",60\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+SMCONF=\"CLEANSS\",1\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+SMCONF=\"CLIENTID\",\"simmqtt\"\r\n");
  readstr = device.waitMsg(200);
  debug(readstr);
  device.sendMsg("AT+SMCONN\r\n");
  readstr = device.waitMsg(200);
  if (readstr.indexOf("OK") > -1) {
    MQTT_CONNECTED = true;
    log(String("\r\nMQTT Connected Successfully!\r\n"));
  }
}

void getGnssInfo(String str) {
  int start = str.indexOf("+CGNSINF:");
  String buf = str.substring(start, str.length() - 8);
  GNSS_LAT = buf.substring(14,22);
  GNSS_LNG = buf.substring(23,31);
}

void riseGnss() {
  log("Turn GNSS power on..");
  while (device.send_and_getMsg("AT+CGNSPWR=1\r\n").indexOf("OK") == -1) {
    log(".");
    delay(1000);
  }
  log("Finished\r\n");
  log("Waiting to get the data..\r\n");
  delay(20000);
  device.sendMsg("AT+CGNSINF\r\n");
  readstr = device.waitMsg(1000);
  getGnssInfo(readstr);
  while (device.send_and_getMsg("AT+CGNSPWR=0\r\n").indexOf("OK") == -1) {
    log(".");
    delay(1000);
  }
  log("Turn GNSS power off..");
  log("Finished\r\n");
}


void setup()
{
    M5.begin();
    display.begin();
    if (display.isEPD())
    {
        display.setEpdMode(epd_mode_t::epd_fastest);
        display.invertDisplay(true);
        display.clear(TFT_BLACK);
    }
    if (display.width() < display.height())
    {
        display.setRotation(display.getRotation() ^ 1);
    }
    canvas.setColorDepth(1); // mono color
    canvas.createSprite(display.width(), display.height());
    canvas.setTextSize((float)canvas.width() / 160);
    canvas.setTextScroll(true);

    //SIM7080
    log(String("Start SIM7080 init...\r\n"));
    //device.Init(&Serial2, 16, 17);  // for M5Stack
    device.Init(&Serial2, 13, 14);    // for M5Core2

    /* Reboot SIM7080G */
    log("Reboot SIM7080G..");
    while (device.send_and_getMsg("AT+CREBOOT\r\n").indexOf("OK") == -1) {
        log(".");
        delay(1000);
    }
    log(String("Finished\r\n"));

}

void loop()
{
    if (GNSS_EXECED == false) riseGnss();
    if (SORACOM_CONNECTED == false) connectSoracom();
    if (MQTT_CONNECTED == false) connectMqtt();
    if (MQTT_SUBSCRIBED == false) {
      device.sendMsg("AT+SMSUB=\"sub_topic\",1\r\n");
      readstr = device.waitMsg(1000);
      log(readstr);
    }

    MQ_MSG = String("{\"lat\":") + GNSS_LAT + String(", \"lng\":") + GNSS_LNG + String("}");
    log(MQ_MSG + "\r\n");


    while(1){
        M5.update();
        if(M5.BtnA.wasPressed()) {
            log(String("Send MQTT Message!\r\n"));
            log(device.send_and_getMsg("AT+SMPUB=?\r\n"));
            delay(200);
            device.sendMsg(String("AT+SMPUB=\"pub_topic\",") + MQ_MSG.length() + String(",1,1\r\n"));
            delay(100);
            device.sendMsg(MQ_MSG + String("\r\n"));
        }
        readstr = device.waitMsg(0);
        //Serial.print(readstr);
        log(readstr);
        if(M5.BtnB.wasPressed()) {
          log(String("Close MQTT!\r\n"));
          log(device.send_and_getMsg("AT+SMUNSUB=\"sub_topic\"\r\n"));
          delay(1000);
          log(device.send_and_getMsg("AT+SMDISC\r\n"));
          delay(1000);
          log(String("MQTT closed.\r\n"));
        }
    }
}

上記をコンパイルしてM5Stack Core2に転送。30秒くらいしてMQTT Connected Successfully!が画面に表示されたあとにAボタンをタップするとbroker.emqx.ioに位置情報が送信されます。送信された情報はmosquittoなどでサブスクライブして確認してみてください。以下にmosquittoの例を示します。

ターミナルでmosquittoを起動します

/usr/local/sbin/mosquitto -c /usr/local/etc/mosquitto/mosquitto.conf

1658594560: mosquitto version 2.0.14 starting
1658594560: Config loaded from /usr/local/etc/mosquitto/mosquitto.conf.
1658594560: Starting in local only mode. Connections will only be possible from clients running on this machine.
1658594560: Create a configuration file which defines a listener to allow remote access.
1658594560: For more details see https://mosquitto.org/documentation/authentication-methods/
1658594560: Opening ipv4 listen socket on port 1883.
1658594560: Opening ipv6 listen socket on port 1883.
1658594560: mosquitto version 2.0.14 running

別タブでsubscriberを起動します

mosquitto_sub -d -t orz -h broker.emqx.io --topic pub_topic

Client null sending CONNECT
Client null received CONNACK (0)
Client null sending SUBSCRIBE (Mid: 1, Topic: orz, QoS: 0, Options: 0x00)
Client null sending SUBSCRIBE (Mid: 1, Topic: pub_topic, QoS: 0, Options: 0x00)
Client null received SUBACK
Subscribed (mid: 1): 0, 0
Client null received PUBLISH (d0, q0, r1, m0, 'pub_topic', ... (32 bytes))
{"lat":0.000000, "lng":0.000000}

動作している様子

M5StackCore2_and_SIM7080G

まとめ

処理の手順はざっくり以下。

  1. GNSSセンサのパワーをONする
  2. GNSS位置情報を取得する
  3. GNSSセンサのパワーをOFFする
  4. SORACOMの接続を確立する
  5. MQTTブローカーに接続する

GNSSセンサをONしてからGNSS情報を取得にいくまで20秒待ってます。

最初はsetup時にGNSSセンサをONしてからSORACOM接続->MQTT接続とやってみたけどMQTT接続が必ずERRORになってしまいました。どうやらSIM7080Gの中ではMQTTとGNSSはコンカレントに使えないようなのでGNSSセンサで位置情報をとったあとセンサを切ってからMQTT接続を行っています。

リアルタイムに位置情報とりながらMQTTで送るという処理はちょっといろいろ考えないといけないかも。このあたりうまい方法があったらぜひ教えてください。

ともあれM5Stack系でLTEがとても簡単に使えるようになっただけでもありがたい。いろいろと捗りそうです。

Discussion