概要
※ こちらは SORACOM Advent Calendar 2022 12日目の記事です。
2022/11/27 に AWS IoT Core に新機能 Device Location が追加されました(EN/JP)。IoT デバイスが利用しているセルラーネットワーク、Wi-Fi、GNSS、IP アドレスを用いて位置を特定できる機能です。今回は SORACOM Air for セルラーの LTE 回線を利用しているデバイスの位置情報を取得して Amazon Location Service に送信してみたいと思います。
構成
手順
まずデバイスを用意します。今回は以下を用意しました。
- Raspberry Pi 4 Model B rev 1.2 2GB
- SORACOM Onyx LTE USB ドングル
- SORACOM Air plan-D nano
他は以下の環境で進めます。
- MacBook Pro macOS Ventura 13.0.1
- VSCode
- Chrome
1. Raspberry Pi の初期セットアップ
Raspberry Pi の公式サイトから Raspberry Pi Imager をダウンロードして PC にインストールします。
microSD カードを PC に接続し OS を書き込んでいきます。
OS は Raspberry Pi OS (64-bit) を選択しました。
Wi-Fi の設定、SSH の設定を入れておきます。その他、必要な設定を入力したら WRITE します。
Write→Verify が完了するまで待ちます。
Verify が完了したら microSD カードを PCから外して Raspberry Pi の microSD カードスロットに挿して電源に接続します。
※ ちなみに金属製のアーマーケースを装着しています。冷却効果はわかりませんが満足度は上がりました。
Raspberry Pi Imager で Wi-Fi と SSH の設定を入れておいたので、VSCodeから SSH で接続してみます。
VSCode の Remote Development Extensions で Raspberry Piに接続します。
SSH で繋がりました。
OS を新規インストールしたので、とりあえず apt update & upgrade します。
pi@raspberrypi:~ $ sudo apt update
pi@raspberrypi:~ $ sudo apt upgrade
2. SORACOM Onyx LTE USB ドングルの初期セットアップ
こちらを参考に SORACOM Onyx LTE USB ドングル をセットアップします。
SORACOM Onyx LTE USB ドングルを Raspberry Pi に挿します。
VSCode の Terminal で作業していきます。
pi@raspberrypi:~ $ ls /dev/ttyUSB*
/dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3
pi@raspberrypi:~ $ mkdir ONYX
pi@raspberrypi:~ $ cd ONYX/
pi@raspberrypi:~/ONYX $ curl -O https://soracom-files.s3.amazonaws.com/setup_air.sh
pi@raspberrypi:~/ONYX $ sudo bash setup_air.sh
pi@raspberrypi:~/ONYX $ ifconfig ppp0
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1464
inet 10.142.nnn.nnn netmask 255.255.255.255 destination 10.64.64.64
ppp txqueuelen 3 (Point-to-Point Protocol)
RX packets 420 bytes 139308 (136.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 401 bytes 30541 (29.8 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
pi@raspberrypi:~/ONYX $ cd
こちらもすぐに SORACOM プラットフォームに繋がりました。もう少し確認してみます。
pi@raspberrypi:~ $ ping -c 4 pong.soracom.io
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=1 ttl=64 time=39.0 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=2 ttl=64 time=63.5 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=3 ttl=64 time=61.8 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=4 ttl=64 time=58.6 ms
--- pong.soracom.io ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 38.984/55.716/63.474/9.816 ms
pi@raspberrypi:~ $ curl -s http://metadata.soracom.io/v1/subscriber
{"imsi":"4401050..省略}
ここで http://metadata.soracom.io/v1/subscriber
が取得できないときはメタデータサービスを設定してください。
3. AWS IoT Core Device Location を試してみる
Raspberry Pi + SORACOM Onyx LTE USB ドングルでセルラー回線に繋がったので、とりあえず AWS IoT Core Device Location を試してみます。AWS にログインし AWS IoT Core の管理画面に遷移すると、左側メニューの Test
に Device Location
が追加されています。
payload を入力して Resolve
をクリックすると結果が取得できます。payload の詳細については AWS のドキュメント Location solvers and device payload に記載されています。今回は Cellular based solver を使用します。
LTE で required となっている項目は Mcc
,Mnc
,EutranCid
の 3 つです。ちなみにざっくりと説明すると以下のような情報です。
- MCC: 国を表すコード
- MNC: ネットワークを表すコード
- EutranCid: そのエリアの中で基地局に割り当てられた番号
これらの情報を取得するためには、デバイスで AT コマンド(通信モジュールを制御するためのコマンド)を実行する必要があります。ただ、今回は SORACOM Air を利用しているため、メタデータを利用して簡単に取得します。
Mcc
,Mnc
,EutranCid
の 3 つはメタデータの sessionStatus.cell
に含まれています。
pi@raspberrypi:~ $ curl -s http://metadata.soracom.io/v1/subscriber.sessionStatus.cell
{"radioType":"LTE","mcc":nnn,"mnc":nn,"tac":nnnnn,"eci":nnnnnnnnn}
メタデータのクエリ機能を使用して cell
のみを抽出しています。
EutranCid
= eci
です。これを Device Location の payload に合わせて整形します。整形には jq を利用します。tac
も取得できるので optional ですが含めておきます。
pi@raspberrypi:~ $ sudo apt install -y jq
pi@raspberrypi:~ $ curl -s http://metadata.soracom.io/v1/subscriber.sessionStatus.cell | jq '{"CellTowers":{"Lte":[{"Mcc":.mcc,"Mnc":.mnc,"EutranCid":.eci,"Tac":.tac}]}}'
{
"CellTowers": {
"Lte": [
{
"Mcc": nnn,
"Mnc": nn,
"EutranCid": nnnnnnnnn,
"Tac": nnnnn
}
]
}
}
jq の使い方、オプションの詳細は公式サイトを参照ください。
整形された payload を Resolve
してみます。
コピー & ペーストして Resolve
をクリック。
結果は GeoJSON 形式で返ってきます
無事 Resolve されました。coordinates
に 経度, 緯度
が返ってきます。Google マップで確認してみます。確認する際は 緯度, 経度
のように入れ替えて検索します。
おぉ、近からず遠からず...。ということでなんとなく AWS IoT Code Device Location の使い方がわかりました。
4. Raspberry Pi から SORACOM Beam を使って、 AWS IoT Core にデータを送信する
手動での確認が出来たので、ここからはデバイスとクラウドを繋いでいきます。SORACOM のドキュメント AWS IoT と接続する を参考に Things(モノ) を作成します。
出来ました。作成時に表示される各種証明書については全てダウンロードしておきます。
証明書にポリシーもアタッチしました。ポリシーでは iot:Publish
と iot:Connect
を許可しています。
デバイスデータエンドポイントを確認しておきます。
次に SORACOM Beam の有効化を行います。今回は MQTT エントリポイントを追加します。
HOST NAME
には AWS IoT Core のデバイスデータエンドポイント、PORT NUMBER
には 8883 を入力します。
AWS IoT Core へは Things を作成した時にダウンロードしたクライアント証明書を利用して認証します。CLIENT CERT
を設定し Save
します。
SORACOM Beam の設定が完了したので、こちらを参考にデバイスから AWS IoT Core に MQTT でデータを送信してみます。まず AWS IoT Core の MQTT Test Client の Subscribe を行います。Topic Filter
に任意の文字列を設定し Subscribe
をクリックします。
次に VSCode の Terminal(Raspberry Pi) で作業します。MQTT Client として mosquitto を利用します。
pi@raspberrypi:~ $ sudo apt install mosquitto-clients
mosquitto がインストールできたら、とりあえず Publish してみます。-t
には MQTT Test Client で設定した任意の文字列(Topic)を指定します。
pi@raspberrypi:~ $ mosquitto_pub -d -h beam.soracom.io -t beam_test -m '{"Hello": "World"}'
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'beam_test', ... (12 bytes))
Client (null) sending DISCONNECT
MQTT Test Client を見てみるとメッセージが届いていることが確認できます。
5. Raspberry Pi で Location を Resolve して AWS IoT Core に送信する
今回は Raspberry Pi 上で SORACOM Air for セルラーの LTE 回線の情報を元に位置情報を取得し、 AWS IoT Core に送信する形で実装します。VSCode の Terminal(Raspberry Pi) で作業していきます。また Python および AWS SDK for Python(boto3) 、paho を使用します。
Boto3 の Quickstart を参考にインストールします。
pi@raspberrypi:~ $ python -V
Python 3.9.2
pi@raspberrypi:~ $ pip install boto3
pi@raspberrypi:~ $ pip install paho-mqtt
AWS への認証情報を設定します。~/.aws/credentials
に以下のように記載します。
[default]
aws_access_key_id = Axxxxxxx
aws_secret_access_key = Qxxxxxx
Python のコードは以下です。
import time
import requests
import json
import boto3
import datetime
import paho.mqtt.client as mqtt
client_id = 'takuya-raspi'
protocol = mqtt.MQTTv311
host = 'beam.soracom.io'
port = 1883
keepalive = 60
topic = client_id + '/location'
wait = 60
def on_log(client, userdata, level, buf):
print(buf)
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
def main():
mqtt_client = mqtt.Client(protocol=protocol)
mqtt_client.on_log = on_log
mqtt_client.on_connect = on_connect
mqtt_client.connect(host=host, port=port, keepalive=keepalive)
while True:
# Get cellular tower information
res = requests.get(
'http://metadata.soracom.io/v1/subscriber.sessionStatus.cell')
json_data = res.json()
print("cell_data: " + json.dumps(json_data))
# Resolve location
boto3_client = boto3.client('iotwireless')
ts = datetime.datetime.now(
datetime.timezone(datetime.timedelta(hours=9)))
res = boto3_client.get_position_estimate(
CellTowers={
'Lte': [
{
'Mcc': json_data['mcc'],
'Mnc': json_data['mnc'],
'EutranCid': json_data['eci'],
'Tac': json_data['tac']
},
],
},
Timestamp=ts
)
geoJson = json.loads(res['GeoJsonPayload'].read().decode())
geoJson["device_id"] = client_id
print("send_geoJson: " + json.dumps(geoJson))
mqtt_client.publish(topic, json.dumps(geoJson))
time.sleep(wait)
if __name__ == "__main__":
main()
適当な名前で保存し、実行します。
pi@raspberrypi:~ $ vi send_location.py
pi@raspberrypi:~ $ python send_location.py
簡単にコードを説明すると
- メタデータから
sessionStatus.cell
を取得 - AWS SDK for Python(boto3)で
get_position_estimate
を実行し位置情報(GeoJSON 形式)を取得する - 取得した位置情報(GeoJSON 形式)に
device_id
を追加する - MQTT で Topic:
takuya-raspi/location
に Publish する - 上記を 60 秒間隔で繰り返す
という内容になっています。停止させる際は Ctrl + c
で停止させてください。
AWS IoT Core の MQTT Test Client でデータが届いていることを確認します。
5. AWS IoT Rule Actions で Amazon Loction Service にデータを送信する
AWS IoT Core まで位置情報が届いたので、その位置情報を Amazon Location Service に送信します。送信には AWS IoT Rule Actions の Location を使用します。
Rule を新規作成していきます。
SQL Statement は Raspberry Pi から送信(Python Script から送信)している Topic を FROM に指定します。
Rule actions の Action 1
で Location
を選択し Create a Tracker
を選択します。
Amazon Location Service の画面が開きます。Tracker の Name
を入力し、Create tracker
を選択します。
元の画面に戻り再読み込みボタンを選択し、作成した Tracker を選択します。Device ID
に ${device_id}
、Longitude
に ${get(coordinates, 0)}
、Latitude
に ${get(coordinates, 1)}
、Timestamp
に ${timestamp()}
と入力し、 Create new role
を選択します。
${expression} は置換テンプレートです。AWS IoT Core に送信されたデータに置き換えたり、関数や演算子を使用できます。今回は Longitude/Latitude の取得に get、Timestamp value に timestamp() という関数を使用しています。
Role Name を入力して Create
を選択します。
作成した IAM Role が選択されていること、他の入力項目が正しいことを確認して Next
を選択します。
内容を確認して Create
を選択します。
これで、Topic: takuya-raspi/location
に Publish されたデータが、Amazon Location Service の Tracker: takuya-tracker
に送信されるようになりました。もう一度 Raspberry Pi でデータ送信を実行します。
pi@raspberrypi:~ $ python send_location.py
何度かデータが送信されたら、以下のコードで Tracker のデータを確認します。
import json
import pprint
import boto3
client_id = 'takuya-raspi'
tracker_name = 'takuya-tracker'
def main():
client = boto3.client('location')
res = client.get_device_position_history(
DeviceId=client_id,
TrackerName=tracker_name
)
devicePositions = json.dumps(
res['DevicePositions'], default=str)
print(devicePositions)
if __name__ == "__main__":
main()
適当な名前で保存し、実行します。
pi@raspberrypi:~ $ vi get_device_position_history.py
pi@raspberrypi:~ $ python get_device_position_history.py
[{"DeviceId": "takuya-raspi", "Position": [135.52nnnnnnnnnnnn, 34.74nnnnnnnnnnnn], "ReceivedTime": "2022-12-10 09:26:13.905000+00:00", "SampleTime": "2022-12-10 09:26:12.729000+00:00"}, {"DeviceId": "takuya-raspi", "Position": [135.52nnnnnnnnnnnn, 34.74nnnnnnnnnnnn], "ReceivedTime": "2022-12-10 09:27:15.022000+00:00", "SampleTime": "2022-12-10 09:27:14.085000+00:00"}]
Tracker にデータが格納されていることが確認できました。
あとがき
LTE 回線を利用しているデバイスの位置情報の取得が簡単にできました。今回は Raspberry Pi 上で位置情報を取得(get_position_estimate
)しましたが、デバイス側を省力化する場合は AWS IoT Core にデータを送信した先で位置情報を取得する形がいいと思います。例えば、
-
takuya-raspi-location-cellular
に Publish → Rule Action で Lambda を呼び出す → Lambda で位置情報を取得しtakura-raspi-location
に Republish する -
takuya-raspi-location
の SQL Statement で Lambda を呼び出す
のような形が考えられます。
ちなみに、SORACOM Air for セルラーでは基地局の位置情報を取得できる API が無料で提供されています。AWS IoT Core Device Location は Semtech 、 HERE 、MaxMind など AWS パートナーが提供するソリューションと統合されていますが、SORACOM の位置情報取得 API は Mozilla Location Service のデータを利用しています。また戻り値が GeoJson 形式ではないので、利用する際にはデータの送信前に加工する、 Location Action の置換テンプレートを変更する、といった対応が必要になります。
あと、置換テンプレートが便利でした。GeoJson 形式では {"Position": [135.52nnnnnnnnnnnn, 34.74nnnnnnnnnnnn]}
のように Longitude / Latitude を配列で取り扱いますが、配列そのままで値を取得することができます。置換テンプレート(というか AWS IoT の SQL Statement)がないと、GeoJson 形式を JSON(KEY/VALUE) に変換して送信する、ということが必要になります。Rule をうまく使えば、Longitude / Latitude で受信したデータと GeoJson 形式で受信したデータの両方を Amazon Location Service に送信することも簡単にできます。今回は Raspberry Pi を使用したので送信するデータの加工は比較的容易ですが、送信するデータフォーマットが固定されているデバイスでも置換テンプレート(と SORACOM Orbit )を使えばなんとかできそうな気がします。
Amazon Location Service での可視化はアプリ(Web アプリ、Android)を作る必要があります。こちらもいつか試してみたいと思います。
ではまた。