LTE-M Shield for Arduinoの初期設定・接続診断スケッチにまつわるあれこれ

# はじめに
この記事は[株式会社ソラコム Advent Calendar](https://qiita.com/advent-calendar/2021/soracominc) の 11 日目の記事で、昨日 (12/10) はソリューションアーキテクト桶谷 (takuya) の「[IoT + AI/MLを考えた時にエッジ or クラウドどっちでAI/MLするのか](https://zenn.dev/okeee/articles/12a0e2eef3759e)」という記事でした。

ごきげんよう。ソラコム CRE[^a] の岡田 (hisaya) です。
普段は [1 日目に登場](https://zenn.dev/n_mikuni/articles/03aed57cd5903b)した同じく CRE の三國 (mick) や [2 日目に登場](https://qiita.com/makoto-soracom/items/5b1b80d9cd6ea262b8aa)した Technical Writer の矢崎 (makoto) といったメンバーとともに、お客様からの SORACOM サービスの利用方法やデバイスに関するお問い合わせにお答えしたり、ユーザードキュメントの更新やサンプルスケッチの開発といった活動を通してより安心して SORACOM サービスをお使いいただくためのお手伝いをしています。

実は 2021 年の 4月に入社して以来ソラコムの会社名を出して記事を書くのは初めてということで少々緊張しているところではありますが、11 日目の今日は　SORACOM IoT ストアで販売している [LTE-M Shield for Arduino](https://soracom.jp/store/5303/) の初期設定・接続診断スケッチにまつわるお話です。

[^a]: Customer Reliability Engineer の略ですが、密かに「Comic Relief Engineer」を目指しています。

# 初期設定・接続診断スケッチができるまで
Arduino とセンサを組み合わせてさまざまなデバイスをプロトタイピングされる方は個人・法人問わず数多くいらっしゃいますが、LTE-M Shield for Arduino はそんなデバイスを LTE-M 通信で SORACOM プラットフォームに、そしてインターネットに接続することで応用の可能性を無限に広げるポテンシャルを持つ画期的なシールドです。

![PXL_20211209_095851478.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/b231b729-91cd-b8ff-2192-53c18f1f21c1.jpeg)

ところで、ソラコムの CRE のおもな業務はお客様から日々送られてくるお問い合わせに回答を差し上げることですが、SORACOM IoT ストアなどで販売しているデバイスの利用方法や動作不良に関するお問い合わせへの回答もその一つです。
このデバイスに限った話でもないですが、たとえば「 LTE-M Shield for Arduino で通信できない」というお問い合わせがあった場合を一例にとっても、確認すべき事項は多岐に渡ります。たとえば…

- ジャンパピンやプッシュスイッチの状態は適切に設定されていますか？
- SIM カードは正しく挿入され、利用可能な状態になっていますか？
- アンテナは正しく取り付けられ、緩んでいませんか？
- 初期設定や APN 設定が書き込まれていて、セルラー通信セッションが確立していますか？

ざっとこれだけのことを確認する必要がありますが、確認事項が多いことはお客様にご負担をおかけすることになりますし、かといって不具合のお申し出があったすべての製品を返送いただいて我々が状況を確認することは現実的ではありません。
さらに、お問い合わせいただくお客様もデベロッパーからビギナーまでさまざまであるため、込み入った確認手順の場合は我々が知りたい情報を提供いただけない場合もあります。かつては状況確認や設定のために Arduino のシリアルモニタ経由で送信した内容を通信モジュールにパススルーするスケッチを書き込んでいただいた上でいくつかの AT コマンドをお試しいただく場合もありましたが、シリアルモニタの改行コード設定やタイプミスなどにより適切な結果が得られないケースもありました。

このようなお問い合わせをいただく場合、少なくともお客様は「 Arduino IDE を扱うことができて、スケッチをコンパイルして書き込むことができる」スキルをお持ちであることは間違いありません。このベースラインからできるだけお客様のお手を煩わせずに、我々が知りたい情報を提供いただいて適切な対処方法をご案内できるかが、迅速かつスムーズな問題解決のカギとなります。

# 初期設定・接続診断スケッチでできること
こうした背景もあり、問題の解決に必要な情報をシリアルモニタに出力し、実行結果をサポート窓口に返信いただくだけで手元の LTE-M Shield for Arduino の状況を確認・把握すべく作成したのが、タイトルにもある「初期設定・接続診断スケッチ」です。
使い方はソラコムのユーザーガイド「 [LTE-M Shield for Arduino の初期設定と接続確認をする ](https://users.soracom.io/ja-jp/guides/starter-kits/arduino/initial-configurations/) 」にも書かれているとおりで、お客様はスケッチを Arduino IDE に貼り付けて書き込み、実行するだけです。

このスケッチは LTE-M Shield for Arduino に搭載されている通信モジュールに各種 AT コマンドを送信し、先に挙げた確認事項のほとんどを明示的に取得してシリアルモニタに出力するものです。
たとえば画像の (1) の部分でエラーが表示される場合は LTE-M Shield for Arduino 本体や Arduino との接続に問題や不具合があると判断できますし、(2) のように `AT+CIMI`(SIM のIMSI の確認 )や `AT+QSIMSTAT?`(SIM の挿入状態 )コマンドがエラーになる場合は正常に SIM が挿入されていないか SIM カードの故障が疑われます。
また、(4) の部分でセルラー通信セッションが確立できていても (5) の`AT+QCSQ`( セルラー通信の状態取得 )コマンドの実行結果に含まれる信号強度が弱かったり、(6) のpong.soracom.io への ping 送信が失敗する場合はアンテナの緩みや通信が不安定な環境に設置されていることが想定されます。

![results_correct.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/5db96092-d6e2-6e34-d406-9ec07f4e2d13.png)

このスケッチが完成してからは、お問い合わせいただいた際の確認事項は最低限「外形的な故障の有無」と「スケッチの実行結果」になり [^*] 、確認いただく作業量やチケットのやりとりの回数が減少しました。また、上記ユーザーガイドの更新やレシピへの導線追加を進めたことでお客様自身で原因の切り分けを実施できるようになったことで、売れ行きに対してお問い合わせの件数も少なくなりました。これはソラコムの CRE が目指す「お客様が自身でプラットフォームを利用できる状態をつくる」[^**]ことを達成できた、ポジティブな事例の一つと言えると思います。

[^*]: もちろん言語的に状況確認をあわせてお願いしています。

[^**]: https://blog.soracom.com/ja-jp/2020/04/15/introducing-soracom-cre/


# 高度な使い方

ここまではサポートに関連して平易な使い方をした場合の話題でしたが、このスケッチのもう一つの目的は「セルラー通信関連の最低限の接続設定と確認コマンドを網羅する」ことと、TinyGSM ライブラリを利用する際に「定義のない AT コマンドを送信するスケッチを作成する際のサンプル実装」になることを意識した作りになっていることです。

ソラコムが提供する LTE-M Shield for Arduino や M5Stack 向けの IoT レシピではそれらに搭載されている通信モジュールに対応していることもあって TinyGSM を利用したものが多いです。一方で通信モジュールが対応しているコマンドはライブラリに実装されている以外にも数多くあり、複数のモデムに対応した汎用的なライブラリである都合上、すべてのコマンドと関数の対応は困難であるという事情があります。
とはいえ基本的にコマンドを送信して必要な時間だけ待機するという流れはすべての AT コマンドに対応した通信モジュールで共通ですので、これらをイチから実装するよりはライブラリを活用するほうがメリットが大きいといえます。車輪の再発明は時として気付きを得られますが、たいていはそれを活用して別のものを作るほうが実りが大きいのです。

ということで、ここでは初期設定・接続診断スケッチの実装の一部を引用しながら TinyGSM を使ったプロトタイピングの参考になりそうなポイントを解説してみようと思います。

## 事前準備と参考資料

スケッチを開発する上で参考にしたり必要な情報が記載されているリファレンスは以下のとおりです。

- [connectivity_diagnostics_for_lte_m_shield.ino](https://github.com/soracom-labs/arduino-dragino-unified/blob/main/connectivity_diagnostics_for_lte_m_shield/connectivity_diagnostics_for_lte_m_shield.ino)
  - 解説するスケッチの実体です。
- [TinyGSM](https://github.com/vshymanskyy/TinyGSM)
  - 利用するライブラリです。
- [Quectel BG96](https://www.quectel.com/product/lpwa-bg96-cat-m1-nb1-egprs) のAT Commands Manual および Application Note
  - LTE-M Shield for Arduino に搭載されている通信モジュールの AT コマンドマニュアルです。ダウンロードには会員登録が必要です。
- [SORACOM で学ぶ AT コマンド入門 ](https://zenn.dev/bearmini/books/12ebf8331d6f9ea1d90d)
  - AT コマンドの使い方について体系的に把握できます。

## AT コマンドの送受信
以下は AT コマンドの送受信部(`executeAT` 関数)と近しい関数を抜き出してきたものです。AT コマンドは送信後にコマンドの実行完了を待つ必要があるため、TinyGSM の`sendAT` 関数と待ち時間指定に対応している `waitResponse` 関数を利用しています。
なお、コマンドの待機時間は通信モジュールによって異なり、最大処理時間が AT コマンドマニュアルに記載されているケースもあります。

```c
String formatResponse(String str) {
 str.replace("\r\nOK\r\n", "[OK]");
 str.replace("\rOK\r", "[OK]");
 str.replace("\r\n", "\t");
 str.replace("\r", "\t");
 str.trim();
 return str;
}

String executeAT(String command, long timeout) {
 String buf;
 
 MODEM.sendAT(command);
 if (MODEM.waitResponse(timeout, buf) != 1) {
  return "ERROR";
 }

 return formatResponse(buf);
}

void showModemInformation() {
 String res;

 CONSOLE.println(F("> AT+GSN"));
 res = executeAT(F("+GSN"), 300);
 CONSOLE.println(res);

 ...
}
```

ちなみに、上記に近いことを TinyGSM は以下のような関数で定義していました ( [参考 ](https://github.com/vshymanskyy/TinyGSM/blob/master/src/TinyGsmGPRS.tpp#L102-L111) ) 。あれ、さっき車輪がどうこう言ってたのは誰…？という感じですが、大きな違いは実行結果の取得手法と待ち時間指定がない点のみなので、自分のスケッチ内で独自の関数を定義するうえでは上記のように似たような形で実装すればよいということが分かると思います。

```c
String getIMEIImpl() {
 thisModem().sendAT(GF("+GSN"));
 thisModem().streamSkipUntil('\n'); // skip first newline
 String res = thisModem().stream.readStringUntil('\n');
 thisModem().waitResponse();
 res.trim();
 return res;
}
```

## ping 送信結果のパース
応用として、スケッチの最後に実施している ping を送信するケースを紹介します。じつは TinyGSM には ping を送信する関数は定義されていません。

`AT+QPING` コマンドは指定した送信先に ping を送信し、その結果を返すコマンドです。`AT+Q` から始まるコマンドはメーカー独自のコマンドで、同じような機能でもメーカーによってパラメータや出力フォーマットが異なるので汎用化が少々難しいものです。
また、これまでに出てきた AT コマンドは概ね 1行で実行結果が表現されますが、`AT+QPING` は下記のように先に `OK` が返ってから ping の実行結果が複数行で表示されるので、少し変わった処理が必要になります。

```shell:表示例
AT+QPING=1,"pong.soracom.io",3,3
OK

+QPING: 0,"100.127.100.127",32,75,255

+QPING: 0,"100.127.100.127",32,80,255

+QPING: 0,"100.127.100.127",32,87,255

+QPING: 0,3,3,0,75,87,80
```

行単位で読み込む関数は TinyGSM に実装されています。結果はカンマ区切りで返ってくるので、AT コマンドマニュアルを見ながら各値の名前を追加しつつ見やすい形式で表示されるように `strtok` 関数などを駆使して整形しています。ちょっと泥臭いですね。

```c
void pingToSoracomNetwork() {
 String res;
 res = executeAT(F("+QPING=1,\"pong.soracom.io\",3,3"), 3000);

 if (isOk(res)) {
  MODEM.stream.readStringUntil('\n');
  String try1 = MODEM.stream.readStringUntil('\n');
  MODEM.stream.readStringUntil('\n');
  String try2 = MODEM.stream.readStringUntil('\n');
  MODEM.stream.readStringUntil('\n');
  String try3 = MODEM.stream.readStringUntil('\n');
  MODEM.stream.readStringUntil('\n');
  String summary = MODEM.stream.readStringUntil('\n');
  MODEM.stream.readStringUntil('\n');

  printPingResult(try1);
  printPingResult(try2);
  printPingResult(try3);
  printPingSummary(summary);
 }
}

int printPingResult(String input) {
 input.replace("+QPING: ", "");

 char buf[100] = { 0 };
 input.toCharArray(buf, 100);
 if (strlen(buf) <= 0) return -1;
 
 int result = atoi(strtok(buf, ","));
 if (result == 0) {
  CONSOLE.print(F("Dest=")); CONSOLE.print(strtok(NULL, ","));
  CONSOLE.print(F(", Bytes=")); CONSOLE.print(strtok(NULL, ","));
  CONSOLE.print(F(", Time=")); CONSOLE.print(strtok(NULL, ","));
  CONSOLE.print(F(", TTL=")); CONSOLE.print(strtok(NULL, ","));
  CONSOLE.println();
 }
 else {
  CONSOLE.print("(R)Error: "); CONSOLE.println(result);
 }

 return result;
}
```

# おわりに
いかがだったでしょうか。この記事では LTE-M Shield for Arduino の初期設定・接続診断スケッチの開発背景や内容を解説してきました。記事を通してソラコム CRE の取り組みやその思いが少しでも伝われば幸いです。

12 日目はソリューションアーキテクト・横田が担当します。おたのしみに！