【AWS IoT】AWS IoT初心者がAWS IoTを100時間触って「つなぎ」の部分をわかった範囲でまとめてみた

#はじめに

エッジデバイスとAWSサービス（IoT CoreまたはS3）間でとりあえず通信ができるところまでの手順と簡単さ、注意点についてまとめてみた。コード更新のしやすさ、管理のしやすさなどは考慮に入れていない。初心者の記事なので、間違っている箇所があるかもしれないのでご了承いただきたい。

※2021年3月19日追記：ラズパイ上でGreengrass Lambdaを動かしローカルファイルをS3にアップロードする方法へのリンクを追加しました。

#1. 対象となる機種
今回エッジデバイス代表としてはよく使われる以下の2つを対象とした。
ESP32（OSなし）
ラズパイ（Raspbian OS）


##1-1. ESP32
マイコン代表。1000円。ESP8266（500円）に比べてスペックが高いのでZerynthで簡単なPythonが動かせたりAmazon Free RTOSも乗る。NodeREDをかませればAWS IoTとも割と簡単につながる（NodeREDは基本無料）。一番使われているのはArduino IDEでの開発らしい。確かに敷居が低く、ESP32が手元にあればLチカまでの時間は驚きの5分（ESP32のArduino IDEでの開発に関しては[こちら](https://qiita.com/hdmn54321/items/1579200f4b4663b4ffaf)を参照。

##1-2. ラズパイ
今回はJetson nanoなどの小さいPCを代表。実際Jetson nanoとの違いは感じられない（RaspbianとUbuntu）。最初ディスクイメージをマウントしたSDカードをラズパイに入れる前に、SDカードに空の「ssh」という名前のファイルとwifiの認証情報を入れた「wpa_supplicant.conf」という名前のファイルを入れて起動するとスクリーンやマウスキーボードを準備しなくて良いのでオススメ（[こちら](https://qiita.com/nori-dev-akg/items/a8361e728a66a8c3bdba)に説明がある。その場合自分のPCからSSH接続等でつなぐ）。ラズパイゼロなど最初からCLIのみで使う場合などは必須の設定。

#2. ESP32でMQTT通信
調べた限り4つの方法がある。

##2-1. Arduino IDEをPCにインストールして、直接AWS IoTとMQTT
何度か試したが、挫折。ネットの評判でも「まさに悪夢的な体験だった」とか、できなかったと言う声が多い。自分は[このコード](https://github.com/HarringayMakerSpace/awsiot/blob/master/Esp32AWSIoTExample/Esp32AWSIoTExample.ino)でトライした。

##2-2. NodeREDをかませてTLS認証をやってもらい、AWS IoTとMQTT
簡単だった。やり方は[こちら](https://qiita.com/hdmn54321/items/1579200f4b4663b4ffaf)を参照。IBMクラウドアカウントを作ってやったが、RaspbianOSにデフォルトで入っているNodeREDを使ってもできるかも…。

##2-3. Amazon Free RTOSを乗せてAWS IoTとMQTT
AWSがしきりに推しているFreeRTOS。実際の使用率はまだまだ低いらしい。もともとのESP32にはないフォルダが作れ、より粒度の高いプログラムが書ける。Arduino IDEで開発している際は出てこない「LEDblink.c」みたいなあまりみたことないファイルが出てくる。コードを書く難易度は他と比べて高い。ただGreengrassと同様にOTAアップデートに対応しているのは強い。ESP8266だとメモリが足らなくてのらない。

##2-4. Mongoose OSをインストールしてJavaScriptでコード記述／ZerynthをインストールしてLightPythonでコード記述
この二つはTLS認証も仲介してくれて、かつCでコードを書かなくて良いのがポイント。まだ試していない。

##2-5. 感想
実際の運用における管理のしやすさやコードのアップデートを考えない限りは、NodeREDを使う方法で必要十分に思える。所詮ESP32はくっついているセンサーの値を一定時間間隔でIoT CoreやGreengrass Coreデバイスに送るだけが機能なのでそこまで頭を悩ませたくないというのもある。ただ一斉にOTAアップデートをかける方法が見当たらない。そういう運用はしないのだろうか…。

#3. ESP32でHTTPS通信
ESP32で大量のデータをHTTPSで送るケースが考えられないのでここでは考えない。MQTTでAWS IoTに送ってRuleでKinesis FirehoseをかませてS3に保存するか、Greengrass Coreデバイスに送ってそこで処理をかませてAWS IoTに送るパターンの2つで問題はなさそう。


#4. ラズパイでMQTT通信
AIカメラを使ったり大規模データを扱ったりしなければ大体の目的はMQTTで達成できる。以下の2つの方法がある。

①AWS IoT Device SDKを使う

②Greengrass CoreをインストールしてLambdaをラズパイ上で動かす


##4-1. ①AWS IoT Device SDKを使う（自分はPythonでやった）
簡単。3STEPで15分ほどで完了する。

```
#Githubから必要なSDKとサンプルとなるPythonファイルをダウンロードする。
git clone https://github.com/aws/aws-iot-device-sdk-python.git

#nanoでPythonファイルを編集
cd /.../sample/basicPubSub/
nano basicPubSub.py

#PythonファイルにAWS IoTのカスタムエンドポイント、デバイスの証明書、サーバー証明書、秘密鍵のファイルパスを書き込んで、
python basicPubSub.py
で実行する。
```

※実はAWS IoTコンソールで「モノ」を登録しなくても、他のモノの証明書と秘密鍵を使いまわせば行けてしまう（本番はもちろん逐一生成推奨）。

##4-2. ②Greengrass CoreをインストールしてLambdaをラズパイ上で動かす

###4-2-1. 注意点
Greengrass CoreのインストールとAWS IoTとの単純なMQTT通信は簡単だが、新たにLambdaを作成してローカルにデプロイするにはGreengrass　Groupのサブスクリプションを理解する必要があるし、Lambdaのバージョン管理やデプロイ、GreengrassGroupデプロイのプロセスが煩雑で、慣れるまでに結構時間がかかる。

###4-2-2. 手順
Greengrass Coreのラズパイへのインストールは公式の[QuickStart](https://docs.aws.amazon.com/ja_jp/greengrass/v1/developerguide/quick-start.html)がとにかくおすすめだ。基本的な流れとしてはSSHでラズパイに接続してターミナル上で、

```
#AWSアカウント情報を環境変数として設定（あなたのAWSアカウントの情報を入れる）
export AWS_ACCESS_KEY_ID=[アクセスキー]
export AWS_SECRET_ACCESS_KEY=[シークレットアクセスキー]

#設定されていることを確認
echo $AWS_ACCESS_KEY_ID
echo $AWS_SECRET_ACCESS_KEY

#curlでGreengrassCoreソフトウェアやセットアップ用のシェルスクリプトのダウンロードと起動
curl https://d1onfpft10uf5o.cloudfront.net/greengrass-device-setup/downloads/gg-device-setup-latest.sh > gg-device-setup-latest.sh && chmod +x ./gg-device-setup-latest.sh && sudo -E ./gg-device-setup-latest.sh bootstrap-greengrass-interactive 
```

だけでできてしまう

※ラズパイの場合は以下のように、

```
#再起動
sudo reboot

#(sshで繋いでいた場合は)
ssh xxx@xxx.xxx.xxx.xxx

#セットアップシェルスクリプトの再起動（「前のセッションの設定(condiguration)を再利用しますか？」と言う
メッセージが出るので、「yes」を打つ）
sudo -E ./gg-device-setup-latest.sh bootstrap-greengrass
```

をやらないと初期設定が完了せず、Greengrass GroupもCoreも作成されないので注意（※もし認証エラーが出るようならもう一度export AWS_ACCESS_KEY_ID=[アクセスキー]　export AWS_SECRET_ACCESS_KEY=[シークレットアクセスキー]を設定する必要がある）。

ラズパイの場合はすごく面倒だが、本来なら使いにくいAWSコンソールで色々と細かい設定をしなくてはならず、「ああIAMロール作るの忘れた！」ということになりがちだが、そこら辺も全てやってくれる。このセットアップ用のシェルスクリプトが英語で色々と聞いてくるのでそこで簡単な初期設定を打ち込むだけでいい。その内容をよく読んでみると必要なIAMロールなどを自動で作ってくれているがターミナルでもわかるはずだ。

単純なMQTT通信だけであればGreengrassCoreインストール時に一緒にインストールできるHelloWorldLambdaでとりあえずのAWS IoTとのMQTT通信はできる（やり方は[こちら](https://qiita.com/kt-yt/items/d8d44c7649699303a727#2%E3%82%A8%E3%83%83%E3%82%B8%E7%92%B0%E5%A2%83%E3%81%B8%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4)を参照のこと）。


#5. ラズパイでHTTPS通信
3つ方法がある。

##5-1. AWSコマンドを使う
自分はバージョン1でやった。インストール方法は[こちら](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-install.html)。やり方は簡単で、単純にローカルにあるファイルをS3にアップロードするだけならすぐにできる。大まかな流れとしては、

```
#AWS CLIのダウンロード
curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"

#ZIPファイルの解凍
unzip awscli-bundle.zip

#インストールファイルの実行と$PATHの指定
sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws

```
でAWSコマンドを使えるようにして、

```
aws configure
```

であなたのAWSアカウントの認証情報と基本リージョン、出力形式を指定する。AWS IoTサービスはリージョン依存的なのでAWS IoTのリージョンと同じリージョンを指定して、出力形式はjsonやあるいはnoneで良い。これで準備は完了したので、以下のコマンドでS3にHTTPS通信でファイルのアップロードができる。

```
aws s3 cp <source> <target> [--options]

#<source>はアップロードしたいファイルのパスを指定。
#<target>には上げる対象となるS3のバケツの名前。「s3://my-bucket/path」という感じで指定する。
```

AWSコマンドを使ったS3の操作は[こちら](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-s3-commands.html)に詳しい。AWSコマンドの全体的なreferenceは英語だが[こちら](https://docs.aws.amazon.com/ja_jp/cli/latest/reference/)を参照のこと。

##5-2. 感想
とにかく簡単。Lambdaでsystem callで呼ぶようにすれば運用も簡単にできそう（後述するようにLambdaでGreengrass CoreデバイスのローカルにあるファイルをS3にアップロードするのは簡単ではなかった）。

##5-3. AWS SDKを使う
自分はPython版でやった(通称boto3。インストール方法は英語だが[こちら](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)を参照のこと）。

S3にラズパイのローカルファイルをアップロードするPythonファイルは以下参照。boto3のその他のS3関係のサンプルコードは英語だが[こちら](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-examples.html)を参照のこと（S3以外のAWSサービスのコードも載っている）。

```python
import boto3

s3 = boto3.resource('s3')

#S3の全てのバケツを表示
for bucket in s3.buckets.all():
  print(bucket.name)

#ローカルファイルをS3にアップロード（file_nameにはファイルパスを指定。bucket_nameには対象となるS3バケツの名前を指定）
file_name = 'file_path'
bucket_name = 'bucket_name'

data = open(file_name,'rb')
s3.Bucket(bucket_name).put_object(Key=file_name, Body=data)
```


##5-4. Greengrass CoreにデプロイしたLambdaをkickしてS3にHTTPS通信でアップロード
他と比べて圧倒的に難易度が高いが、[こちら](https://qiita.com/hdmn54321/items/e10990014620d674d3ec)に手順と注意点をまとめたので参照していただきたい。2,3時間くらいはみておきたい。


#6. さいごに
今回はエッジデバイスとAWSとのつなぎの部分をまとめてみた。基本的なところだが、難易度には大きく開きがある。ぜひ参考にしていただきたい。

※間違いなどあればコメント欄で指摘していただけると幸いです。