こんにちは、エンジニアの@k-tkrです。 今年2月にスマートショッピングに入社し、スマートマットのファームウェア開発やスマートマットから送られてくる重量データの分析などをやっています。
現在のスマートマットではWifi通信を使って重量データをクラウド上に送っています。 今後Wifiルータを設置できないような場所でもスマートマットを使っていただけるよう、SIMカードを内蔵したスマートマットの開発を進めてきました。 SIM内蔵に当たって通信制御のプログラムを一から作り直すことになったので、その開発について書いてみたいと思います。
SIMモジュールを制御するためのマイコンにはSTマイクロエレクトロニクスのSTM32を使いました。 STM32シリーズはポピュラーなマイコンだけあって開発環境や作例が充実していて開発しやすいなという印象でした。
マイコンのペリフェラル設定には、STマイクロエレクトロニクスが出しているSTM32CubeMXというソフトを使いました。
このソフトを使うとペリフェラルがGUIでワンタッチ設定でき、コードを自動生成してくれます。
ペリフェラル設定のコードを書くのはかなり面倒なところですが、STM32シリーズの場合はこのおかげで作業が相当軽減されます。
ただし自作のコードと組み合わせるときに、コードを正しい場所に書かないと自動生成コードで上書きされてしまうということだけは注意する必要があります(自作のコードはできるだけ別ファイルで管理することでほぼ回避されます)。
マイコンからUART通信によりATコマンドを送信することでSIMモジュールを制御します。 基本的には 1. SIMモジュールの電源ONコマンドを送る 2. 通信の設定(PDP, APNなど)をする 3. 通信によりメッセージをやり取りする 4. SIMモジュールの電源OFFコマンドを送る という流れで進みます。 STM32には複数のUARTが備わっているのですが微妙に使える機能が違います。 またUART用の端子が他の機能として使われていることもあり、SIMモジュールとの通信の他にも重量計や外部メモリとの通信、スマートマットの電圧監視に使う端子なども必要なので上手くやりくりしなければならないです。 今回はデータのオーバーランを防ぎ正確に通信を行うためにハードウェアフロー制御付きのUARTを使っています(このあたりの設定は本来UART_HandleTypeDef構造体の変数を設定しなければならないのですがCubeMXを使うと簡単にできます)。
マイコンの通信制御プログラム自体は上記の実装と通信中にLEDを点滅させるくらいなので比較的単純に実装できます。 UART受信の割り込み制御、タイマー割り込み制御などを実装する必要がありますが、これらもひな形はCubeMXで作成できます。
開発の初期段階ではNucleoボードや開発用基板とSIMモジュールをジャンパ線で結線していました。 ハードウェアフロー制御のためUARTだけでVdd/Rx/Tx/RTS/CTS/GNDの6箇所結線する必要があり、デバッグ用配線や外部メモリとのSPI通信用の配線と合わせるとすぐにスパゲッティ配線になってしまい大変でした(しかもSIMモジュールの開発用基板では電圧の変換が必要で結局ハードウェアフロー制御が使えなかった…)。
普段プログラムを書くときは深く考えずにLF(\n)を使っていたのですが、SIMモジュールにATコマンドを送る場合はCRLF(\r\n)を使う必要がありました。 間違えてLFを使ってしまいSIMモジュールからの応答がなく、デバッグしたり配線を変えたりオシロを当てたり(配線かペリフェラル設定の問題だと思い込んでいたため)と悩みました。 ドキュメントをしっかり読まなければならないと痛感しました。
乾電池で動くスマートマットでの消費電力量をできるだけ抑えるために、消費電力の大部分を占めるSIMモジュールの起動はできる限り短時間で済ませる必要があります。 起動時間をなるべく削減するために、電源ONコマンド送信後UARTが有効化されたら即通信を開始しようとしたのですが、通信時間が全く削減されず、電波状況も”Not known or not detectable”ステータスが返ってきていました。 どうやらUARTが有効化されても通信ができるようになるまでには更に時間が掛かるようです。 結局典型的な部分最適に陥ってしまっていました。
ATコマンドを送信⇒SIMモジュールからの応答を受信という流れにおいて、初期設定では応答メッセージに送信したコマンドが含まれています。 長いコマンドを送信した際(AT+QISENDでメッセージを送信した際など)に応答がバッファから溢れてしまうことが起きていました。 ATE0でエコーをオフにすることで解決できました。
これまではメモリがGB単位で十分に使える環境でコーディングをしていたため、数十KBのメモリの中に通信制御をはじめ電源制御・OTA・ブートローダーといったコードを全て収めるのには苦労しました。 最終的にCubeMXで自動生成させずに手動のコードを作成しコードサイズを減らした箇所もあります。 また、書き慣れていたC++とC言語の細かな違い(bool型が使えない、std::stringやstd::vectorが使えない)にもよく躓きました。
前職までC++でコーディングをしていたので違和感なくマイコンプログラミングができるかと思っていたのですが想像以上にいろいろなギャップがありました。 特にメモリ配置やレジスタなどはこれまでほとんど気にしたことがなかったため、公式ドキュメントやデータシートを眺めながらデバッグし挙動を確かめるという試行錯誤の連続でした。 IoTの分野はこれからまだまだ成長する分野なのでSIMの他にも今後新しいモジュールと格闘していけたらと思います。