この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
IoT事業部の平内(SIN)です。
唐突ですが、パトランプ回してみました。ON/OFFに使用したのは、100円ショップ(330円)で販売されているBluetooth リモコン シャッターです。
下の動画は、動作 を確認している様子です。
2つあるボタンで、それぞれON/OFFするようになっています。なお、Bluetoothで取得したボタン押下の情報は、MQTTにPublishされ、Sabscribeしているデバイスが、そのPayloadに基づいてパトランプを操作しています。
操作指示がMQTTのメッセージなので、IoT Coreのテスト画面からメッセージを送っても、動作できていることが分かります。これで、地球の裏側のパトランプも操作できるはずです。
2 構成
構成は、以下の通りです。
デバイス側で動作するプログラムは、Greengrass V2のコンポーネントとして作成されています。 図中の左右のGreengrassで動作するコンポーネントは、同一のものです。 末端デバイスとしてリモコンを繋ぐか、パトライトを繋ぐかで役割が変わるイメージです。
また、MQTTメッセージがトリガですので、1つのリモコンで、複数のパトライトを操作することも可能です。
3 Bluetooth リモコン シャッター
使用したリモコンです。現在は、ダイソーで品切れになっているようですが、Amazonnでも購入できます。まったく同じものでは無いですが、Can★Doでは、最近でも店頭に並んでいるのを見かけました。
https://www.amazon.co.jp/Bluetooth-Shutter-ABS3-BLK/dp/B00JX70WK4/
4 Raspberry PI
使用したのは、RaspberryPi 3Bで、OSは、今年4月の最新版(Raspberry Pi OS with desktop)です。
3Bでは、最初からBluetoothが利用可能なので、特にドングルを用意する必要なありません。
$ cat /proc/cpuinfo | grep Revision
Revision : a32082
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description: Raspbian GNU/Linux 10 (buster)
Release: 10
Codename: buster
$ uname -a
Linux raspberrypi 5.10.17-v7+ #1414 SMP Fri Apr 30 13:18:35 BST 2021 armv7l GNU/Linux
5 ペアリング
Bluetoothのペアリングは、RaspberryPiで利用可能な、bluetoothctlを使用しました。
(1) 起動
$ bluetoothctl
Agent registered
[bluetooth]#
(2) スキャン
scan onで、電源ONとなっている周辺のBluetoothデバイスが列挙できるので、対象(AB Shutter3)のアドレスを控えます。
[bluetooth]# scan on
Discovery started
[NEW] Device 2A:1B:3C:11:22:38 AB Shutter3
・・・略・・・
[bluetooth]# devices
Device 2A:1B:3C:11:22:38 AB Shutter3
・・・略・・・
[bluetooth]# scan off
Discovery stopped
(3) ペアリング
pair/removeでペアリング及び、解除ができます。 下記では、デバイスをペアリングして、ペアリング中のデバイス一覧しています。
[AB Shutter3]#pair 2A:1B:3C:11:22:38
Attempting to pair with 2A:1B:3C:11:22:38
[AB Shutter3]# paired-devices
Device 2A:1B:3C:11:22:38 AB Shutter3
(4) 信頼
trustで、デバイスを信頼しておくことで、再起動後もペアリング有効になります。
[AB Shutter3]# trust 2A:1B:3C:11:22:38
[CHG] Device 2A:1B:3C:11:22:38 Trusted: yes
Changing 2A:1B:3C:11:22:38 trust succeeded
[AB Shutter3]# exit
$
(5) /dev/input/evdev0
認識されているリモコンは、入力デバイスとして/dev/input/の配下に見つけることができます。
$ ls -la /dev/input
total 0
drwxr-xr-x 2 root root 100 Sep 26 14:59 .
drwxr-xr-x 16 root root 3660 Sep 26 14:59 ..
crw-rw---- 1 root input 13, 64 Sep 26 14:59 event0
crw-rw---- 1 root input 13, 63 Sep 26 14:59 mice
6 権限追加
Greengrassで、デフォルトのユーザー(ggc_user)を使用する場合、GPIO及び、入力デバイスでパーミッションエラーが発生してしまいます。
stderr. GPIO.setup(PATLAMP, GPIO.OUT).
stdout. Exception [Errno 13] Permission denied: '/dev/input/event0'.
どちらも、グループに追加することで、解消可能です。
$ sudo usermod -G input,gpio ggc_user
$ cat /etc/group | grep ggc_user
input:x:105:pi,ggc_user
gpio:x:997:pi,ggc_user
ggc_user:x:995:
7 アーティクル
下記が、本体コードとなります。
それぞれ役割ごとにクラスになっています。
- Patramp パトライト制御クラス(GPIO処理)
- Bluetooth Bluetooth受信クラス
- Mqtt MQTT送受信クラス
Greenglassのセットアップや、コンポーネントの作成、MQTTのPub/Subについては、下記の記事をご参照ください。
[AWS IoT Greengrass V2] RaspberryPIにインストールしてみました
[AWS IoT Greengrass V2] RaspberryPIでコンポーネントを作成してみました
[AWS IoT Greengrass V2] コンポーネントからIoT CoreのメッセージブローカーにPublish/Subscribeしてみました
import time
import glob
import RPi.GPIO as GPIO
import evdev
import json
import awsiot.greengrasscoreipc
import awsiot.greengrasscoreipc.client as client
from awsiot.greengrasscoreipc.model import (
IoTCoreMessage,
QOS,
PublishToIoTCoreRequest,
SubscribeToIoTCoreRequest
)
# パトライト制御クラス
class Patramp():
def __init__(self, gpio) -> None:
self.PATLAMP = gpio
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.PATLAMP, GPIO.OUT)
def on(self):
GPIO.output(self.PATLAMP, 1)
def off(self):
GPIO.output(self.PATLAMP, 0)
# Bluetooth受信クラス
class Bluetooth():
def __init__(self) -> None:
inputs = glob.glob('/dev/input/event*')
if(len(inputs) <= 0):
print('device not found.')
exit(0)
self.input = inputs[0]
print(self.input)
def recv(self):
self.device = evdev.InputDevice(self.input)
for event in self.device.read_loop():
if(event.type == 1):
if(event.code == 28):
return 1
elif(event.code == 115):
return 0
return -1
# MQTT送受信クラス
class Mqtt():
def __init__(self) -> None:
self.ipc_client = awsiot.greengrasscoreipc.connect()
self.timeout = 10
class StreamHandler(client.SubscribeToIoTCoreStreamHandler):
def __init__(self, on_recv):
super().__init__()
self.__on_recv = on_recv
def on_stream_event(self, event: IoTCoreMessage) -> None:
message = str(event.message.payload, "utf-8")
print("onRecv payload:{}".format(message))
self.__on_recv(json.loads(message))
def on_stream_error(self, error: Exception) -> bool:
return True
def on_stream_closed(self) -> None:
pass
def subscribe(self, topic, on_recv):
qos = QOS.AT_LEAST_ONCE
request = SubscribeToIoTCoreRequest()
request.topic_name = topic
request.qos = qos
handler = self.StreamHandler(on_recv)
operation = self.ipc_client.new_subscribe_to_iot_core(handler)
future = operation.activate(request)
future.result(self.timeout)
def publish(self, topic, payload):
qos = QOS.AT_LEAST_ONCE
request = PublishToIoTCoreRequest()
request.topic_name = topic
request.payload = json.dumps(payload).encode('utf-8')
request.qos = qos
operation = self.ipc_client.new_publish_to_iot_core()
operation.activate(request)
future = operation.get_response()
future.result(self.timeout)
print("publish :{}".format(payload))
time.sleep(1)
gpio = 17 # GPIO 17
patraml = Patramp(gpio)
bluetooth = Bluetooth()
def on_recv(message):
if(message["sw"] == "ON"):
patraml.on()
else:
patraml.off()
topic = "patramp/100"
mqtt = Mqtt()
mqtt.subscribe(topic, on_recv)
while True:
try:
result = bluetooth.recv()
if(result != -1):
if(result == 0):
sw = "ON"
else:
sw = "OFF"
mqtt.publish(topic, { "sw": sw })
except Exception as e:
print('Exception {}'.format(e))
time.sleep(1)
8 レシピ
レシピです。 デフォルトCoonfigrationのaccessControlでIoTCoreに対するPublishとSubscribeが許可されています。
---
RecipeFormatVersion: 2020-01-25
ComponentName: com.example.Patramp
ComponentVersion: '1.0.4'
ComponentConfiguration:
DefaultConfiguration:
accessControl:
aws.greengrass.ipc.mqttproxy:
com.example.Patramp:mqtt:1:
operations:
- "aws.greengrass#PublishToIoTCore"
- "aws.greengrass#SubscribeToIoTCore"
resources:
- "patramp/#"
Manifests:
- Platform:
os: linux
Lifecycle:
Install: pip3 install evdev awsiotsdk
Run: python3 -u {artifacts:path}/patramp.py
Artifacts:
- URI: s3://gg-artifacts-2021-08-11/artifacts/com.example.Patramp/1.0.0/patramp.py
9 最後に
今回は、Bluetoothのリモコンでパトランプを操作するコンポーネントを作成してみました。慣れてくると、普通のツールを作成するのと、コンポーネントを作成するのに、それほど差異を感じなくなってきます。
要件が許す場合、ありがたくGreengrassの恩恵を受けれるように、色々試しておきたいと思っています。
10 参考リンク
[AWS IoT Greengrass V2] RaspberryPIにインストールしてみました
[AWS IoT Greengrass V2] RaspberryPIでコンポーネントを作成してみました
[AWS IoT Greengrass V2] クラウド側から複数のコアデバイスにコンポーネントをデプロイしてみました
[AWS IoT Greengrass V2] クラウド側からコンポーネントを削除してみました
[AWS IoT Greengrass V2] ローカルデバッグコンソール(aws.greengrass.LocalDebugConsole)を使用してみました
[AWS IoT Greengrass V2] Lambda関数(コンポーネント)をデプロイしてみました
[AWS IoT Greengrass V2] コンポーネントからIoT CoreのメッセージブローカーにPublish/Subscribeしてみました
[AWS IoT Greengrass V2] コンポーネントからシークレットマネージャにアクセスしてみました
[AWS IoT Greengrass V2] コンポーネントでコアデバイス間のPublish/Subscribeを試してみました
[AWS IoT Greengrass V2] ログマネージャでコンポーネントのログをCloudWatch Logsに送ってみました
[AWS IoT Greengrass V2] トークン交換サービスでコンポーネントからDynamoDBにアクセスしてみました
[AWS IoT Greengrass V2] ストリームマネージャーを使用してコンポーネントからKinesis Data Streamsへデータを送ってみました
[AWS IoT Greengrass V2] プロセス間通信 (IPC) を使用してコンポーネントの設定値を使用してみました
[AWS IoT Greengrass V2] ストリームマネージャーを使用してWebカメラの画像を毎秒2フレームでS3に送信してみました