【IoT入門】GPSトラッカーで遊んでみた話

IoTデバイスとして各種センサーを接続可能な汎用GNSS/GPSトラッカー、マエストロ・ワイヤレス社（**※**）の**BOLERO**をいじる機会があったので、使い方を簡単に紹介する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/ce0c1607-7b4c-77a4-38a6-5cd1d62d5693.png)

**※**：香港に本社を置くメーカー。建機等の車両動態管理、産業機械などの遠隔監視に強み。2019年7月、Lantronix社に買収された。BOLEROの仕様は[こちら](https://cdn.lantronix.com/wp-content/uploads/pdf/BOLERO40_PB_A4.pdf)。

# 基本知識
本記事に興味を示す読者なら既にご存知のことと思うが、最初に基礎的なことから解説する。

## IoTとM2M
**IoT**とは、あらゆる**モノ**がインターネットに繋がる概念をいう。この〝**モノ**〟にあたるのを**エッジデバイス**といい、インターネットに繋ぐための中継装置を**ゲートウェイ**という。ゲートウェイを通してクラウドに蓄積された膨大な情報、これを**ビッグデータ**というが、この<font color="Red">ビッグデータを分析し、新たな価値を生み出してビジネスに活用すること</font>こそIoTの本質と言っても過言ではない。
IoTで収集したデータをすべて送信しようとすると大容量の通信回線が必要になり、コストの増大やレイテンシ（遅延）が懸念される。これを解決する技術としてIoTデバイス側に処理機能を持たせる**エッジコンピューティング**があり、近年注目されている。

**M2M**は機器同士が直接ネットワークで接続し、相互に情報をやり取りする。情報をインターネットやクラウドに送るところまでの機能は持っていない。なので、M2MはIoTのサブセットといえる。

## GPSとGNSS
人口衛星によって地上の現在位置を特定するものを衛星測位システムと呼ぶが、その中でも地球すべてを測位可能なものを**GNSS**と呼ぶ。
広く周知されている**GPS**はアメリカが開発したシステムであり、GNSSのひとつである。他にロシアのGLONASS、EUのGalileo、中国の北斗、日本の「みちびき」（準天頂衛星システム：QZSS）などがある。

今日のGNSS測量ではGPSを含む複数のGNSSを併用し、より効率的で精度の高い測量が可能になっている。

## ロガーとトラッカー
**GNSSロガー**は位置情報を端末に記録する（＝ログを取る）だけ。GPS電波を受信できても送信することはできない。位置情報を閲覧するにはパソコンなどに接続する必要がある。

一方、**GNSSトラッカー**は携帯電話の回線（3G/LTE）で位置情報をサーバに送信するため、パソコンやスマホなどでリアルタイムに位置情報を確認できる。ただしデータ通信専用のSIMを契約することになるので、携帯電話同様に月々の費用が発生する。近年**MVNO**の出現により、リーズナブルな料金でトラッカーを運用できるようになった。

## GSMと3G
**GSM**は第2世代携帯電話の通信方式のひとつで、多くの国で使用されており、発展途上国では未だ主流である。

**3G**は第3世代携帯電話の通信方式のひとつで、電波が届く範囲が**4G（LTE）**よりも広く、さまざまな場所で通信できるという利点がある反面、通信速度が**4G**より遅いというデメリットがある。動画再生やネットサーフィンは快適とは言えないが、少量のデータ通信で問題になることは殆どないだろう。

ちなみにアナログ携帯電話が**1G**（第1世代）、デジタル携帯電話が**2G**（第2世代）で、これに続く**3G**（第3世代）という意味である。

# 使ってみよう
小箱を開梱すると、BOLERO本体、制御ボックス、プリペイドSIMカード（評価用）、USBシリアル変換アダプタ、ACアダプタ、取説が入っている。
BOLEROに給電を始めると、まもなく赤色のLEDが点灯または点滅する。
他のGPSトラッカー同様、GPS、3G/GSMそれぞれの電波状態が、ステータスLEDで分かるようになっている。

|色|状態|意味|
|:-:|:-:|:-----------------|
|<font color="Red">**赤**</font>|消灯|電源が入っていない。|
|<font color="Red">**赤**</font>|点灯|GPS状態は良好である。（4つ以上のGPS衛星が捕捉できている）   |
|<font color="Red">**赤**</font>|点滅|GPS状態は不良である。（使用中のGPS衛星が3つ以下）   |
|<font color="Green">**緑**</font>|消灯|GSM通信（3G/GSM）ができていない。   |
|<font color="Green">**緑**</font>|点灯|クラウドのサーバ（TCP/IP）と通信中。   |
|<font color="Green">**緑**</font>|点滅|GSM通信（3G/GSM）ができる状態である。   |

## SIMカードを装着してみよう
BOLERO本体のネジを外してハードケースを開けると、SIMカードを2枚差せることが判る（Dual SIM対応）。
KDDI系IoTベンチャーSORACOM社が、**MVNO**で提供しているデータ通信専用SIM（**SORACOM Air SIM**）を装着した。**SORACOM Air SIM** はAmazonで1枚から購入可能だ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/96a662f1-1ab3-61a3-c354-762eb2cb9381.png)
無論、SORACOM認定デバイスのひとつである。
https://soracom.jp/support_partners/certified_device/nexty-bolero-45/

## パソコンと接続してみよう
制御ボックスを、USBシリアル変換アダプタを介してパソコンと接続する。
パソコンで操作するときのみ、BOLEROを制御ボックスと接続する。
![名称未設定2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/1e22bf5f-fd09-186d-f421-5c59218507d7.png)
パソコンでJava製の専用ソフトを起動し、シリアルポートを指定すると、コンフィグレーションの書き込みや専用スクリプトの動作確認ができる画面になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/611a132c-7ba1-7f6d-20bf-5907bb664d9c.png)

## センサーと接続してみよう
可変型直流安定化電源と光電センサを下図の通り接続してみた。
可変型電源は、アナログ測定した電圧値（**IO1**）がサーバに送信されるか確認するために用意。
光電センサは、デジタル測定した物体検出のオンオフ変化（**IO2**）がサーバに送信されるか確認するために用意した。
このときパソコンと接続していれば、シリアル入力でもセンサーの状態を確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/a53c5733-da93-53f7-d20b-50f9610fb3ce.png)
![bolero.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/d6e66fcf-80e3-b624-0b6e-7d2c6d9a4606.jpeg)

## コンフィグレーションを書いてみよう
BOLEROはノンプログラミングで、**センサーのデータ**と**GPS位置情報（NMEA 0183フォーマット）**をサーバに送信できる。
そのための設定情報（コンフィグレーション）を一部だが以下に抜粋する。
BOLEROのコンフィグレーションとはコマンドの羅列であり、先頭から順番に1行ずつ実行される。
イベントの概念を持ち、「もし～なら～を実行する」という`If`文のような定義もできる。
ステータス表示用のLED（緑と赤）もプログラマブルに制御可能だ。

```c:コンフィグレーション例
$PFAL,CNF.Set,DEVICE.BAT.CHARGEMODE=eco  // 内蔵バッテリの充電タイミング
$PFAL,CNF.Set,DEVICE.GPS.AUTOCORRECT=off,5.0,58,50,10,50  // GNSSの自動補正(位置情報算出のためのパラメータを設定し、指定した条件を満たさないとGNSSは有効としない)
$PFAL,CNF.Set,DEVICE.GPS.CFG=3  // 位置情報を有効とする衛星捕捉数を設定
$PFAL,CNF.Set,TCP.CLIENT.SENDMODE=2,1000  // データを不揮発性メモリに保存した後、TCP送信する
$PFAL,CNF.Set,TCP.CLIENT.TIMEOUT=300000,30000  // GNSSトラッカーの応答時間、TCP接続が失敗した時の次の接続までの時間を設定
$PFAL,CNF.Set,GPRS.AUTOSTART=1  // ネットワークの準備ができたら自動で接続/再接続する
$PFAL,CNF.Set,GPRS.TIMEOUT=1,600000  // 600秒間、通信が無ければネットワークを切断(消費電力を抑えるため)
$PFAL,GSM.Band=auto  // 使用するGSM帯域を自動設定にする

// NMEA 0183 メッセージの出力可否（1=出力、0=出力しない）
$PFAL,CNF.Set,PROT.GGA=1
$PFAL,CNF.Set,PROT.GLL=1
$PFAL,CNF.Set,PROT.GSA=1
$PFAL,CNF.Set,PROT.GSM=1
$PFAL,CNF.Set,PROT.GSV=1
$PFAL,CNF.Set,PROT.RMC=1
$PFAL,CNF.Set,PROT.VTG=0

// 緑LED（3G/LTEの状態）のイベント制御
$PFAL,CNF.Set,AL21=GSM.eOpfound:IO12.Set=cyclic,200,200  // GSM事業者を見つけたら
$PFAL,CNF.Set,AL26=GSM.eOplost:IO12.Set=low  // GSM事業者を見失ったら
$PFAL,CNF.Set,AL22=GSM.GPRS.eConnected:IO12.Set=cyclic,400,400  // GPRSネットワークに接続したら
$PFAL,CNF.Set,AL25=GSM.GPRS.eDisconnected&GSM.sOpValid:IO12.Set=cyclic,200,200  // GPRSネットワークと切断したら
$PFAL,CNF.Set,AL23=TCP.Client.eConnected:IO12.Set=high  // サーバと接続したら
$PFAL,CNF.Set,AL24=TCP.Client.eDisconnected&GSM.sOpValid:IO12.Set=cyclic,400,400  // サーバと切断したら

// 赤LED（GPSの状態）のイベント制御
$PFAL,CNF.Set,AL27=Sys.Device.eStart&GPS.Nav.sFix=valid:IO13.Set=high  // 4つ以上のGPS衛星が捕捉できたら
$PFAL,CNF.Set,AL18=Sys.device.eStart&GPS.Nav.sFix=invalid:IO13.Set=cyclic,200,200  // 使用中のGPS衛星が3つ以下になったら
$PFAL,CNF.Set,AL19=GPS.Nav.eFix=valid:IO13.Set=high  // 4つ以上のGPS衛星が捕捉できたら
$PFAL,CNF.Set,AL20=GPS.Nav.eFix=invalid:IO13.Set=cyclic,200,200  // 使用中のGPS衛星が3つ以下になったら

$PFAL,CNF.Set,DEVICE.PFAL.SEND.FORMAT="$",NOCKSUM,"","$<end>"  // メッセージフォーマット

// リモートサーバのIPアドレスおよびポートを指定(アカウントを取得すればマエストロ社のクラウドサービス trace4you に送信して可視化できる)
$PFAL,CNF.Set,TCP.CLIENT.CONNECT=1,12.34.56.78,8888
$PFAL,CNF.Set,TCP.CLIENT.ALTERNATIVE=1,12.34.56.78,8888

// APN(Access Point Name)の設定 ... SORACOM Air の場合
$PFAL,CNF.Set,GPRS.APN=soracom.io
$PFAL,CNF.Set,PPP.PASSWORD=sora
$PFAL,CNF.Set,PPP.USERNAME=sora

///////////////////////////
//  センサーのイベント制御
///////////////////////////

// IO1 に接続した電圧値を30秒ごとにテキストでサーバへ送信する
$PFAL,CNF.Set,AL30=SYS.Device.eStart:SYS.Timer6.Start=cyclic,30000
$PFAL,CNF.Set,AL31=SYS.Timer.e6:TCP.Client.Send,8,"IO1 &(ANA1) Voltage"&TCP.Client.FlushSendBuffer

// IO2 に接続した光電センサーのON/OFFが変化したらテキストでサーバに送信する
$PFAL,IO2.Config=DI  // OnとOffのデジタル入力とする
$PFAL,CNF.Set,AL32=IO.e2=Redge:TCP.Client.Send,8,"IO2 Rising edge"&TCP.Client.FlushSendBuffer  // IO2 が Low から High に変わったら
$PFAL,CNF.Set,AL33=IO.e2=Fedge:TCP.Client.Send,8,"IO2 Falling edge"&TCP.Client.FlushSendBuffer  // IO2 が High から Low に変わったら

// 後処理
$PFAL,CNF.Backup  // 現在のユーザ設定をバックアップ
$PFAL,Sys.Device.Reset  // 5秒後に再起動
```

## サーバに送信してみよう
サーバとの接続は、ただの**TCPソケット**なので、次のような簡単なPythonプログラムを数行書くだけでデータを受信できる。

```python:tcp_server.py
import socket
from contextlib import closing

def main():
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  with closing(sock):
    sock.bind(('0.0.0.0', 8888))
    sock.listen(10)
    while True:
      conn, addr = sock.accept()
      with closing(conn):
        raw_data = conn.recv(1024)
        print(raw_data.decode())
  return

if __name__ == '__main__':
  main()
```

ただし、このプログラムはソケット通信の多重化まで考慮していない。`accept`が処理をブロックする間、他のクライアントと通信できないのだ。
シングルプロセスかつシングルスレッドのままで多重化したいなら`select`もしくは`selectors`モジュールを使う。`select`モジュールの使い方は@homines22さんの記事[【selectモジュールによるポーリング】](https://qiita.com/homines22/items/a161ac652d9585d7273a)が詳しいので参照して欲しい。

```:実行結果
$ python3 -V
Python 3.6.8

$ python3 tcp_server.py
$IO1 12.483 Voltage
$GPRMC,000057.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
$IO2 Rising edge
$GPRMC,000058.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
$IO2 Falling edge
$GPRMC,000102.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
$IO1 12.682 Voltage
$GPRMC,000107.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
$IO2 Rising edge
$GPRMC,000109.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
$IO1 13.659 Voltage
$GPRMC,000117.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
$IO1 15.448 Voltage
$GPRMC,000127.000,V,0000.0000,N,00000.0000,E,,,060180,,
$<end>
```

送信に失敗したデータはBOLEROの内蔵メモリに蓄えられ、次回接続成功時に再送してくれる。

以上、駆け足で説明したがイメージが伝われば幸いである。