SORACOM Air のバイナリパーサーを使ってみた

##概要

[SORACOM Air for セルラー](https://soracom.jp/services/air/cellular/) のLTE-M回線を使って、センサから吸い上げたデータを、[SORACOM Unified Endpoint](https://dev.soracom.io/jp/unified_endpoint/what-is-unified_endpoint/)に送信します。

通信量を削減すると、通信料金を安くできるだけでなく、消費電力の節約にもつながります。

そこで、[バイナリパーサー](https://dev.soracom.io/jp/docs/binary_parser/)を使って、通信量の削減に挑戦してみます。


##開発環境

Arduino IDE: 1.8.9
[Wio_cell_lib_for_Arduino](https://github.com/SeeedJP/Wio_cell_lib_for_Arduino)
こちらのライブラリを[フォーク](https://github.com/cami/BG96_controller_for_nRF52840_LibforArduino)して、開発しております。

##SORACOMコンソールでバイナリパーサーを設定

コンソールにログイン後、左上の **MENU** を開き、 **SORACOM Air for Cellular** を選択します。

**基本設定** 内の **SORACOM Air for Cellular 設定** を開きます。

すると、最下部に
<img width="1317" alt="スクリーンショット 2019-06-30 19.10.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317198/bc93aadb-47d3-d099-a773-656101ba31b6.png">
**バイナリパーサー設定** が出てきますので、ONにして保存します。

画像の右側の **フォーマット** 部分に、設定を記述していきます。


##バイナリパーサーのフォーマットを作成

SORACOMさんの[解説記事](https://dev.soracom.io/jp/docs/binary_parser/)を参考にしながら、フォーマットを作成していきます。

バイナリパーサーに送るデータは、「経度：139752101」、「緯度：35682501」です。
バイナリパーサーは floatにも対応していますが、使用しているライブラリの UDP通信のメソッドが charのみに対応しているので、 10^6をかけています。

フォーマットは、**lat:offset:length:operations lng:offset:length:operations**という形になります。

###16進数への変換

経度: (139752101)10 = (85472A5)16
緯度:  (35682501)10 = (22078C5)16

変換した16進数をくっつけます。
バイナリパーサーで変換するバイナリは、**0x85472A522078C5**となります。

このバイナリの１文字は、16進数ですので4bitsです。

そのため、 **length = 4 * 8**です。

###offsetの指定

経度・緯度を16進数で表したものはそれぞれ７桁ですが、送信されるデータは先頭に**0**が付与されて８桁です（1Byteごとに送信されるため）。

そのため、offsetは
lat: 0
lng: 4
となります。

###operationsの設定

こちらは、10^6で割れば良いので、**/1000000**です。

###作成されたフォーマット

以上より、作成されたフォーマットは、**lat:0:uint:32:/1000000 lng:4:uint:32:/1000000**です。


##バイナリへの変換

ここで私は、つまづいてしまいました。

心優しい方が、実装してくださっていたので、それを活用しております。
[SORACOM バイナリパーサーで、位置情報送ってみよう！](https://qiita.com/KazuyukiEguchi/items/be823fd27f242b3cb277)

私の失敗に関しては後ほど触れます。

バイナリパーサーでの変換に成功したソースコードはこちらです。

```grove-gps.ino
float lat = 139.752101;
float lng = 35.682501;

unsigned long int const lat_decimal = (unsigned long int)(lat * 1000000);
unsigned long int const lng_decimal = (unsigned long int)(lng * 1000000);

// Calculating the digits of lat, lng in hexadecimal.
unsigned long int lat_to_calc_digit = lat_decimal;
unsigned long int lng_to_calc_digit = lng_decimal;
unsigned int lat_digit_hex = 0;
unsigned int lng_digit_hex = 0;
while (lat_to_calc_digit!=0) {
    lat_to_calc_digit /= 16;
    lat_digit_hex++;
}
while (lng_to_calc_digit!=0) {
    lng_to_calc_digit /= 16;
    lng_digit_hex++;
}
int size_of_post_data_lat = (int)(ceil((double)lat_digit_hex/2));
int size_of_post_data_lng = (int)(ceil((double)lng_digit_hex/2));

// Convert into binary data to post to SORACOM binary parser.
// Binary parser configuration on SORACOM console: "lat:0:uint:32:/1000000 lng:4:uint:32:/1000000"
char *post_data_lat[size_of_post_data_lat];
char *post_data_lng[size_of_post_data_lng];
memcpy(&post_data_lat[0], Nectis.ConvertDecimalToHex(lat_decimal, size_of_post_data_lat), size_of_post_data_lat);
memcpy(&post_data_lng[0], Nectis.ConvertDecimalToHex(lng_decimal, size_of_post_data_lng), size_of_post_data_lng);

char post_data[] = {0x00};
memcpy(&post_data[0], post_data_lat, size_of_post_data_lat);
memcpy(&post_data[0]+size_of_post_data_lat, post_data_lng, size_of_post_data_lng);
```

```NectisCellular.cpp
char *NectisCellular::ConvertDecimalToHex(unsigned long int const decimal, int byte_size) {
    // The last index of post_data is filled with 0x00 for print function.
    memset(&HexConvertedFromDecimal[0], 0X00, sizeof(HexConvertedFromDecimal));
    
    for (int i = 0; i < (int) byte_size; i++) {
        // 16進数に変換し、４ビットずつ post_data を埋めていく
        HexConvertedFromDecimal[i] = (decimal >> (8 * ((byte_size - 1) - i))) & 0xff;
    }
    return HexConvertedFromDecimal;
}
```


ポイントは、 **memcpy()** を用いて、取得した緯度経度のデータをくっつけているところです。

実際に送信したデータをArduinoIDEのシリアルモニタで確認すると、
**Send:Tr⸮ x⸮**
と文字化けしており、変換に成功したことが確認できます。


##送信データをSORACOMコンソールで確認

**{"lat":139.752101,"lng":35.682501,"binaryParserEnabled":true}**

となっており、バイナリパーサーで正確に変換できていることを確認できました。


##失敗したバイナリへの変換

こちらの記事にまとめております。

[SORACOM Air のバイナリパーサーでうまくデータを変換できない。](https://qiita.com/NaotoFujihiro/items/63f75e0ea9f5be65f944)



##参考文献
https://blog.soracom.jp/blog/2018/09/27/lte-m-launch/