soracomバイナリパーサーを使ってLTE-M Shield for Arduinoのデータ通信量を削減する

Quectel 社製 LTE-MモジュールBG96 が搭載されている[IoT スターターキット for Arduino](https://soracom.jp/store/5243/)の公式サンプルスケッチを例にデータ利用量を大幅に削減する方法を紹介します。

まず公式[soracom-labのリポジトリ](https://github.com/soracom-labs/arduino-dragino-unified)から[send_multiple_sensor_data_with_soracom.ino](https://github.com/soracom-labs/arduino-dragino-unified/blob/main/send_multiple_sensor_data_with_soracom/send_multiple_sensor_data_with_soracom.ino)を使ってみます。
[セットアップページ](https://dev.soracom.io/jp/start/iotstarter_kit_for_arduino/)に従って送信を開始するとsoracom Harvest Dataのページでこのようにデータが流れてきているのがわかります。
![スクリーンショット 2021-02-26 195538.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/67c9d793-b986-7a75-5e21-5835da2072a1.png)

このスケッチではHTTP　POSTメソッドでJSON形式のデータを送信しています。

``` c:send_multiple_sensor_data_with_soracom.ino
(163行目）
 char payload[90];
  sprintf_P(payload, PSTR("{\"light\":%d,\"sound\":%d,\"temp_c\":%s,\"humi\":%d,\"air_pressure_hpa\":%s}"),
    light_mapped_value, sound_mapped_value, temp_buf, humi, hPa_buf);
  /* example:
   *  {"light":48,"sound":60,"temp_c": 25.0,"humi":34,"air_pressure_hpa":1015.5}
   */
  CONSOLE.println(payload);

  CONSOLE.print(F("Send..."));
  drawText_P(&u8x8, PSTR("Send..."));
  /* connect */
  if (!ctx.connect(ENDPOINT, 80)) {
    CONSOLE.println(F("failed."));
    drawText_P(&u8x8, PSTR("failed."));
    delay(3000);
    return;
  }
  /* send request */
  char hdr_buf[40];
  ctx.println(F("POST / HTTP/1.1"));
  sprintf_P(hdr_buf, PSTR("Host: %s"), ENDPOINT);
  ctx.println(hdr_buf);
  ctx.println(F("Content-Type: application/json"));
  sprintf_P(hdr_buf, PSTR("Content-Length: %d"), strlen(payload));
  ctx.println(hdr_buf);
  ctx.println();
  ctx.println(payload);
  /* receive response */
  // NOTE: response header and body are ignore due to saving for memory
  ctx.stop();
  CONSOLE.println(F("done."));
  u8x8.clearLine(6);

```
この部分を以下のように変更します
1.　int型(float型)の数値を%04X（%08lX)フォーマット指定子をつかって16進数に置き換え

2.　ATコマンドの+QISENDEXを使って送信
https://www.quectel.com/product/bg96.htm
にある Quectel_BG96_TCP(IP)_AT_Commands_Manual_V1.1.pdf を参照

>2.1.9. AT+QISENDEX Send Hex String
This command cannot be applied for "UDP SERVICE" and "TCP LISTENER" sockets.
Write Command
AT+QISENDEX=\<connectID>,\<hex_string>
Parameter
\<connectID> Integer type. The socket service index. The range is 0-11.
\<hex_string> String type. Hex string. The max length is 512 bytes.

3.　送信先をHTTP　80番ポートからTCP　23080番ポートに変更
https://dev.soracom.io/jp/unified_endpoint/how-it-works/

```c:HEX_send_multiple_sensor_data_with_soracom.ino

  char cmd[64]={"0"};
  sprintf_P(cmd, PSTR("+QISENDEX=0,\"%04X%04X%08lX%04X%08lX\""),
       light_mapped_value, sound_mapped_value, temp, humi, hPa);
  /* binary-parser format:
   *  light:0:int:16 sound:2:int:16 temp_c:4:float:32 humi:8:int:16 air_pressure_hpa:10:float:32
   */
  CONSOLE.println(cmd);
  CONSOLE.print(F("Send..."));
  drawText_P(&u8x8, PSTR("Send..."));
  /* connect */
  if (!ctx.connect(ENDPOINT, 23080)) {
    CONSOLE.println(F("failed."));
    drawText_P(&u8x8, PSTR("failed."));
    delay(3000);
    return;
  }
  /*send AT command */
  modem.sendAT(cmd);
  /* receive response */
  // NOTE: response header and body are ignore due to saving for memory
  ctx.stop();
  CONSOLE.println(F("done."));
  u8x8.clearLine(6);

```




次に　ユーザーコンソール > SIM グループ　>　SORACOM Air for Cellular 設定　>　バイナリーパーサーフォーマット　に以下のように入力します

```
light:0:int:16 sound:2:int:16 temp_c:4:float:32 humi:8:int:16 air_pressure_hpa:10:float:32
```
![スクリーンショット 2021-02-26 201201.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/79120994-ebdf-03c8-214e-5658ee3fc195.png)
バイナリパーサーカスタムフォーマットについては[https://dev.soracom.io/jp/docs/binary_parser/](https://dev.soracom.io/jp/docs/binary_parser/)　を参照


再びHarvest Dataで見てみると"binaryParserEnabled":true となっています。
![スクリーンショット 2021-02-26 201709.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/0e8d9452-0b76-204b-1e5a-b778b3a52c07.png)

データ使用量を比べて見ると1時間あたり420KiB から50KiB程まで削減できました。
![無題の図形描画.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261581/af722d58-d764-be37-9ec9-158e796af81d.png)


さらに、例えばDHT11の温度は整数で（-127～127）で表せるので
16bitのint型でなくchar(int8_t)にすれば1byteに、

気圧はhPaの小数第2位（Pa）で800.00～1200.00ぐらい表せればいいので
(int)(hPa*100-100000）で送ってバイナリーパーサーoperationsで:+100000/100　
として元の数値に戻せば2byteに削減できます。

またbyte単位でなくbit単位でもカスタムできます。