8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SORACOMAdvent Calendar 2022

Day 12

AWS IoT Core Device Location を使って SORACOM Air for セルラー(LTE) を利用しているデバイスの位置を取得して AWS IoT の Location Action で Amazon Location Service に送信する

Last updated at Posted at 2022-12-12

概要

※ こちらは 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 に送信してみたいと思います。

構成

Screenshot 2022-12-10 at 19.12.59.png

手順

まずデバイスを用意します。今回は以下を用意しました。

  • 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 にインストールします。
Raspberry Pi OS – Raspberry Pi 2022-12-06 14-17-04.png
microSD カードを PC に接続し OS を書き込んでいきます。
OS は Raspberry Pi OS (64-bit) を選択しました。
Raspberry Pi Imager v1.7.3 2022-12-06 14-34-13.png
Wi-Fi の設定、SSH の設定を入れておきます。その他、必要な設定を入力したら WRITE します。
Raspberry Pi Imager v1.7.3 2022-12-06 14-23-56.png
Write→Verify が完了するまで待ちます。
Raspberry Pi Imager v1.7.3 2022-12-06 14-33-50.png
Verify が完了したら microSD カードを PCから外して Raspberry Pi の microSD カードスロットに挿して電源に接続します。
raspi.jpg
※ ちなみに金属製のアーマーケースを装着しています。冷却効果はわかりませんが満足度は上がりました。
Raspberry Pi Imager で Wi-Fi と SSH の設定を入れておいたので、VSCodeから SSH で接続してみます。
VSCodeRemote Development Extensions で Raspberry Piに接続します。
pi [SSH: raspi4b] 2022-12-06 14-56-46.png
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 に挿します。
onyx.jpg

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 の管理画面に遷移すると、左側メニューの TestDevice Location が追加されています。
AWS IoT 2022-12-07 14-17-05.png
AWS IoT - Test - Device location 2022-12-07 14-19-05.png
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 してみます。
AWS IoT - Test - Device location 2022-12-07 14-44-49.png
コピー & ペーストして Resolve をクリック。
AWS IoT - Test - Device location 2022-12-07 14-44-04.png

結果は GeoJSON 形式で返ってきます

無事 Resolve されました。coordinates経度, 緯度 が返ってきます。Google マップで確認してみます。確認する際は 緯度, 経度 のように入れ替えて検索します。
34°44'28.5"N 135°31'22.3"E - Google Maps 2022-12-08 10-15-52.png
おぉ、近からず遠からず...。ということでなんとなく AWS IoT Code Device Location の使い方がわかりました。

4. Raspberry Pi から SORACOM Beam を使って、 AWS IoT Core にデータを送信する

手動での確認が出来たので、ここからはデバイスとクラウドを繋いでいきます。SORACOM のドキュメント AWS IoT と接続する を参考に Things(モノ) を作成します。
AWS IoT - Manage - Things 2022-12-07 15-08-58.png
出来ました。作成時に表示される各種証明書については全てダウンロードしておきます。
AWS IoT - Manage - Things - takuya-raspi 2022-12-10 17-28-19.png

証明書にポリシーもアタッチしました。ポリシーでは iot:Publishiot:Connect を許可しています。
AWS IoT - Security - Certificates - 851057952c2b953277a4bc17f96b763f962b6c04dcb60cb65d0a49d7c703630a 2022-12-07 15-10-28.png
デバイスデータエンドポイントを確認しておきます。
AWS IoT - Settings 2022-12-07 15-18-06.png
次に SORACOM Beam の有効化を行います。今回は MQTT エントリポイントを追加します。
Groups - SORACOM User Console 2022-12-10 13-32-51.png
HOST NAME には AWS IoT Core のデバイスデータエンドポイント、PORT NUMBERには 8883 を入力します。
Groups - SORACOM User Console 2022-12-10 13-51-47.png
AWS IoT Core へは Things を作成した時にダウンロードしたクライアント証明書を利用して認証します。CLIENT CERT を設定し Save します。
Groups - SORACOM User Console 2022-12-10 13-56-02.png
SORACOM Beam の設定が完了したので、こちらを参考にデバイスから AWS IoT Core に MQTT でデータを送信してみます。まず AWS IoT Core の MQTT Test Client の Subscribe を行います。Topic Filter に任意の文字列を設定し Subscribe をクリックします。
AWS IoT - MQTT test client 2022-12-07 15-37-03.png
次に 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 を見てみるとメッセージが届いていることが確認できます。
AWS IoT - MQTT test client 2022-12-10 17-06-51.png

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 に以下のように記載します。

credentials
[default]
aws_access_key_id = Axxxxxxx
aws_secret_access_key = Qxxxxxx

Python のコードは以下です。

send_location.py
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 

簡単にコードを説明すると

  1. メタデータから sessionStatus.cell を取得
  2. AWS SDK for Python(boto3)で get_position_estimate を実行し位置情報(GeoJSON 形式)を取得する
  3. 取得した位置情報(GeoJSON 形式)にdevice_id を追加する
  4. MQTT で Topic: takuya-raspi/location に Publish する
  5. 上記を 60 秒間隔で繰り返す

という内容になっています。停止させる際は Ctrl + c で停止させてください。

AWS IoT Core の MQTT Test Client でデータが届いていることを確認します。
AWS IoT - MQTT test client 2022-12-11 23-41-32.png

5. AWS IoT Rule Actions で Amazon Loction Service にデータを送信する

AWS IoT Core まで位置情報が届いたので、その位置情報を Amazon Location Service に送信します。送信には AWS IoT Rule Actions の Location を使用します。
Rule を新規作成していきます。
AWS IoT - Message Routing - Rules 2022-12-10 17-53-58.png
AWS IoT - Message Routing - Rules - Create rule 2022-12-10 17-57-31.png
SQL Statement は Raspberry Pi から送信(Python Script から送信)している Topic を FROM に指定します。
AWS IoT - Message Routing - Rules - Create rule 2022-12-10 18-00-10.png
Rule actions の Action 1Location を選択し Create a Tracker を選択します。
AWS IoT - Message Routing - Rules - Create rule 2022-12-10 18-03-04.png
Amazon Location Service の画面が開きます。Tracker の Name を入力し、Create tracker を選択します。
Create tracker | Trackers | Amazon Location 2022-12-10 18-05-28.png
元の画面に戻り再読み込みボタンを選択し、作成した Tracker を選択します。Device ID${device_id}Longitude${get(coordinates, 0)}Latitude${get(coordinates, 1)}Timestamp${timestamp()} と入力し、 Create new role を選択します。
AWS IoT - Message Routing - Rules - Create rule 2022-12-11 23-47-27.png

${expression} は置換テンプレートです。AWS IoT Core に送信されたデータに置き換えたり、関数や演算子を使用できます。今回は Longitude/Latitude の取得に get、Timestamp value に timestamp() という関数を使用しています。

Role Name を入力して Create を選択します。
AWS IoT - Message Routing - Rules - Create rule 2022-12-10 18-18-50.png
作成した IAM Role が選択されていること、他の入力項目が正しいことを確認して Next を選択します。
AWS IoT - Message Routing - Rules - Create rule 2022-12-12 00-15-14.png
内容を確認して Create を選択します。
AWS IoT - Message Routing - Rules - Create rule 2022-12-12 13-45-14.png
これで、Topic: takuya-raspi/location に Publish されたデータが、Amazon Location Service の Tracker: takuya-tracker に送信されるようになりました。もう一度 Raspberry Pi でデータ送信を実行します。

pi@raspberrypi:~ $ python send_location.py 

何度かデータが送信されたら、以下のコードで Tracker のデータを確認します。

get_device_position_history.py
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)を作る必要があります。こちらもいつか試してみたいと思います。

ではまた。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?