AWS GreengrassでLチカ：クラウドとエッジについて考える(1)

# できたもの

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">LED blink by AWS Greengrass controll <a href="https://t.co/sdBzLaifdf">pic.twitter.com/sdBzLaifdf</a></p>&mdash; nobu_e753 (@nobu_e753) <a href="https://twitter.com/nobu_e753/status/916533432183750656?ref_src=twsrc%5Etfw">2017年10月7日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

- AWS Greengrassのコンソール上からLambda関数をRasPi3へデプロイすると，RasPi3につないであるLEDが点滅する（5秒毎に赤→緑→赤...）
- その後，RasPi3のネットワークを切断してもLEDは既定のパターンで点滅し続ける

# はじめに

先日，JAWS-UG主催の[AWS Greengrassハンズオン](https://jawsug-nagoya.doorkeeper.jp/events/64809)に参加したのですが，機材トラブルや難度の問題もあり，半端な状態で終わってしまいました．あまり理解できず終わってしまったことが悔しかったので，自宅にて自分なりの課題を立て進めてみることにしました．本記事はそのまとめです．

※「自分なりに理解すること」を優先したので，公式のやり方に沿わないもしくは語弊がある部分もあるかもしれません

# 下準備

機材は以下で進めました．[こちら](https://aws.amazon.com/jp/greengrass/faqs/)にGreengrassに対応している機材一覧があります．

- RaspberryPi 3 
- PC

またとりくみ前に以下のチュートリアルを実施しました．内容そのものの理解というより"なんとなくオペレーションを理解しておく"ためです．

- [AWS IoTの公式チュートリアル](http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-gs.html)
- [AWS Greengrassの公式チュートリアル](http://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/what-is-gg.html)

言語はPythonですすめました．あと，あえて述べておくと私のAWSに関する知識は「EC2やDynamoDBを仕事でつかうものの利用は基本的な範囲にとどまる」レベルです．

# とりくみ内容

AWSを利用して以下のような「ネットワーク型信号機」を設計する，ことをお題にしました（実際は信号機にみたてたRasPi3+LEDを制御・点灯させます）．

![problem.png](https://qiita-image-store.s3.amazonaws.com/0/156705/1ebd6db0-c6af-3157-ad24-8530ccf34708.png)

この課題に取り組むにあたり

1. AWS IoT にて実現（クラウドの考え方）
1. AWS Greengrass にて実現（エッジの考え方）

の双方を試すことにより，それらの違い，そしてクラウドとエッジの考え方を感覚的に理解することを目標にしました．本記事では1つめ，[続編](https://qiita.com/nobu_e753/items/e6dc509d72222904458a)で2つめに取り組みます．

# 1. AWS IoTにてネットワーク型信号機を実現する

まずは構成検討です．できるだけ単純なモデルにするため以下にしました．

![p1.png](https://qiita-image-store.s3.amazonaws.com/0/156705/3ca619cc-e8bb-8d9e-6ac4-3604e4bef8be.png)

- 制御はAWS側から相当するMQTTメッセージを送信
- RasPi3側でメッセージを受信，信号機に見立てたLEDを制御（赤/緑の2色）

赤/緑だけの2色信号機をネットワーク経由で制御します（最初に「赤を点灯」に相当するメッセージを発信，60秒後に「緑を点灯」に相当するメッセージを発信...）．信号の切り替え間隔を変える場合も，サーバー(AWS)側の制御プログラムを変更するだけで済みます．なんかクラウドっぽいです...
早速作成してみます．

## 信号機側S/W

- MQTTメッセージを受信
- メッセージに合わせGPIO経由でLED制御

機能としてはこれだけです．コードは以下としました．[AWS IoTのサンプル](https://github.com/aws/aws-iot-device-sdk-python/tree/master/samples/basicPubSub)に毛の生えた程度です．各種アドレスやファイルパスはすべて埋め込みとしました．AWS IoTに接続するための証明書はあらかじめ作成し，./certs/ フォルダ以下に格納しておくものとします．
GPIO経由でLEDを点灯させることについては[こちら](https://qiita.com/aryoa/items/3f6d82b8c63761cef087)の記事などが参考になります．

```py3:led_brink_iot.py
# -*- coding: utf-8 -*-
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import RPi.GPIO as GPIO
import time
import json

# gpio pins
LED_R = 14
LED_G = 15
LEVEL = [GPIO.LOW, GPIO.HIGH]

# host
host = "xxxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com"
port = 8883

# certs
rootca_path = "./certs/root-ca.pem"
certificate_path = "./certs/thing-certificate.pem.crt"
privatekey_path = "./certs/thing-private.pem.key"
thing_name = "RasPi3"

def onMessage(message):    # メッセージを受け取った際に呼ばれる
	payload = json.loads(message.payload.decode('utf-8'))
	print(" > Message : {}".format(payload))
	print("--------------\n")	
	
	try:    # GPIOの制御
		red_lv = int(payload['red'])	
		green_lv = int(payload['green'])
		GPIO.output(LED_R, LEVEL[red_lv])
		GPIO.output(LED_G, LEVEL[green_lv])
	except:
		print(" ! Invalid message")		

if __name__ == "__main__":

	# gpio setup
	GPIO.setwarnings(False) 
	GPIO.setmode(GPIO.BCM)
	GPIO.setup(LED_R, GPIO.OUT)
	GPIO.setup(LED_G, GPIO.OUT)
	
	# mqtt setup
	mqtt_client = AWSIoTMQTTClient(thing_name)
	mqtt_client.configureEndpoint(host,port)    # MQTTクライアントの接続先を上で指定したhost/portにする
	mqtt_client.configureCredentials(rootca_path, privatekey_path, certificate_path)

	mqtt_client.configureOfflinePublishQueueing(0)
	mqtt_client.configureDrainingFrequency(2)
	mqtt_client.configureConnectDisconnectTimeout(120)
	mqtt_client.configureMQTTOperationTimeout(5)

	# Register an onMessage callback
	mqtt_client.onMessage = onMessage    # メッセージを受信したときのコールバック関数設定

	# start subscribe
	mqtt_client.connect()    # 接続
	time.sleep(2)
	print(" * Connection successful")	

	mqtt_client.subscribe("io/led", 1, None)
	print(" * Start subscribe..")

	while True:
		time.sleep(3)    # 無限ループでメッセージ受信を待つ
```

サーバー側からのメッセージは以下のMQTTトピック＆JSON形式としました．0なら消灯，1なら点灯です．

```bash
# MQTT topic
io/led
```
```python
# Message format
{
    "red":0,
    "green":1
}
```

## 信号機側H/W

GPIO-LED間の接続は以下としました（抵抗には手元にあった330Ωを使用）．

![20170929_krs_qiita.png](https://qiita-image-store.s3.amazonaws.com/0/156705/a38ba563-4f16-5f38-d2d4-7153cc6c9247.png)

# 動かしてみる
まずはRasPi3側のプログラムを動かします．

```shell-session
pi@raspi3-nobu_e753:~/Workspace/aws $ ls
certs/  led_blink_awsgg.py  led_blink_awsiot.py
pi@raspi3-nobu_e753:~/Workspace/aws $ python3 led_blink_awsiot.py 
 * connection success
 * start subscribing...    # AWS側からメッセージを発行すると以下が出力される
 > Message : {'green': 0, 'red': 1}
--------------

 > Message : {'green': 1, 'red': 0}
--------------

 > Message : {'green': 0, 'red': 1}
--------------
```

待ち受け状態になったらAWS IoTのコンソールからMQTTメッセージを発行してみます．

![aws01s.png](https://qiita-image-store.s3.amazonaws.com/0/156705/3e011c0b-3758-de06-89df-6d78f36fbb27.png)

![aws02.png](https://qiita-image-store.s3.amazonaws.com/0/156705/039f1807-f26d-fcc5-1939-f35b01b62501.png)

メッセージを発行すると，コンソールにその内容が表示されると当時にRasPi3に接続されているLEDの点灯状況が変わるはずです．このメッセージを発行手順を，手動ではなく，AWS Lambdaで記述し，これを定期実行するようにすれば，「サーバーからのメッセージによって切り替えを行うクラウドタイプ信号機」が実現できます（AWS Lambdaによる定期発行のコードは省略します）．

# クラウドタイプ信号機の問題点

さて，もし世の中の信号機が上記の構成で実現されていたら**かなり危ない**ことに気づくと思います．

**1. ネットワークが切れたら終わり**
まずこれです．信号機の制御はサーバーから送信されにはてくるメッセージによっているので，ネットワークが切れた瞬間に制御できなくなります．事故が多発するに違いありません．

**2. 通信費がバカにならない** 
日本にある信号機は全部で約20万基だそうです（ちなみに，コンビニ5万件，歯医者7万件，美容院23万件！だそうです）．これらをすべて制御する場合，一体どれだけの費用がかかるか想像もしたくありません．

どうやらこの構成だと問題が大きそうです

# ではどうしたら？

上記問題を解決するには

- 信号機側である程度自立して動くようにする
- ただしネットワークを通じて制御内容をアップデートできるようにする

としたいです．すると信号機側にアップデーを受け入れる機構や，新設定にしたがって動き出す機構，アップデートが失敗した場合の手当てを行う機構などなど，本題ではないところでいろいろと面倒なことが発生します．

ううむ面倒だ，誰かやってもらえないだろうか... 

**→ AWS Greengrassに面倒をみてもらおう！**（という意図のサービスだと私は理解しました

[記事2](https://qiita.com/nobu_e753/items/e6dc509d72222904458a)に続きます．