AWS IoT GreengrassでRaspberry Piに接続されたModbusのデータを取得

こちらの記事で作成したModbus通信の温湿度計を、AWS IoT GreenGrass経由で操作してみました。

yomon.hatenablog.com

概要

やりたいことの概要です、MQTT経由でコマンドを送り、温度、または湿度を指定して値を取得してみようと思います。(Raspberry Pi 3B+を利用しています)

f:id:yomon8:20200908224005p:plain

Lambda作成

GreenGrassにデプロイするLambdaを作成します。Python3.7にします。

f:id:yomon8:20200908214720p:plain

細かい内容は冒頭でも書いたこちらの記事に記載しています。

コードを載せておきます。

import sys
import json
import time
import logging

import greengrasssdk

import serial
import RPi.GPIO as GPIO


INPUT_TOPIC = "test/command"
OUTPUT_TOPIC = "test/get_value"

# Modbus Commands
TEMPERATURE_CMD = bytes.fromhex("010400010001600A")
HUMIDITY_CMD = bytes.fromhex("010400020001900A")

# Setup
logger = logging.getLogger(__name__)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
client = greengrasssdk.client("iot-data")

# Setup GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
GPIO.output(17, False)
GPIO.setup(18, GPIO.OUT)
GPIO.output(18, True)
ser = serial.Serial("/dev/ttyS0", 9600, timeout=None)


def is_valid_input_topic(context):
    try:
        topic = context.client_context.custom["subject"]
        if topic == INPUT_TOPIC:
            return True
    except Exception as e:
        logging.error("Topic could not be parsed. " + repr(e))
    return False


def get_input_metrics(event):
    metrics = event["metrics"]
    return metrics


def get_sensor_data(command):
    ser.write(command)
    time.sleep(1)
    res = ser.read_all()
    sensor_value = int(res.hex()[22:26], 16) / 10
    return sensor_value


def get_value_by_metrics(metrics):
    if metrics.lower() == "temperature":
        return get_sensor_data(TEMPERATURE_CMD)
    elif metrics.lower() == "humidity":
        return get_sensor_data(HUMIDITY_CMD)
    else:
        return ""


def publish_aws_iot_topic(topic, data):
    mqtt_conn = greengrasssdk.client("iot-data")
    mqtt_conn.publish(
        topic=topic,
        queueFullPolicy="AllOrException",
        payload=data,
    )


def lambda_handler(event, context):
    try:
        if is_valid_input_topic(context):
            metrics = get_input_metrics(event)
            logger.warn(f"metrics -> {metrics}")
            value = get_value_by_metrics(metrics)
            logger.warn(f"value -> {value}")
            publish_aws_iot_topic(
                OUTPUT_TOPIC,
                json.dumps(
                    {
                        "metrics": metrics,
                        "value": value,
                    }
                ),
            )
    except Exception as e:
        logging.error(e)

エイリアスを切って保存します。

f:id:yomon8:20200908214818p:plain

GreenGrass設定

Lambda設定

先程作成したLambdaを設定します。

Lambdaにローカルリソースを指定します。

例えば /dev/ttyS0 を使ってシリアル通信する場合は以下のように設定します。

今回はGPIOも利用するので、 /dev/gpiomem も設定します。

サブスクリプション設定

IoT CloudからLambdaにコマンドを送り、Lambdaからの結果をIoT Cloudで受け取る設定にします。

f:id:yomon8:20200908224638p:plain

デプロイ

デプロイします。

f:id:yomon8:20200908224736p:plain

動かしてみる

test/get_value トピックををサブスクライブしながら、 test/command トピックに以下のようなPayloadを発行してみます。

{
  "metrics": "temperature"
}

metricsに temperature を指定すれば温度、humidity を指定すれば湿度が返ってきました。想定した通り動いていそうです。

f:id:yomon8:20200908223039p:plain

プロトコルアダプター

Modbus-RTU用のプロトコルアダプタが準備されているので、それを使うこともできます。

f:id:yomon8:20200909000427p:plain

docs.aws.amazon.com