JetsonからMQTTでAWS IoTにデータを送ってみた

# 目次
- はじめに 
- 目的 
- 環境
- 手順
　　1. AWS IoTでモノの登録
　　2. AWS IoT Core エンドポイントの確認
　　3. Jetsonの準備
　　4. JetsonからMQTTでデータが送信されることの確認
　　5. DeepStream Pythonアプリの作成
　　6. DynamoDB テーブルの作成
　　7. AWS IoT ルールの作成

- 動かしてみた
- おわりに
- 参考



# はじめに
これまでJetson、DeepStream SDKを使ってエッジAIアプリの開発をしてきましたが、AWSも勉強したいと思い始めました（AWSのサービスではEC2とS3を少し使ったことがある程度です）。そこで、まずエッジAIアプリと連携して使うことがあるAWS IoT, DynamoDBを使って簡単なアプリを作ることにしました。

# 目的
下図の構成のアプリを作ります。  
Jetsonでは、カメラ映像から人を検出し、画面内の人数をカウントします。人数カウントの結果をMQTTでAWS Iotに送ります。AWS IoTではデータを受け取ったら、人数やタイムスタンプの情報をDynamoDBに登録します。
![スクリーンショット 2024-02-11 12.48.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/2b53d8d6-6cf5-c29f-8c14-3248b284236f.png)


# 環境
- Jetson Nano 4GB
- JetPack 4.6.1
- DeepStream SDK 6.0.1
- deepstream_python_apps v1.1.1

# 手順

こちらのチュートリアルを参考にしました。
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/using-laptop-as-device.html

## 1. AWS IoTでモノの登録
AWSマネジメントコンソールで作業します。  
IoT Coreのページを開きます。
![スクリーンショット 2024-02-10 9.50.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/6e926c4b-94e6-fc4e-0d46-3a618326a98c.png)
  
リージョンを選択します。今回は東京に設定します。  
ナビゲーションペイン（左のウィンドウ）から、「モノ」を選択します。
![スクリーンショット 2024-02-10 9.49.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/f30d7653-ad39-dab5-eba1-1032bed74748.png)

「モノを作成」をクリックします。
![スクリーンショット 2024-02-10 9.49.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/7c73f70f-7f28-c391-234d-4fdc4871611e.png)

「1つのモノを作成」を選択し、「次へ」をクリックします。
![スクリーンショット 2024-02-10 9.49.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/0ce68cb9-48eb-c46c-2b71-37a8878ff4d6.png)

モノの名前を入力します。あとはデフォルトの設定のままで「次へ」をクリックします。
![スクリーンショット 2024-02-10 9.49.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/0c3d7558-cfa5-3f2e-980d-468c7456ee97.png)

「新しい証明書を自動生成（推奨）」を選択し、「次へ」をクリックします。
![スクリーンショット 2024-02-10 9.50.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/3a28cf71-f4ce-c5d0-fb3c-b0aa9826e362.png)

「ポリシーを作成」をクリックします。

![スクリーンショット 2024-02-10 11.26.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/ad8bc017-d2e4-91ff-f2bf-667937fc3e03.png)

ポリシー名を入力し、「JSON」をクリックします。  
「ポリシードキュメント」の欄にJSON形式のポリシーをコピペします。コピーする前に"region"を自分で設定したregionに、 "account"を自分のAWSアカウントに置き換えてください。  
ここでは、すべてのクライアントと"/test/topic"というtopicをやりとりできるように設定しています。

![スクリーンショット 2024-02-10 11.29.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/1133752e-015b-8f7c-91e8-c88ea13feb23.png)

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Publish",
                "iot:Receive"
            ],
            "Resource": [
                "arn:aws:iot:region:account:topic/test/topic"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Subscribe"
            ],
            "Resource": [
                "arn:aws:iot:region:account:topicfilter/test/topic"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iot:Connect"
            ],
            "Resource": [
                "arn:aws:iot:region:account:client/*"
            ]
        }
    ]
}
```

モノの作成ページに戻り、先ほど作成したポリシーを選択し、「モノを作成」をクリックします。
![スクリーンショット 2024-02-10 12.55.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/b5f4ab9b-3668-ec6c-8ebe-b8b8605e556a.png)


最後に証明書のダウンロード画面が出ますので、証明書をダウンロードします。

## 2. AWS IoT Core エンドポイントの確認
AWSマネジメントコンソールで作業します。  
AWS IoT Coreのページを開きます。  
ナビゲーションペインから、「設定」を選択します。
![スクリーンショット 2024-02-10 13.44.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/38ae8a47-65ee-bb84-1a58-1df0a7757ea2.png)

エンドポイントをコピーしてテキストファイルなどに保存してください。
![スクリーンショット 2024-02-10 13.46.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/10ba6f6d-1001-7186-154f-b248af6e0ed4.png)

## 3. Jetsonの準備
Jetsonで作業します。  

python 3.7以降が必要なため、下記のコマンドでシンボリックリンクを作成します。
```
sudo ln -s /usr/bin/python3.6 /usr/bin/python3.7
```

AWS IoT Device SDK for Pythonをインストールします。
```
python3 -m pip install awsiotsdk
```

下記のコマンドでリポジトリをクローンします。
```
cd ~ 
git clone https://github.com/aws/aws-iot-device-sdk-python-v2.git
```
下記のコマンドを実行し、homeディレクトリ下に証明書などを保存するためのディレクトリを作成します。  

```
mkdir ~/certs
```
手順1でダウンロードしたプライベートキー(xxxx-private.pem.key)、デバイス証明書(xxxx-certificate.pem.crt)、ルートCA証明書(AmazonRootCA1.pem)をJetsonのcertsディレクトリにコピーします。手順2でエンドポイントを保存したテキストファイルもJetsonにコピーします。

## 4. JetsonからMQTTでデータが送信されることの確認
Jetsonでサンプルプログラムを実行し、MQTTでデータを送信・受信します。AWS IoTのMQTTテストクライアントを使用してJetsonから送信されたメッセージを確認します。

### 4.1. MQTTテストクライアントの設定
AWSマネジメントコンソールで作業します。  
AWS IoT Coreのページを開き、ナビゲーションペインから、「MQTTテストクライアント」を選択します。

![スクリーンショット 2024-02-10 13.26.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/a7b4349b-ea33-846e-2e9d-828a55dfc5fa.png)

「トピックをサブスクライブする」のタブを選択します。  
ポリシーで設定した「test/topic」を入力し、「サブスクライブ」をクリックします。
![スクリーンショット 2024-02-10 13.26.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/0fc4cfd4-0df7-da38-2b93-a78534674829.png)

Jetsonからデータが送られてくると、こちらのページでデータが表示されます。


### 4.2. Jetson側でサンプルプログラムを実行
Jetsonで作業します。  

下記コマンドでサンプルプログラムがあるディレクトリに移動します。
```
cd ~/aws-iot-device-sdk-python-v2/samples
```
pubsub.pyを実行します。エンドポイント、プライベートキー、デバイス証明書、ルートCA証明書のファイルパスを設定する必要があります。
```
python3 pubsub.py --endpoint your-iot-endpoint --ca_file ~/certs/AmazonRootCA1.pem --cert ~/certs/xxxx-certificate.pem.crt --key ~/certs/xxxx-private.pem.key
```

AWSマネジメントコンソールでMQTTテストクライアントのページを開き、データが送信されていることを確認します。  
"Hello World! [10]"のようなメッセージが送信されています。
![スクリーンショット 2024-02-10 14.20.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/09750b1f-a894-dd32-23bd-0dd322e322b5.png)

## 5. DeepStream Pythonアプリの作成
先ほどのサンプルプログラムpubsub.pyと、deepstream_python_appsの[deepstream-test1-usbcam](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/tree/v1.1.1/apps/deepstream-test1-usbcam)アプリを参考にして、x秒間隔でカメラに映っている人の人数をMQTTで送信するアプリを作成しました。  
コードはこちらにあります。  
https://github.com/mihara-shoko/deepstream-python-aws-iot/tree/main

- 送信されるデータ
json形式でデータが送信されます。
```
{
    "time": "2024-02-10 18:03:03.620019",
    "device_id": "test-device",
    "person": 2
}
```

| 項目 | 単位 | 説明 |  
| ---- | ---- | ---- |
| time | - | データ送信時刻 |
| device_id | - | デバイスID |
| person | 人 | カメラに映っている人数。|

- 設定できるオプション
config/config.iniでオプションを設定できます。
endpoint, cert_filepath, pri_key_filepath, ca_filepathは設定必須となっています。

| 項目 | 単位 | 説明 | デフォルト値 | 
| ---- | ---- | ---- | ---- |
| endpoint | - | AWS IoT Core エンドポイント | - (設定必須) |
| cert_filepath | - | デバイス証明書のファイルパス（絶対パス） | - (設定必須) |
| pri_key_filepath | - | プライベートキーのファイルパス（絶対パス）| - (設定必須) |
| ca_filepath | - | ルート CA 証明書のファイルパス（絶対パス） | - (設定必須) |
| device_id | - | デバイスID | test_jetson |
| send_interval_secs | 秒 | データ送信間隔 | 10 |
| deveice | - | カメラのパス | /dev/video0 |
| port | - | MQTT ブローカーに接続するポート番号 | 8883 |
| client_id | - | クライアントID | test-123 |
| keep_alive_secs | 秒 | keep alive値 | 30 |
| message_topic | - |　トピック　| test/topic |

deepstream-test1-usbcam-modifiedアプリを/opt/nvidia/deepstream/deepstream-6.0/sources/deepstream_python_apps/apps下に保存します。  
下記コマンドを実行してアプリを動かします。
```
python3 deepstream_test_1_usb.py
```

MQTTテストクライアントを使って、test/topicをサブスクライブし、データが送信できていることを確認します。

## 6. DynamoDB テーブルの作成
AWSマネジメントコンソールで作業します。  
DynamoDBのページを開きます。
![スクリーンショット 2024-02-10 14.37.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/e4c41464-92d3-c4f1-43c0-86cc7d2b84eb.png)

ナビゲーションペインから「テーブル」を選択します。
![スクリーンショット 2024-02-10 14.37.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/35659373-dc60-6f9e-83c2-d842bce1999b.png)

リージョンは、AWS IoTでモノを作成した時のリージョンと同じに設定してください。  
「テーブルの作成」をクリックします。
![スクリーンショット 2024-02-10 14.37.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/68ff8f43-d2cd-ba51-b321-f41d2928f996.png)

任意のテーブル名とパーティションキーを入力し、「テーブルの作成」をクリックします。　　
今回は、"time"をパーティションキーとしました。
![スクリーンショット 2024-02-10 14.54.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/a25cc844-10c9-93ae-9eca-c3adac4c2df6.png)


作成したテーブルの状態が"アクティブ"になるまで待ちます。
![スクリーンショット 2024-02-10 14.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/ac11876f-ec8e-9072-457f-e0f3f035e902.png)


## 7. AWS IoT ルールの作成
データが送られてきたらDynamoDBに登録するようにルールを作成します。  
AWSマネジメントコンソールで作業します。  
IoT Coreのページを開き、ナビゲーションペインから「メッセージのルーティング」を選択します。
![スクリーンショット 2024-02-10 16.07.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/4ddc1d7c-43aa-736a-7bf9-7d72d49b875f.png)

「ルールを作成」をクリックします。
![スクリーンショット 2024-02-10 16.07.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/bf925b2e-fee0-33ff-e16f-b0afa39c60da.png)

ルール名を入力します。
![スクリーンショット 2024-02-10 16.08.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/45c1388f-6b87-f195-5a2a-6809e234ccbc.png)

SQLでDynamoDBにデータをルーティングする条件を記入します。下記では、"test/topic"というtopicで送られたデータの中からtime, device_id, personというkeyを選択してDBに登録するように記載しています。
```
SELECT time, device_id, person FROM 'test/topic'
```
![スクリーンショット 2024-02-10 16.08.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/cb6b9c78-13bb-76dc-8cee-35de771c4502.png)

アクションのプルダウンから、「DynamoDBv2」を選択します。テーブル名のプルダウンからは、先ほど作成したテーブルを選択します。DynamoDBv2を選択すると、送信されたデータをテーブルの複数のカラムに保存することができます。

![スクリーンショット 2024-02-10 16.17.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/51b1b955-3bea-b91e-da99-cd87e1dd7f5b.png)

IAM ロールの項では「新しいロールを作成」をクリックします。

任意の名前を入力し、ロールを作成します。作成したロールをクリックします。  
「許可を追加」をクリックし、さらに「インラインポリシーを作成」をクリックします。
![スクリーンショット 2024-02-10 16.09.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/fa0a76a3-b9b6-2f48-26bf-c22a9cf34e87.png)

下記のJSONのようなポリシーを作成し、DynamoDBにデータをPutできるようにします。region, account, table_nameをそれぞれ置き換えてください。

```
{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "dynamodb:PutItem",
        "Resource": "arn:aws:dynamodb:region:account:table/table_name"
    }
}
```

メッセージのルーティング画面に戻り、作成した IAMポリシーを選択し、「次へ」をクリックします。　  
確認画面に遷移します。内容に問題がなければ「作成」をクリックします。
![スクリーンショット 2024-02-10 15.09.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/69a32050-ce8e-9c27-eeb7-2d8c25f091bd.png)
  
  
MQTTテストクライアントからデータを送信し、DynamoDBに登録されるかを確認します。  
  
AWS IoTのナビゲーションペインから「MQTTテストクライアント」を選択します。 
「トピックに公開する」タブを選択します。 
トピック名とメッセージペイロードを入力し、「発行」をクリックします。
![スクリーンショット 2024-02-10 16.38.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/43aa33da-a189-a742-1b1b-193523d5d8d8.png)

DynamoDBのページを開き、作成したテーブルをクリックします。

![スクリーンショット 2024-02-10 16.39.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/1db18851-79d5-3c83-32a0-c92ff1b0291a.png)

「テーブルアイテムの探索」をクリックします。
![スクリーンショット 2024-02-10 16.39.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/f8c9d721-3d94-fbfa-63c1-bd6e7443a1d2.png)

「返された項目」の箇所に、先ほど発行したデータが入っていることを確認します。
![スクリーンショット 2024-02-10 16.39.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/551f660d-bbdf-b0ed-66a4-40fda9c247df.png)


# 動かしてみた
カメラで人が映っている動画を映し、DBにデータが保存されるかを確認しました。 想定通りに動きました！ 

Jetsonのキャプチャ  
![スクリーンショット 2024-02-11 15.27.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/e7d3cebd-4e4f-b857-30da-faece8da241e.png)


DynamoDB PersonCountTable
![スクリーンショット 2024-02-11 14.55.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319419/b95b757b-c554-b6bb-9193-927ce0e0a976.png)


# おわりに
AWSでは多くのサービスが提供されており、Jetsonからデータを送信してDBに登録するやり方は他にもあります。今回は比較的シンプルな方法で行いました。今後はDBに登録されたデータをQuicksightで可視化したり、閾値以上の人数が検出されたらAmazon SNSで通知したりなども試してみたいです。

# 参考
- https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/using-laptop-as-device.html
- https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-ddb-rule.html
- https://aws.amazon.com/jp/builders-flash/202009/iot-greengrass-family/?awsf.filter-name=*all
- https://www.kccs.co.jp/sigfox/blog/technical/2018/1015/
- https://aws.github.io/aws-iot-device-sdk-python-v2/awsiot/mqtt_connection_builder.html