前回までは、エッジデバイスから送信されたデータを Azure のクラウド側で受信して処理するところまでを解説しました。
クラウド上で処理したデータをエッジデバイスに対して送信することが出来れば、エッジデバイス←→クラウド←→別のエッジデバイス間で自在に処理結果の交換が出来るようになります。
今回は、Azure のクラウド側からエッジデバイスへ向けて MQTT でデータを送信する方法を紹介します。
- Azure IoT Hub へデータ受信側デバイスを登録
- 基本編:http トリガ関数でクラウドから単純な文字列データ送信
- 応用編:前回記事のCosmosDBトリガー関数を改造(エッジデバイス→クラウド→別のエッジデバイスでのデータ交換を実現)
Azure IoT Hub へデータ受信側デバイスを登録
デバイスの(追加)登録
クラウドか送られたデータを受信する側となるデバイスを、Azure IoT Hub へ登録します。
手順は、以下のリンクを参考にしてください。
[リンク] PLCnext コントローラ-クラウド間データ転送実験の準備:Azure IoT Hubの設定
もし「Azure連携(2):デバイスからIoT HubへのMQTT送信(Python)」の「Azure アカウントの作成と IoT Hub サービスの設定 (デバイス登録)」の手順を実行済で、クラウドへデータを送信するためのデバイスを Azure IoT Hub 上へ登録済だったとしても、それとは別に新しいデバイスを追加で登録する必要があります。
Azure IoT Hub 上に登録されるデバイスの情報に「送信用」「受信用」といった属性があるわけではありません。登録したデバイスをどちらの用途に使うかは、その後のプログラミング次第です。
デバイスを追加登録する理由
ここで、なぜ改めてデータ受信側のデバイスを追加で登録する必要があるのかについて疑問に思われるかも知れません。前回までの記事では、クラウドへデータを送信するためのデバイスを IoT Hub に登録済みでした。そのため、クラウドから送られたデータを受信するエッジデバイスが送信用デバイスと同じ個体であるなら、その登録済みデバイスに対してデータを送れば良いと考えられるからです。
確かにそのようにプログラムを組んで動作させることは問題なく出来ますが、注意点があるため、「Azure連携(1):クラウド-デバイス間のMQTTによるデータ交換」の記事で使用した「Azure による具体的なシステム構成」の図を使って説明します。
図では Azure IoT Hub サービスへデータを送信する「センサデータ送信用デバイス」と Azure IoT Hub サービスからデータを受信する「警報判定結果受信用デバイス」が、Azure IoT Hub サービス上に別々に登録されており、それぞれが別個体の物理的なエッジデバイスに対応しています。
これは、Azure IoT Hub 上のデバイスに接続できる MQTT Client は 1 つだけであり、複数の MQTT Client が接続を行った場合は後から接続された Client が優先されるというルールがあるためです。
上記の図の例の場合、「センサデータ送信用デバイス」に対して接続を行っている MQTT Client は「データ送信用」PLCnext Control のみであり、「警報判定結果受信用デバイス」に対して接続を行っている MQTT Client は「データ送信用」PLCnext Control のみであるため、問題なく送受信処理を行うことが出来ます。
当記事では、なるべく単純な形で技術をご紹介する目的で Azure IoT Hub 上のデバイスを複数個作成しています。
もし物理エッジデバイスが1つしか存在せず、その物理エッジデバイスがクラウドに対する送信と受信の両方を行う場合、プログラミング上の注意が必要です。
Azure IoT Hub 上のデバイスに対する接続が1個であり、それを用いて送信・受信を行う場合、問題はありません。
一つの物理エッジデバイスの中でAzure IoT Hub 上のデバイスに対する接続をそれぞれ一つずつ作成した場合も、問題ありません。
一方、Azure IoT Hub 上のデバイスに対して同時に二つ以上の接続を作成すると、意図した通りには動作しません。
物理エッジデバイスが複数個存在し、それらが Azure IoT Hub 上の同一のデバイスへ接続を行った場合も、同様の不具合が発生します。
基本編:http トリガー関数でクラウドから単純な文字列データ送信
まずは最もシンプルな形で、単純な文字列データをクラウドからエッジデバイスへ向けて送信するプログラムを、http トリガー関数の形で作成してみましょう。
http トリガー関数のひな形作成
前回記事内の「簡単なHTTPトリガー関数を作成」項目を参照して、http トリガー関数を作成してください。
(「ローカルでの動作確認」項目やそれ以降の手順は、まだ行わなくて大丈夫です。)
Microsoft.Azure.Devices ライブラリをプロジェクトへ追加
作成した関数内からMQTTによる送信を行うには、Microsoft.Azure.Devices ライブラリが必要です。
Visual Studio Code 上で NuGet を用いてライブラリをプロジェクトへ追加してください。
(Visual Studio Code へ NuGet をインストール方法については、前回記事の「拡張機能 Nuget Package Manager のインストール」項目をご参照ください。)
MQTT送信に関するコードの追加
次の手順で MQTT 送信に関するコードを HttpTrigger1 クラス内に追加します。
- IoT Hub サービスへの接続文字列を取得
- サービスへの接続とMQTT送信の処理を追加
- デバイスを指定して文字列送信
IoT Hub サービスへの接続文字列を取得
クラウドからの MQTT 送信では、IoT Hub サービスへのサービス接続を介して MQTT による送信を実行します。
そのためにまず、IoT Hub サービスへのプライマリ接続文字列を取得します。
以前の記事 (Azure連携(2):デバイスからIoT HubへのMQTT送信(Python)) では、エッジデバイスからクラウドへ MQTT 送信を行う際にデバイスのプライマリ接続文字列を使用していました。
一方、今回のクラウドからエッジデバイスへのMQTT送信では、サービスのプライマリ接続文字列を使用します。
この二つは目的が異なる物なので注意してください。
接続文字列の種類が違うため、使用するライブラリも呼び出すメソッドも異なります。
サービスへの接続とMQTT送信の処理を追加
追加で使用するライブラリなどの名前空間を以下の様に using で導入してください。System.Text は文字列処理の部分で Encoding を使用するため導入しています。
using Microsoft.Azure.Devices;
using System.Text;
using System.Threading.Tasks; // コード内に見当たらない場合は追加してください。
HttpTrigger1 クラス内に以下のメンバ変数を追加してください。これらは IoT Hub サービスへの接続を仲介するのに必要です。
// デバイスへのメッセージ送信用オブジェクト
static ServiceClient serviceClient;
// IoT-Hub の共有アクセスポリシーから取得したサービス用プライマリ接続文字列を "***" の部分へ記述してください。
static string serviceConnectionString = "*********************";
HttpTrigger1 クラス内に以下のメンバメソッドを追加してください。上記の変数を使用して IoT Hub サービスへの接続を行い、指定したデバイスへMQTTでメッセージを送信するところまでの処理をメソッド化しています。
private static async Task SendMessageToDevice(string devname, string msg, ILogger log)
{
try
{
serviceClient = ServiceClient.CreateFromConnectionString(serviceConnectionString);
SendCloudToDeviceMessageAsync(devname, msg).Wait();
}
catch (Exception e)
{
log.LogInformation("Connect Error!![" + e.StackTrace + "]\n");
}
return;
}
private async static Task SendCloudToDeviceMessageAsync(string hostname, string msg)
{
var commandMessage = new Message(Encoding.ASCII.GetBytes(msg));
await serviceClient.SendAsync(hostname, commandMessage);
}
デバイスを指定して文字列送信
上述の追加したメソッド SendMessageToDevice を呼び出すことで、指定デバイスへ MQTT でメッセージを送信することができます。
HttpTrigger1 クラスの Run メソッド内にて、return 文直前に以下を追加してください。
SendMessageToDevice の第1引数には今回 IoT Hub 上に作成したクラウドからのデータ受信用のデバイス名を指定してください。
第2引数はデバイスへ送信する任意の文字列です。
// 電文を C2D で送信する。
await SendMessageToDevice("axc_f_2152_dwn_article", "Test String from Azure Cloud.", log);
動作確認
デプロイ
前回記事内の「関数のデプロイ」項目を参照して、http トリガー関数をデプロイ (クラウドへ配置)してください。
プログラムを実行
クラウド上の http トリガー関数を実行すると、出力されたログを前回記事内の「ログ出力の確認」項目と同じ手順で確認することが出来ます。
なお、http トリガー関数は以下のいずれの方法からでも実行可能です。
- ブラウザから URL を指定して実行
-
前回記事内の「クラウド上の関数を実行」項目と同じ手順でクラウド上の URL へアクセスすれば、http トリガー関数を実行することが出来ます。
- Azure ポータル 上での操作から実行
-
また、以下の様に Azure ポータル上の関数アプリの画面から実行を指示することも出来ます。
CLI でクラウドから送信されたデータを受信
クラウドから実際に送信されたデータを、Azure CLI を用いた受信により確認してみましょう。
まず、「クラウド受信データのモニタツール:Azure CLI」の記事の「az コマンドによるログイン」の項目を参考に、PCのコマンドライン上で az login コマンドを用いて Azure へログインしてください。
コマンドライン上で以下の様にコマンドを入力してください。青線部は、Azure IoT Hub へ設定した内容に依存します。
az iot device c2d-message receive --hub-name IoTHub-article --device-id axc_f_2152_dwn_article
項目, 引数 | 説明 |
---|---|
az iot device c2d-message receive | Azure CLI の az コマンドにより、クラウドからデバイスへ送られたメッセージをデバイスとして受信します。 |
–hub-name | Iot Hub の名前を指定します。(上記コマンド例では IoTHub-article) |
–device-id | メッセージを受信側のデバイス ID を指定します。(上記コマンド例では axc_f_2152_dwn_article) |
クラウドから送信されたデータを受信できていれば、下記のように表示されます。
応用編:エッジデバイスからの受信データ処理結果をエッジデバイスへ送信
最後に実用的な例として、エッジデバイスからクラウドへ送られたデータをクラウド上で処理し、結果を別のエッジデバイスへ送信する例を紹介します。
今度は固定の文字列を送信するのではなく、クラウドで受信したセンサ値と警報判定用閾値を比較し、警報発報が必要であるか否かの判断結果を json にまとめたうえでエッジデバイスへ向けて送信するようにしてみましょう。
前回記事の CosmodDB トリガー関数を改造
前回の記事「Azure連携(4):IoT Hub受信データをAzure Functionsで処理(C#)」で作成したプロジェクトを用意して下さい。今回はこれを改造します。
Microsoft.Azure.Devices ライブラリをプロジェクトへ追加
前述の「Microsoft.Azure.Devices ライブラリをプロジェクトへ追加」と同じ手順でライブラリをプロジェクトへ追加してください。
サービスへの接続とMQTT送信の処理を追加
前述の http トリガー関数における「サービスへの接続とMQTT送信の処理を追加」と同じ手順で using、変数、メソッドを追加してください。
送信するデータの種類
ここでは、「Azure連携(2):デバイスからIoT HubへのMQTT送信(Python)」の記事で紹介した、PLCnext Control から Azure へ向けて送信したデータを処理することを考えてみます。
Azure に送信された具体的な内容は、同記事の「送信するデータの種類」の項目をご参照ください。
Azure 側ではその内容を用いて演算を行い、以下のデータを JSON 形式で Azure からエッジデバイスへ向けて送信することとします。
JSON キー名 | 説明 |
---|---|
TEMP_WARN_LVL | 温度警報判定値。 Azure 側で受信したセンサ値 TEMPERATURE と警報閾値 TEMP_WARN_H, TEMP_WARN_L の値を比較し、 TEMPERATURE < TEMP_WARN_L のときは 0 (低温警報) TEMPERATURE >= TEMP_WARN_H のときは 2 (高温警報) それ以外は 1 (正常温度) の値を取ります。 |
HUM_WARN_LVL | 湿度警報判定値。 Azure 側で受信したセンサ値 HUMIDITY と警報閾値 HUM_WARN_H, HUM_WARN_Lの値を比較し、 HUMIDITY< HUM_WARN_L のときは 0 (乾燥警報) HUMIDITY>= HUM_WARN_H のときは 2 (高湿度警報) それ以外は 1 (正常湿度) の値を取ります。 |
PWR_WARN_LVL | 湿度警報判定値。 Azure 側で受信したセンサ値 POWER と警報閾値 PWR_WARN_H, PWR_WARN_Lの値を比較し、 POWER < PWR_WARN_L のときは 0 (低電力警報) POWER >= PWR_WARN_H のときは 2 (高電力警報) それ以外は 1 (正常電力) の値を取ります。 |
実際に送信される JSON 文字列は以下の様になります。
{"TEMP_WARN_LVL": 0, "HUM_WARN_LVL": 0, "PWR_WARN_LVL": 2}
警報判定処理を追加
上述した条件判定処理を具体的に作成しましょう。
CosmosDBTrigger 関数内の各種センサ値の取得とログ表示を行っている処理の直後に、以下の警報判定処理を追加してください。
温度、湿度、消費電力それぞれについて、閾値外の値になると警報判定値用の変数に警報状態が反映されます。
// 警報種類:0 = 値が低い側の警報発報, 1 = 正常, 2 = 値が高い側の警報発報
// 温度
int temp_warn_lvl = 1; // 温度警報判定値
if (temp_val < temp_thr_l) temp_warn_lvl = 0; // 低温警報
if (temp_val >= temp_thr_h) temp_warn_lvl = 2; // 高温警報
// 湿度
int hum_warn_LVL = 1; // 湿度警報判定値
if (hum_val < hum_thr_l) hum_warn_LVL = 0; // 乾燥警報
if (hum_val >= hum_thr_h) hum_warn_LVL = 2; // 高湿度警報
// 消費電力
int pwr_warn_LVL = 1; // 電力警報判定値
if (pwr_val < pwr_thr_l) pwr_warn_LVL = 0; // 低電力警報 (想定より消費電力が低い:故障による機器停止の疑いなど)
if (pwr_val >= pwr_thr_h) pwr_warn_LVL = 2; // 高電力警報 (想定より消費電力が高い:機器故障や高負荷の疑いなど)
送信用 json 文字列の作成と送信
先ほどの警報判定処理の直後に、以下の処理を追加してください。
先ほどの警報判定値を反映した json 形式の文字列を sendmsg_json 変数として作成し、それをエッジデバイスへ MQTT で送信しています。
// 送信用 json 文字列の作成
string sendmsg_json = $"{{\"TEMP_WARN_LVL\": {temp_warn_lvl}, \"HUM_WARN_LVL\": {hum_warn_LVL}, \"PWR_WARN_LVL\": {pwr_warn_LVL}}}";
log.LogInformation($"message : {sendmsg_json}");
// 送信用 json 文字列を C2D で送信
await SendMessageToDevice("axc_f_2152_dwn_article", sendmsg_json, log);
SendMessageToDevice の第1引数には今回 IoT Hub 上に作成したクラウドからのデータ受信用のデバイス名を指定してください。
第2引数はデバイスへ送信する文字列として、直前で作成した sendmsg_json 変数を指定しています。
CosmosDB トリガーの関数の場合、MQTT 送信用メソッド周りでメソッドの同期/非同期に関するエラーが出ることがあります。
その場合は下記のように、C# for Visual Studio Code のクイックフィックス機能を用いて自動的にエラーを修正することが出来ます。
動作チェック
前回記事と同じ手順でエッジデバイスからクラウドへデータを送信し、今回はさらにクラウドからのデータを Azure CLI を用いて受信確認します。
前回記事の「最終動作確認」の項目を参照して CosmosDBトリガー関数のデプロイ (クラウドへの配置) と動作チェックを行い、Azure ポータル上のログに想定通りの値が出ることを確認してください。
このようにして、エッジデバイス -> クラウド -> エッジデバイス の間を MQTT 通信で連携させることができます。