GPSマルチユニットのデータをAWS IoT CoreからAmazon Location Serviceにルーティングしてみた
この記事は
2022/10/31に発表された、AWS IoT CoreからAmazon Location ServiceにルーティングするLocation Actionを実際に触ってみたメモです。
なお、画面イメージは2022/11/22現在のものなので、AWSコンソールなどのUIは変更されている可能性があります。cliでやるといいんですけど、その辺は今後の課題ということで・・。
経緯
2022/11/20に開催されたJAWS-UG 福岡 #13:10度目はちょっと濃い目にJAWS DAYS 2022を振り返り、re:Inventに備えようで、Location ActionについてLT枠で発表しました。以下はその時の資料です。
LTではmqttクライアントであるmosquitto_pubを使ってIoT Coreにサンプルのデータを送信し、それがLocation ActionでAmazon Location Serviceにルーティングされるところまでを紹介しました。
その後、GPSマルチユニットのデータを使ってジオフェンシングも動いたので本記事にそれをまとめました。
デバイス接続の準備
久しぶりにIoT Coreのコンソール見たらデバイス接続の準備がずいぶん簡単になってました。
以下の図にある「1個のデバイスを接続」を押し、画面の指示通りに進めていくと、デバイスを作った上で接続キット(SDKや証明書一式が含まれたZIPファイル)をダウンロードできます。
ただし、これで作成されたポリシーは以下の通り接続試験用のトピックへのpub/subなどしか許可されていませんので、適宜ポリシーを変更します。
GPSマルチユニットの設定
GPSマルチユニットは、クラウドリソースアダプター SORACOM Funnelを使ってIoT Coreに接続します。
公式ドキュメントに従ってIAMロールを作成し、SORACOMコンソールから「SIMグループ」に進み、GPSマルチユニットで使っているSIMが所属するSIMグループの設定を変更します。
今回、publish先のトピックはlocation/topic/#{imsi}
としました。
Funnelの設定が完了したら、AWS IoT Coreのコンソールで、データが届いてるかを確認します。
テスト→MQTTテストクライアントから、「トピックをサブスクライブする」でlocation/topic/+
を指定し、サブスクライブします。
GPSマルチユニットからデータを送信してみて、以下のようにデータが届いていることを確認します。
そして、GPSマルチユニットから届くデータは以下のようになってることが分かります。
{
"credentialsId": "funnel",
"operatorId": "**************",
"destination": {
"provider": "aws",
"service": "aws-iot",
"resourceUrl": "**************.iot.ap-northeast-1.amazonaws.com/location/topic/#{imsi}",
"payloadsOnly": false,
"sendPayloadsAsBinary": false
},
"sourceProtocol": "udp",
"payloads": {
"lat": 33.xxxxxx,
"lon": 130.xxxxxx,
"bat": 3,
"rs": 4,
"temp": 18.9,
"humi": 71.3,
"x": 0,
"y": 128,
"z": -1024,
"type": 1
},
"timestamp": xxxxxxxxxxxx,
"imsi": "xxxxxxxxxxxxxxxxxxxx",
"imei": "xxxxxxxxxxxxxxxxxxxx",
"simId": "xxxxxxxxxxxxxxxxxxx"
}
ルーティングの設定
続いてIoT Coreのルーティングを設定します。
SQLステートメントはSELECT * FROM 'location/topic/+'
とします。ルールアクションは「場所」を選びます。
Amazon Location Serviceのトラッカーは既存のものを使うこともできますし、この画面から作成することもできます。 今回はここから「MyTracker」というトラッカーを作成しました。
Location Serviceに送信するデータは、先ほどのJSONを見ながら埋めていきます。
- デバイスID: デバイスIDにsimIDを使うようにしてみます。
${simId}
と入力します。 - 経度:
${payloads.lon}
- 緯度:
${payloads.lat}
- タイムスタンプ:
${timestamp}
。単位はMILLISECONDSです。 - IAMロール: 新規に作成します。
IAMロールはデータを送るトラッカーに対するgeo:BatchUpdateDevicePosition
権限が必要なので、以下のような内容になります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "geo:BatchUpdateDevicePosition",
"Resource": "arn:aws:geo:ap-northeast-1:*************:tracker/MyTracker"
}
]
}
ジオフェンシングの設定
公式ドキュメントに従って設定していきます。今回はドキュメントに記載の通り、geojson.ioを使って会社の周囲を四角形で囲ったものを作り、それを使うようにしました。
ジオフェンスコレクションを作成する際、EventBridgeルールも作るようにします。
あとはトラッカーにこのジオフェンスコレクションをリンクしたら設定完了です。
Lambda関数の準備
EventBridgeから呼び出されるLambda関数を作成します。受け取ったJSONを見て、ENTER
かEXIT
かを判定して、LINE Notifyに通知するだけのものです。
詳細な手順は割愛しますが、以下のコードはHTTPリクエストを送信するのにrequests
を使ってますので、ローカル開発環境でpip install -t ./ requests
でインストールしてZIPでアップロードするか、requests
が含まれるLambda Layerを利用してください。
# -*- coding: utf-8 -*-
import json
import requests
import os
URL = "https://notify-api.line.me/api/notify";
def lambda_handler(event, context):
deviceid = event['detail']['DeviceId'];
line_token = os.environ['LINE_TOKEN']
headers = {"Content-Type": "application/x-www-form-urlencoded","Authorization": "Bearer %s" % line_token};
lon = event['detail']['Position'][0];
lat = event['detail']['Position'][1];
eventType = event['detail']['EventType'];
message = "退社"
if eventType == "ENTER":
message = "出社"
message = "DeviceId %sが%sしました。(%s,%s)" % (deviceid,message,lon,lat);
data = {"message": message.encode("utf-8")}
x=requests.post(URL, headers=headers, data=data);
print(event);
print(x.text);
return {
'statusCode': 200,
'body': json.dumps('OK')
}
LINE Notifyからトークンを取得して、Lambda関数の環境変数にLINE_TOKEN
という名前で設定しておきます。
なお、EventBridgeから受け取るJSONは以下のような形になりますので、ソースコードと見比べてみてください。
{
'version': '0',
'id': '******-****-****-****-****************',
'detail-type': 'Location Geofence Event',
'source': 'aws.geo',
'account': '***********',
'time': '2022-11-21T13:45:52Z',
'region': 'ap-northeast-1',
'resources': ['arn:aws:geo:ap-northeast-1:************:geofence-collection/xxxxxxx',
'arn:aws:geo:ap-northeast-1:***********:tracker/MyTracker'],
'detail':
{
'EventType': 'EXIT',
'GeofenceId': 'monitoring-*******-****-****-****-************',
'DeviceId': '*****',
'SampleTime': '2022-11-21T13:45:51.765Z',
'Position': [130.******, 33.*****]
}
}
EventBridgeのルールの設定
Lambda関数ができたら、EventBridgeのルールを設定します。ジオフェンスコレクションの作成時に、CloudWatchに送信するルールが作成されているので、それを選択します。
「ターゲット」→「編集」を押し、作成したLambda関数を設定します。
送ってみる
実際にGPSマルチユニットからデータを送ってみて、LINE Notifyで通知が来るか確認してみます。
届いてますね!
位置情報をそのまま文字として送らずにgoogle mapsへのURLにしたり、LINE NotifyではなくMessageing APIで位置情報として送ったりすると地図も表示できてより良さそうです。
まとめ
今回は発表されたばかりのLocation Actionを使って、AWS IoT CoreからAmazon Location Serviceに接続してみました。そして、実例としてGPSマルチユニットSORACOM Editionから位置情報をSORACOM Funnelを使ってAWS IoT Coreに送信し、Amazon Location Serviceのジオフェンシングを使って出社・退社を判定してLINE Notifyで通知するというものを作ってみました。
これまでLocation Serviceにデータを送るときにはLambdaを使うケースが多かったかと思いますが、Location Actionを使うとAWS IoT Coreから直接送り込むことができるようになってアーキテクチャがシンプルになります。
また、Locaiton Serviceのジオフェンシングを初めて使いましたが、思った以上に簡単に使えてビックリしました。今回はジオフェンシングだけを使いましたが、今後はLocation Serviceの他の機能も使ってみたいと思います。
余談
最初デバッグのために、mosquitte_pubでデータを送ることで「GPSマルチユニットの位置を強制的に一旦フェンスの外に出す」ということをしたのですが、なぜかGPSマルチユニットからのデータでENTERイベントが発火しないので頭をひねりました。
よく見たらGPSマルチユニットから送られるデータではデバイスIDはsimIDになっており、mosquitte_pubから送るときには手打ちで入れるのが面倒なのでtest
というデバイスIDで送っていました。そうするとフェンシング内では別のデバイスだと思われてるので、「GPSマルチユニットはずっと移動していない」と判定されていてイベントが発火しなかったというオチでした・・・。
同じトラッカー内でもデバイスIDごとに判定されてるということが良く理解できました。
Discussion