🌏

GPSマルチユニットのデータをAWS IoT CoreからAmazon Location Serviceにルーティングしてみた

2022/11/22に公開

この記事は

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枠で発表しました。以下はその時の資料です。

https://speakerdeck.com/kenichirokimura/jaws-ug-fukuoka-20221120-up

LTではmqttクライアントであるmosquitto_pubを使ってIoT Coreにサンプルのデータを送信し、それがLocation ActionでAmazon Location Serviceにルーティングされるところまでを紹介しました。
その後、GPSマルチユニットのデータを使ってジオフェンシングも動いたので本記事にそれをまとめました。

デバイス接続の準備

久しぶりにIoT Coreのコンソール見たらデバイス接続の準備がずいぶん簡単になってました。

以下の図にある「1個のデバイスを接続」を押し、画面の指示通りに進めていくと、デバイスを作った上で接続キット(SDKや証明書一式が含まれたZIPファイル)をダウンロードできます。

1個のデバイスを接続

ただし、これで作成されたポリシーは以下の通り接続試験用のトピックへの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マルチユニットからデータを送信してみて、以下のようにデータが届いていることを確認します。

MQTTテストクライアント

そして、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を見て、ENTEREXITかを判定して、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に送信するルールが作成されているので、それを選択します。

EvendBridgeのルール一覧

「ターゲット」→「編集」を押し、作成したLambda関数を設定します。

ルールの設定

送ってみる

実際にGPSマルチユニットからデータを送ってみて、LINE Notifyで通知が来るか確認してみます。

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ごとに判定されてるということが良く理解できました。

GitHubで編集を提案

Discussion