Azure IoT Hubとエッジデバイスの間でメッセージを送受信する

# はじめに
AzureのIoTデバイスにエッジデバイスを登録して、Azure IoT Hubとエッジデバイスの間でメッセージを送受信する方法を紹介します。
エッジデバイスにランタイムをインストールすることなく、手軽にメッセージの送受信を行うことができます。
（エッジデバイスにIoTEdgeランタイムをインストールしてAzureと接続する方法は[こちら](https://qiita.com/Yokogawa_Mita/items/f7996d539b29618d7159)をご覧ください。）

# 環境
動作確認済デバイス（OS）

- e-RT3 Plus F3RP70-2L[^ert3]（Ubuntu 18.04 32bit）
- Raspberry Pi 4 Model B （Ubuntu Server 20.04 32bit）

これらのデバイスでは armhf アーキテクチャのパッケージが動作します。

# エッジデバイスのクラウドへの登録
## IoT Hubの作成
AzureにサインインしてIoT Hubを作成します。
手順は[こちらの記事の「IoT Hubの作成」](https://qiita.com/Yokogawa_Mita/items/f7996d539b29618d7159#iot-hub%E3%81%AE%E4%BD%9C%E6%88%90)をご覧ください。

## エッジデバイスの登録
作成したIoT Hub →「IoT デバイス」→「新規」をクリックします。
![az-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682603/d10017bc-2924-18dc-cb15-8707125a963e.png)


任意のデバイス登録名を入力し、「保存」をクリックします。
![az-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682603/27f285e5-3fb7-4236-66b4-e5d761d39b99.png)


登録したデバイスが一覧に表示されます。クリックして接続文字列をコピーして控えておきます。
![az-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682603/949d6bb1-2f47-c03c-8fe3-3be5dee696d0.png)


# メッセージの送受信
## エッジデバイスの準備
バージョン3.7以降のpythonが必要です。
python3.7のインストール方法は[以前の記事の「Python3.7のインストール」](https://qiita.com/Yokogawa_Ishino/items/12426577573b2bbb47eb#python37%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)をご覧ください。

venvをインストールします。

```bash
sudo apt install python3.7-venv
```

>**Note**
>エッジデバイスがproxy環境下にある場合は[proxy設定](https://qiita.com/Yokogawa_Mita/items/f7996d539b29618d7159#proxy%E8%A8%AD%E5%AE%9A)が必要です。

仮想環境を作成します。

```bash
python3.7 -m venv venv
source venv/bin/activate
```

必要なパッケージをインストールします。

```bash
(venv) username@ubuntu:~$ pip install wheel
(venv) username@ubuntu:~$ pip install azure-iot-device
```

Azure公式のSDKのサンプルプログラムである[simple_send_message.py](https://github.com/Azure/azure-iot-sdk-python/blob/2021-06-18/azure-iot-device/samples/simple_send_message.py)と[receive_message.py](https://github.com/Azure/azure-iot-sdk-python/blob/2021-06-18/azure-iot-device/samples/sync-samples/receive_message.py)をデバイスの任意の場所に保存します（ライセンスは[こちら](https://github.com/Azure/azure-iot-sdk-python/blob/2021-06-18/LICENSE)）。

> **Note**
> 本記事ではタグ「2021-6-18」のコードを使用しています。

> **Note**
> エッジデバイスがproxy環境下にある場合はコードの変更が必要です。詳しくは[proxy設定](#proxy設定)をご覧ください。
> 

エッジデバイスの環境変数に、[エッジデバイスの登録](#エッジデバイスの登録)で控えておいた接続文字列を設定します。

```bash
export IOTHUB_DEVICE_CONNECTION_STRING="<YOUR_DEVICE_CONNECTION_STRING>"
```

## D2C (Device-to-Cloud)メッセージのやり取り
エッジデバイスからクラウドにメッセージを送信して、クラウド上でメッセージを確認します。

Azure Cloud Shellを開きます。
![az-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682603/963f4098-6476-2766-acf1-ef37450fd066.png)

**Azure Cloud Shellで**以下のコマンドを実行してメッセージの到着を待ちます。`{YOUR_HUB_NAME}`は[IoT Hubの作成](#iot-hubの作成)で作成したIoT Hub名で置き換えてください。

```bash
az iot hub monitor-events --hub-name {YOUR_HUB_NAME} --output table
```

>**Note**
>`az iot`コマンドが使用できない場合は以下のコマンドで拡張機能を追加します。
>
> ```
> az extension add --name azure-iot
> ```


**エッジデバイスで**プログラムを実行してクラウドにメッセージを送信します。

```bash
(venv) username@ubuntu:~$ python3.7 simple_send_message.py
Sending message...
Message successfully sent!
```

エッジデバイスからメッセージを受信すると、**Azure Cloud Shellに**以下のようなメッセージが表示されます。

```bash
event:
  component: ''
  interface: ''
  module: ''
  origin: e-RT3-demo
  payload: This is a message that is being sent
```


## C2D (Cloud-to-Device)メッセージのやり取り
**エッジデバイスで**プログラムを起動してメッセージの到着を待ちます。

```bash
(venv) username@ubuntu:~$ python3.7 receive_message.py
Press Q to quit
```

**Azure Cloud Shellで**以下のコマンドを実行して、クラウドからエッジデバイスにメッセージを送ります。`{YOUR_DEVICE_NAME}`は[エッジデバイスの登録](#エッジデバイスの登録)で登録したデバイス名で、`{YOUR_HUB_NAME}`は[IoT Hubの作成](#iot-hubの作成)で作成したIoT Hub名で置き換えてください。

```bash
az iot device c2d-message send --device-id {YOUR_DEVICE_ID} --hub-name {YOUR_HUB_NAME} --data "This is a message from Azure"
```

クラウドからメッセージを受信すると、**エッジデバイスに**以下のようなメッセージが表示されます。

```bash
the data in the message received was
b'This is a message from Azure'
custom properties are
{}
```

# 補足
## proxy設定
エッジデバイスがproxy環境下にある場合はコードの変更が必要です。
環境により設定は異なりますが、一例として今回私が設定した方法を紹介します。

`simple_send_message.py`、`receive_message.py`ともに以下の2点を変更します。

- インポートするモジュールを追加します。プログラム上部のインポート部分に以下のコードを追加してください。

  ```bash
  from azure.iot.device import ProxyOptions
  import socks
  ```

- proxy設定を追加します。以下のようにコードを変更してください。
    
変更前

```bash
device_client = IoTHubDeviceClient.create_from_connection_string(conn_str)
```
変更後

```bash
proxy_opts = ProxyOptions(
    proxy_type=socks.HTTP,
    proxy_addr="YOUR_PROXY_ADDRESS",
    proxy_port=YOUR_PROXY_PORT,
    proxy_username="YOUR_PROXY_USERNAME",
    proxy_password="YOUR_PROXY_PASSWORD"
)
device_client = IoTHubDeviceClient.create_from_connection_string(conn_str, websockets=True, proxy_options=proxy_opts)
```

[^ert3]: [産業用AIプラットフォーム | 横河電機株式会社](https://www.yokogawa.co.jp/solutions/products-platforms/control-system/real-time-os-controller/ert3-ai-platform/)