メインコンテンツへスキップ
見出し画像

【IoT】LTE-M Shield for Arduinoのsetup()関数を見直し、消費電力量と通信量を低減する方法

    前回記事では、LTE-M Shield for ArduinoでHTTP送信からUDP送信に見直すことで、消費電力量と通信量を低減したことについてまとめました。本記事では、setup()関数を見直すことによる消費電力量と通信量の低減効果について整理します。結論としては、HTTP→UDP x setup()関数改良で、消費電力量を3.1→1.2mW(▲1.9mW)、通信量を7029→308bytes(▲6721bytes)にすることができ、大幅な低減効果を確認できました。

    背景と目的

    電池駆動型のIoTデバイスにおいて、1回の通信あたりの消費電力量と通信量を低減させることは重要な課題の一つです。前回記事では、UDP送信による低減について書きましたが、UDP送信でもプログラム開始から終了までに16秒もかかっていました。この主な要因はsetup()関数内の通信モジュール関連コードです。本記事では、通信モジュール関連コードの要否を一つ一つ検証し、消費電力量と通信量の低減を目指します。

    詳細

    SORACOM公式サイトに掲載されているsend_uptime_with_soracom.ino
    において、setup()関数の通信モジュールに関わる部分に関し、一つ一つ要否を検証していきます。以下がsetup()関数内のコードです。

    void setup() {
      CONSOLE.begin(9600);
    
      CONSOLE.println();
      CONSOLE.print(F("Welcome to "));
      CONSOLE.print(SKETCH_NAME);
      CONSOLE.print(F(" "));
      CONSOLE.println(VERSION);
    
      CONSOLE.print(F("resetting module "));
      pinMode(BG96_RESET,OUTPUT);
      digitalWrite(BG96_RESET,LOW);
      delay(300);
      digitalWrite(BG96_RESET,HIGH);
      delay(300);
      digitalWrite(BG96_RESET,LOW);
      CONSOLE.println(F(" done."));
    
      LTE_M_shieldUART.begin(BAUDRATE);
    
      CONSOLE.print(F("modem.restart()"));
      modem.restart();
      CONSOLE.println(F(" done."));
    
      CONSOLE.print(F("modem.getModemInfo(): "));
      String modemInfo = modem.getModemInfo();
      CONSOLE.println(modemInfo);
    
      CONSOLE.print(F("waitForNetwork()"));
      while (!modem.waitForNetwork()) CONSOLE.print(".");
      CONSOLE.println(F(" Ok."));
    
      CONSOLE.print(F("gprsConnect(soracom.io)"));
      modem.gprsConnect("soracom.io", "sora", "sora");
      CONSOLE.println(F(" done."));
    
      CONSOLE.print(F("isNetworkConnected()"));
      while (!modem.isNetworkConnected()) CONSOLE.print(".");
      CONSOLE.println(F(" Ok."));
    
      CONSOLE.print(F("My IP addr: "));
      IPAddress ipaddr = modem.localIP();
      CONSOLE.println(ipaddr);
    }

    resetting moduleは工夫すれば不要(多分)

    CONSOLE.print(F("resetting module "));
    pinMode(BG96_RESET,OUTPUT);
    digitalWrite(BG96_RESET,LOW);
    delay(300);
    digitalWrite(BG96_RESET,HIGH);
    delay(300);
    digitalWrite(BG96_RESET,LOW);
    CONSOLE.println(F(" done."));

    まず、この部分の要否を考えます。
    この記事を読むと、長期運用時に、通信モジュールの動作が不安定になることがあり、その対策としてモジュールリセットしているようです。loop()関数の最後にdelay(INTERVAL_MS)を入れ、ループさせているプログラムの場合、定期的に通信モジュールをリセットする仕組みがあったほうが良さそうです。
    一方、Wio Extension RTCでsleepさせるプログラムの場合、sleep時間毎に電源がON/OFFされるため、わざわざ通信モジュールをリセットする必要はないのではないかと思っています。

    modem.restart()をmodem.init()に変更

    CONSOLE.print(F("modem.restart()"));
    modem.restart();
    CONSOLE.println(F(" done."));

    この部分を考えます。このmodem.restart()は5秒程度かかるため、なんとか改善したい。TinyGSM(通信モジュールのライブラリ)のREADME.mdを見ると、このように書かれています。

    Initialize the modem
    modem.init() or modem.restart()
    restart generally takes longer than init but ensures the module doesn't have lingering connections

    出典:TinyGSMのREADME.md

    これだけはrestart()とinit()の違いがわかりませんが、TinyGSMのサンプルコード等を見ると、init()でもいけそう、ということで、restart()をinit()に置き換えます。(いまいち、この違いを言語化できない。。)

    /*
    CONSOLE.print(F("modem.restart()"));
    modem.restart();
    CONSOLE.println(F(" done."));
    */
    CONSOLE.print(F("modem.init()"));
    modem.init();
    CONSOLE.println(F(" done."));

    modem.getModemInfo()は不要

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

    この部分で、モデムの情報を取得しています。テスト段階では必要かもしれませんが、実運用段階では不要だと判断し、削除します。

    modem.waitForNetwork()は必要 

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

    このコードでネットワークに接続しているため、必要です。

    modem.gprsConnect()は必要

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

    このコードで、SORACOMのAPN等を設定しているため、必要です。

    modem.isNetworkConnected()は必要

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

    このコードで、SORACOMへの接続を待っているため、必要です。

    modem.localIP()は不要

    CONSOLE.print(F("My IP addr: "));
    IPAddress ipaddr = modem.localIP();
    CONSOLE.println(ipaddr);

    このコードでIPアドレスを取得しています。テスト段階では必要かもしれませんが、実運用段階では不要だと判断し、削除します。

    コード全文

    ということで、改良後コード全文です。

    void setup() {
      CONSOLE.begin(9600);
    
      CONSOLE.println();
      CONSOLE.print(F("Welcome to "));
      CONSOLE.print(SKETCH_NAME);
      CONSOLE.print(F(" "));
      CONSOLE.println(VERSION);
      /*
      CONSOLE.print(F("resetting module "));
      pinMode(BG96_RESET,OUTPUT);
      digitalWrite(BG96_RESET,LOW);
      delay(300);
      digitalWrite(BG96_RESET,HIGH);
      delay(300);
      digitalWrite(BG96_RESET,LOW);
      CONSOLE.println(F(" done."));
      */
      LTE_M_shieldUART.begin(BAUDRATE);
      /*
      CONSOLE.print(F("modem.restart()"));
      modem.restart();
      CONSOLE.println(F(" done."));
      */
      CONSOLE.print(F("modem.init()"));  // restart()の代わりににinit()
      modem.init();
      CONSOLE.println(F(" done."));
      /*
      CONSOLE.print(F("modem.getModemInfo(): "));
      String modemInfo = modem.getModemInfo();
      CONSOLE.println(modemInfo);
      */
    
      CONSOLE.print(F("waitForNetwork()"));
      while (!modem.waitForNetwork()) CONSOLE.print(".");
      CONSOLE.println(F(" Ok."));
    
      CONSOLE.print(F("gprsConnect(soracom.io)"));
      modem.gprsConnect("soracom.io", "sora", "sora");
      CONSOLE.println(F(" done."));
    
      CONSOLE.print(F("isNetworkConnected()"));
      while (!modem.isNetworkConnected()) CONSOLE.print(".");
      CONSOLE.println(F(" Ok."));
      /*
      CONSOLE.print(F("My IP addr: "));
      IPAddress ipaddr = modem.localIP();
      CONSOLE.println(ipaddr);
      */
    }

    比較試験結果

    比較試験を実施しました。まず、前回記事でも掲載したHTTP/TCP/UDP x setup()関数標準の試験結果を再掲します。

    画像

    これに対して、HTTP/TCP/UDP x setup()関数改良の試験結果は下記の通りです。

    画像

    HTTP/TCP/UDPとも、改善していることがわかります。HTTP x setup()標準と、UDP x setup()改良に着目して比較すると以下のとおりです。▲12秒、▲1.9mAh、▲6721bytesと大幅改善しています。

    画像

    単3電池で駆動させた場合

    1回通信あたりの消費電力量が1.2mWhということは、例えば2000mAhの単3ニッケル水素電池4本で稼働させた場合、2000mAh x 1.2V/本 x 4本 = 9600mWhであり、9600mWh / 1.2mWh/回 = 8000回通信できる計算です。1時間に1回通信するデバイスを想定すると、8000回 / 24回/日 = 333日稼働できる計算です。単3x4本を単2x4本や単1x4本にすることで、1年以上の稼働が実現できそうです。

    まとめと今後の課題

    UDP送信かつsetup()関数を改良することで、消費電力量と通信量を低減させることができました。LTE-M Shield for Arduinoを電池駆動で動かす場合、有用な手段だと思いますので、ご参考ください。
    なお、少し気になっているのは、resetting moduleの削除でして、実際1年以上稼働させた実績では、このコードを削除することによって問題は発生していないのですが、どうなんでしょうか。もう少し深掘りして不要だと確信したいところです。または、毎回resetting moduleするのではなく、例えば1日1回だけresetting moduleするや、エラーが起こった際にresetting moduleなどで頻度を減らすか。
    もう少し考えてみたいと思います。

    参考

    ・LTE-M Shield for Arduinoのresetting moduleについて

    ・回路図

    https://github.com/dragino/NB-IoT/blob/master/NB-IoT%20Shield/hardware/IoT%20Shield/v1.2/IoT%20Shield.v1.2%20SCH.pdf

    ・TinyGSMのREADME.md


     
     
    IoTデバイス開発、住宅修繕、家電修理、エアコン設置、電気工事など、できることを増やし続けるモノづくりエンジニア!できること(=生活自給力)を増やし、高知の課題を解きたい。興味はIoT・AI・クラウド、機械・電気・ソフト、空き家再生など。好きな県は高知県、好きな国はインド。

    あなたへのおすすめ