節約プログラマー雑記

Azure IoT HubへのRaspberry Piでの送受信

今年の5月ごろですが、GoogleでIoT Coreが使えなくなるというアナウンスがあり、急遽自宅で使っているRaspberry Piの接続先のIoTを別システムへ移行することの検討することに迫られました。。

そこで、パブリックで使えるMQTTやIoTのサービスを検討したところ、AzureのIoT Hubが無料で使えたので、こちらに移行することにしました。(HerokuもMQTTサービスあったのですが、有料化されてしまったため、やむなく断念してました。AWSの方はなんとなく除外してました。)

今回は、その際に使ったAzure IoTとクライアント側の設定について、書いていこうと思います。

Azure側の準備

ということで、まずはAzure側の準備。Azure側で必要な作業は、接続先の「1. IoT Hubの作成」と「2. 接続デバイスの登録」の2点です。まずは1点目。

IoT Hubの作成

メニュー画面からIoT Hubを選択して、作成を実施します。登録内容は主に、任意で問題ないですが、レベルを無料にすることだけは必須です。これをしないと最低でも、月10ドル掛かってしまうので注意が必要です。設定画面としては、次のような感じで作成します。

IoTHub001.png IoTHub002.png

接続デバイスの登録

IoT Hubの登録ができたら、次はIoT Hubに接続するデバイスを登録します。デバイスは、作成したIoT Hubごとに登録するので、先ほど作成したIoT Hubを選択します。IoT Hubを選択後、デバイスの項目をクリックし、デバイスの追加をクリックして、実行します。

デバイスの登録画面に移動したら、大半の項目は初期値のままでも特に問題ないので、デバイスIDだけ任意の設定をしたら、保存を押してデバイスの登録を完了します。下の画像のように、デバイスの登録が終わったら、クライアントがIoT Hubに繋ぐための準備が一通り完了になります。

IoTHub003.png

クライアント側の準備

Azure側の作業が終わったら、次は接続を行うクライアント側の設定になります。IoT Hubへの接続は、MQTTなどの純粋なプロトコルでもつなげますが、Azure側で言語に応じたライブラリも用意されているので、今回はPythonを使った接続で進めていきたいと思います。

(公式サイトにも参考ページが用意されており、こちらを見ながら進めています。)

ライブラリのインストール

ということで、まずはライブラリのインストール。次のコマンドでPythonで使うために必要なライブラリをRaspberry Piにインストールします。


pip install azure-iot-device

認証情報の確認・接続用スクリプトの作成

次は、アプリから、IoTHubに繋ぐためのスクリプトの準備です。IoTHubに繋ぐためには、デバイスごとの認証情報が必要となるため、まずはそれを確認します。デバイスの認証情報は先ほど作成したAzure IoTHubのデバイスから確認できるので、以下の画面を開き、赤枠部分の内容を確認・コピーします。

認証情報をコピーしたら、以下のサンプルアプリの「接続文字列」の部分に貼り付けて準備完了です。

(サンプルアプリは、Microsoftのgit-hubで公開されているものに若干手を加えたものになっています。)

IoTHub004.png
サンプルアプリ

import os
import random
import time
from azure.iot.device import IoTHubDeviceClient, Message


#ここをプライマリ接続文字列の値に変える
CONNECTION_STRING = "接続文字列"

# Define the JSON message to send to IoT Hub.
TEMPERATURE = 20.0
HUMIDITY = 60
MSG_TXT = '{{"temperature": {temperature},"humidity": {humidity}}}'
RECIEVED_MESSAGE = 0

#受信時の処理
def message_handler(message):
    global RECIEVED_MESSAGE
    global COMMANDS
    RECIEVED_MESSAGE += 1
    print("")
    print("Message recieved")
    for property in vars(message).items():
        print("   {}".format(property))

    # Handling Message
    try:
        msg = message.data.decode('utf-8')
        print(msg)
    except Exception as e:
        print(e)

#送信処理
def run_telemetry_sample(client):
    print("IoT Hub device sending periodic messages")

    client.connect()

    while True:
        # Build the message with simulated telemetry values.
        temperature = TEMPERATURE + (random.random() * 15)
        humidity = HUMIDITY + (random.random() * 20)
        msg_txt_formatted = MSG_TXT.format(temperature=temperature, humidity=humidity)
        message = Message(msg_txt_formatted)

        # Add a custom application property to the message.
        # An IoT hub can filter on these properties without access to the message body.
        if temperature > 30:
            message.custom_properties["temperatureAlert"] = "true"
        else:
            message.custom_properties["temperatureAlert"] = "false"

        # Send the message.
        print("Sending message: {}".format(message))
        client.send_message(message)
        print("Message successfully sent")
        time.sleep(10)


def main():
    print("IoT Hub Quickstart #1 - Simulated device")
    print("Press Ctrl-C to exit")

    # Instantiate the client. Use the same instance of the client for the duration of
    # your application
    client = IoTHubDeviceClient.create_from_connection_string(CONNECTION_STRING)

    # Run Sample
    try:
        #受信時の処理の登録
        client.on_message_received = message_handler
        run_telemetry_sample(client)
    except KeyboardInterrupt:
        print("IoTHubClient sample stopped by user")
    finally:
        # Upon application exit, shut down the client
        print("Shutting down IoTHubClient")
        client.shutdown()

if __name__ == '__main__':
    main()

通信の実施

準備ができたら、実際に上のスクリプトを動かします。接続情報に問題がなければ、以下の送信結果のように、IoTHubに対して、ランダムな値の気温、湿度の情報を送信してくれます。

逆にIoTHub側から、クライアント側に送信をすると、受信結果のようにその結果をクライアント側でメッセージを確認することができました。(IoTHubのデバイス画面から簡単にメッセージを送ることができるので、そこからメッセージを送って試しています。)

こんな感じで、IoT Hubでの接続を試して、無事自宅のIoT環境をAzureに移行することができました。

IoTHub005.png

(補足).Raspberry Piで詰まったこと

今回、私はクライアントをRaspberry Pi 3Bで繋いだのですが、OSがBusterだからか、Pythonで接続アプリを起動しようとしたとき、次の2点ではまりました。

  1. OpenSSLのバージョンアップ
  2. 信頼する認証局の証明書ファイルの変更

1点目に関しては、Pythonのライブラリの一部がOpenSSLのバージョンが「1.1.1」以上である必要があるらしく、通常インストールされているOpenSSLでは処理が失敗してしまいました。そのため、OpenSSLのモジュールをダウンロードし、ビルド・インストールを実行する必要があります。

2点目に関しては、Azureへの接続時に起こっているようで、作成したサンプルアプリで接続しても、sslエラーが吐かれて、接続に失敗すると言うものでした。これもOpenSSLの設定である、「信頼する認証局の証明書」の一覧ファイルが古いままであることが原因みたいでしたので、「環境変数SSL_CERT_FILE」に対して、Pythonの「certifi」ライブラリの証明書を指定するように設定しました。

上記2点の設定で、Raspberry Piからも無事Pythonにて、Azure IoTに繋ぐことができました。