AWS IoT Core とつながった信号灯の声を聞く
2022/10/17
Table of Contents
何に使ったか?
個人電話ブースの空き状況を、信号灯でお知らせするシステムを開発しました。
Fusic には、オンラインミーティング等をするときに使用する小さなブース(通称、サテライト)が6部屋あります。 そのうち3部屋は予約が必要な部屋で、残りの3部屋はあいていれば誰でも自由に使うことができる部屋です。 今回は、あいていれば誰でも自由に使うことができる3部屋のサテライトを対象にしています。
Fusic には弊社松山がOJTで制作したサテライト番長というシステムがあり、Slack コマンド経由、もしくはWebブラウザ経由でサテライトの空き状況を知ることができる仕組みがあります。各部屋に独自開発した超音波センサーを使ったデバイスを設置しており、凝った作りになっています。詳細はIoT でオンライン会議スペースを監視し Slack に表示する をご覧ください。
今回は、サテライト番長と連携してサテライトの空き状況を信号灯の光り方で示し、更に部屋が空いたら信号灯を光らせると同時に「サテライトN が空きました」と音声で通知する仕組みを作成しました。
サテライトと信号灯の位置関係
サテライトは執務室の奥まった場所にあり、空き状況を確認するのはかなりめんどくさく、多くの人が徒歩で確認しに行き部屋が空いていたら利用するという運用が行われていました。
サテライト番長の登場により状況が一変するかと思いきや、電話がかかってきた瞬間に部屋の空きを確認するためにブラウザを開いたりSlackコマンドを打ったりする運用はなかなかハードルが高く、あまり社内には浸透していませんでした。
サテライトの様子はこちら。
また、設置した信号灯と監視対象のサテライトの位置関係はこんな感じです。
この信号灯をオフィスの大部分から見られる場所設置したことで、多くの人がひと目でサテライトの空き状況を把握できるようになりました。
オフィス入口側から見た風景
オフィス奥側から見た風景
今まで、サテライトはオフィスの大部分から死角になっていたことがわかります。
会社の多くの場所から状況を確認できる位置にサテライトの状況を知らせてくれる信号灯をおいたことにより、サテライトの空き状況が一目で判断できるようになり、急な電話の際などにもサテライトを利用しやすくなりました。
ここからは、技術的な話に入っていこうと思います。
技術的な話
Text To Speech
Text To Speech と呼ばれる技術を使うと、テキストを読み上げることができます。アクセントや音の高低などテキストを読み上げるというのはとても難しいのですが、近年は研究が進み、自然な読み上げができるようになってきました。Fusic でもText To Speech の検証、実証実験等を行っており、過去に「簡単に作れるTTSモデル:ESPnetを用いたつくよみちゃんTTSモデル作成」というTech Blog を公開したりしています。
今回の信号灯にはText To Speech が実装されていて、日本語、もしくは英語を読ことができます。 本記事の後半で、実際に読み上げを行います。
AWS IoT Core とのつなぎ込み
信号灯は、AWS IoT Core を使ってクラウドから制御することができます。 今回は、クラウド経由でサテライトの空き状況を定期的に信号灯に送信し、光らせました。
簡単ですが、AWSの構成図は、以下のようになっています。
実際の動作は、
① 定期的にサテライト番長の様子を確認するAmazon EventBridge が起動する
② AWS Lambda から、サテライト番長のAPIにリクエストを行い、サテライトの空き状態を問い合わせる
③ サテライト番長からのレスポンス
④ 部屋のステータスをAWS IoT Core に送信
⑤ AWS IoT Core からPublish された命令を元に、信号灯のステータスが更新
という流れです。
Amazon EventBridge の設置や AWS Lambda の設定はChaliceを使用して行い、AWS IoT Core の設定は信号灯のドキュメントを参考にしながら行いました。
優秀すぎるドキュメント
Tech Blog なので、AWS IoT Core との接続方法とか信号灯の設定とかをご紹介すべきなのですが、いかんせんドキュメントが充実しすぎていて。。ここで劣化コピーを作成するより完成されたドキュメントを読んでいただいた方が数十倍良いと思います。
残念ながら、AWS IoT Core のAWS コンソールは使いやすいとは言えず、何の知識もない状態では何をすればいいのか一切わかりません。でも、信号灯のマニュアルがあれば大丈夫!ほぼ詰まることなく設定が完了します!
自分が唯一詰まったのは、AWS IoT Core Shadow というサービスの部分だけでした。これは、マニュアルの問題ではなく、サービス理解の欠如の問題でした。AWS IoT Core Shadow については少し解説したいと思います。
AWS IoT Core Shadow
Shadow は、日本語に直すと影。つまり、デバイスが実体で、デバイスの影をクラウド上で見えるようにするというニュアンスだと思います。おしゃれですね。
クラウド上からデバイスのステータスを知ることができる機能というわけです。
IoT デバイスは、物理空間に置かれているため、人が勝手に触ってステータスを変えることができます。例えば、AWS IoT Core からの命令で緑色が光っている状態からデバイスを再起動した場合を考えます。再起動時の設定に漏れがあると、クラウド上ではデバイスは緑に光っているはずなのに、実際は緑の光が消えているということが起こりえます。この問題を解決してくれるのが、IoT Core Shadowです。
デバイスの現在の状態を取得することができるので、再起動を挟んだ場合にデバイスのライトが消えていることをクラウドが検知することができるのです。とても便利!まるで有線で繋いでいるかのような錯覚に陥ります。
データシャドウの料金は、東京リージョンで1.50USD (オペレーション 100 万件あたり)。これが高いのか安いのか正直わかりませんが、めちゃくちゃ便利なことは間違いないです!
この機能を使うと、デバイスの状態を保存しておくデータベースが必要なくなるのもとても便利です。 今回実装した機能の中に、部屋が空いたら空いた部屋を音声で教えるという機能がありますが、この機能はAWS IoT Core Shadow を使用して実装しています。
実際に、信号灯を触ってみる
ここからは、実際の画面やコードを織り交ぜながら、信号灯の使い方と今回作成したシステムの作り方をご説明しようと思います。 初期設定は簡単なので、今回は割愛します。
信号灯のWebコンソールから、信号灯に喋らせる
前回使用した型にはなかった、信号灯が喋る機能を実際に使ってみたいと思います。
信号灯のWebコンソールに入ると、任意のテキストを入れて信号灯に喋らせることができます。
- 左側のタブから、音声登録ボタンをクリック
- 登録チャンネルを入力 (1 ~ 60のint)
- タイトルを入れます
- 音声種別を音声合成にする (デフォルトは音声合成)
- 読み上げたいテキストを入力
- 言語、声、トーン、速度などを入力し、チャンネルリストに追加
- 試聴する
これだけで、信号灯に任意の文字列を話させることができます。
AWS IoT Core 経由で信号灯に喋らせる
IoT Core の設定が終わったら、以下のコードを参考にして、信号灯にプログラムから送った文字列を喋らせることができます。なんとリアルタイム音声読み上げに対応しており、AWS IoT Core から任意の文字列をPublish すると、ほぼタイムラグなく読み上げてくれます! すごすぎ!!
コードの例を示します。
import boto3
import json
your_profile_name = "profile"
your_topic_name = "topic/subscribe"
sess = boto3.Session(profile_name=your_profile_name)
iot = sess.client("iot-data")
data = {
"speech":[{
"text": "信号灯がこんなにスラスラ喋るなんて!!",
"lang": "jp",
"voice": "male",
"speed": "-3",
"tone": "0"
}]
}
iot.publish(
topic=your_topic_name,
qos=1,
payload=json.dumps(data, ensure_ascii=False)
)
たった数行のコードで、AWS IoT Core を経由して信号灯に喋らせることができます。
システムのデプロイ
今回は連携先のサテライト番長があるので、そこから帰ってくるデータについて先に説明します。
サテライト番長は、URL にPOST すると、
1. 空
2. 空
3. 空
4. 空
5. 使用中
6. 空
というふうなデータを返してきます。
今回は、このデータをParse して、空き状況を取得しています。
Chalice の設定
aws/chaliceを使用して、プロジェクトの初期設定を行います。 ドキュメントに記載がある通り、 chalice new-project helloworld
と打つと helloworld
というディレクトリが作成され、その中に以下のようなファイルが設置されます。
$ cd helloworld
$ ls -la
drwxr-xr-x .chalice
-rw-r--r-- app.py
-rw-r--r-- requirements.txt
app.py の中身を編集すると、今回のシステムが出来上がります。
なお、app.py の中身を別ファイルに切り出したい場合は、 chalicelib
というディレクトリを作成しその中にコードを設置、app.py からImport する必要がある点に注意が必要です。
実際のコード
from chalice import Chalice, Rate
import boto3
import json
import urllib.request
import urllib.parse
from datetime import datetime, timezone, timedelta
app = Chalice(app_name='helloworld')
iot = boto3.client("iot-data")
thing_name = "thing_name"
def get_bancho_status():
"""
実装は省略。
"""
return """1. 空
2. 空
3. 空
4. 空
5. 使用中
6. 空
"""
def control_patlite():
result = get_bancho_status()
keys = [
"led_red",
"led_yellow",
"led_green",
]
status = {k: 0 for k in keys}
for i, text in enumerate(result.split("\n")[3:6]):
tmp = 0 if "空" in text else 1
status[keys[i]] = tmp
# ここで、Shadow を調査
shadow_result = json.loads(iot.get_thing_shadow(thingName=thing_name)['payload'].read().decode())['state']['reported']
announced_rooms = []
for k in shadow_result:
if k not in keys:
continue
if shadow_result[k] == 1 and status[k] == 0:
# keys.index(k) + 4 としているのは、
# サテライトの番号が4~6 を対象としているから。
announced_rooms.append(keys.index(k) + 4)
led = f"{status['led_red']}{status['led_yellow']}{status['led_green']}00"
if len(announced_rooms) == 0:
published = json.dumps({"led": led}, ensure_ascii=False)
else:
announce = "と".join(announced_rooms)
published = json.dumps({
"led": led,
"speech": [
# 4と5が空いている場合、サテライトの4と5が空いたよ!と喋る。
"text": f"サテライトの{announce}が空いたよ!",
"lang": "jp",
"voice": "male",
"speed": 0,
"tone": 0
]
}, ensure_ascii=False)
iot.publish(
topic="patlite/subscribe",
qos=0,
payload=published
)
# 1分に一回EventBridgeを実行する
@app.schedule(Rate(1, unit=Rate.MINUTES))
def patlite(event=None):
t_delta = timedelta(hours=9)
jst = timezone(t_delta, 'JST')
now = datetime.now(jst)
## 人があまりいない時間帯は、実行しない。
# https://note.nkmk.me/python-datetime-day-locale-function/#_1
# 土日は無視
if now.weekday() >= 6:
return
# 9時以前は無視
if now.hour <= 9:
return
# 20時以降は無視
if now.hour >= 20:
return
control_patlite()
このようなコードを書くと、サテライトの状況を確認し、状況に応じて信号灯を点灯、消灯させたり、信号灯に喋らせたりすることができます。
とてもお手軽でした!
振り返り
実装して気づいたこと
私にとって初めてのIoT 開発でしたが、AWS IoT Coreを使ってデバイスと接続できさえすれば、後は普段使用するAWS と全く同じ感覚でデバイスを触れるというのは大きな発見でした。AWS IoT Core の設定周りは見慣れないことも多くかなり手こずりましたが、それ以外は比較的すんなり開発できました。今回の経験から、IoT に初めて触れる人の教材としても、信号灯は非常に有用だなと感じました。
この体験ができたのは、信号灯がAWS IoT Core に対応しており、更に AWS IoT Core Shadow にも対応している神デバイスだからなんだろうなとも思っています。次はRaspberry Pi などを使って、AWS IoT Core に対応する苦しみについても学びたいと思います。
運用して気づいたこと
点滅に注意
信号灯は、点灯消灯以外に、点滅というステータスを持つこともできます。 初期システムは、部屋が空いたら1分間は該当するライトを点滅させるという仕様で運用を行っていました。 サテライト番長の誤作動が原因で、誤って部屋のステータスを表示する可能性を減らす狙いでした。
しかし、この機能は社員からのクレームでボツになりました。
信号灯は様々なステータスを持てて便利ですが、使い方を誤ると迷惑をかけ得ると知れた事例でした。一方で、本当に緊急のときは点滅させることにより強制的に意識を向けることができるということも知りました。この教訓は、次以降信号灯を使ったシステムを作成する際に、生かしていこうと思います。
ワンクリックの壁
当初想定していた通り、信号灯のおかげで、サテライトの状況確認がとてもスムーズにできるようになりました。 今までSlack コマンドを打ったりWebページを開いたりしていた手間というのは恐ろしく高い障壁だったんだなと感じました。
便利なんだけどひと手間かかる。そんな身近なひと手間を減らすことの価値を改めて感じました。
自分がシステムを作成しているとき、「運用でカバー」という言葉がチラチラとこちらを伺ってくる場面も多いですが、今回感じたひと手間を減らすことの価値を肝に銘じ、これからもシステム開発に邁進していこうと思います。
安定運用
信号灯を設置して、約2週間運用を行いました。 一度点滅のご指摘をいただいた以外は、特に大きな問題もなく安定運用できています。
今回は貸与期間が過ぎてしまいましたが、今後長期運用の可能性についても検証できたら嬉しいなと思います。
要望
お借りしておいて図々しい話で恐縮ですが、一つ要望を書いておきたいと思います。 それは、
有線LAN 以外の選択肢も増やしてほしい。できればSIM がさせるようにして欲しい。
という要望です。
要望する理由は大きく以下の2点です。
- 屋外など、有線LAN が使えない環境での利用
- AWS IoT Core とのつなぎ込みの手間の解消
有線LAN が使えない環境での利用
Fusic は会社で田んぼを借りており、田んぼに田んぼまもる君というIoT デバイスを置いています(田んぼを見守るIoTデバイスを作りました!)。
ソーラーパネルで発電した電気で動いているこのデバイスにはSORACOM のSIM が刺さっており、定期的に田んぼの画像を弊社のSlack に送りつけてくれています(先日の台風14号にも耐えました🎉)
もしSIMの刺さる信号灯があれば、使い方の幅が広がるのではないかな?と考えています(いや、AWS IoT Core につながるだけでもものすごいんですよ!これは、本当にそう思います)。
つなぎこみの手間の解消
SORACOM社のサービスを使えば、電源を入れただけですぐに使い始められるIoT信号灯の実現も難しくないと考えています。
この仕組みだと、納品してすぐに証明書の管理など行わずにインターネットに繋がるため、使い始めるための手間が劇的に減ることが予想されます。
以上、勝手な要望でした。 ご検討いただけると幸いです。
まとめ
信号灯を使って、サテライトの空き状況を可視化する仕組みを作成しました。 AWS IoT Core を使った開発自体が初めてでしたが、特に詰まることもなく開発でき、快適なIoT ライフを堪能できました。
Yasuaki Hamano
I'm a software engineer in Fukuoka, Japan. Recently, I am developing machine learning models using TensorFlow, and also developing Web services by using PHP.