Azure連携(5):Azure FunctionsからデバイスへのMQTT送信(C#)

<< 前の記事:「Azure連携(4):IoT Hub受信データをAzure Functionsで処理(C#)」
>> 次の記事:「Azure連携(6):クラウドからのMQTT通信を受信(Python)」 … <準備中>

この記事は PLCnext Control に限らず一般的なパソコン等にも適用可能です。

前回までは、エッジデバイスから送信されたデータを 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 による具体的なシステム構成」の図を再掲載

これは、Azure IoT Hub 上に登録されたデバイスが「エッジデバイス→クラウド」か「クラウド→エッジデバイス」のどちらかのリンクしか保持できないことから、双方向の通信を行いたい場合は通信方向毎にデバイスを登録しておく必要があるためです。

もし物理エッジデバイスが1つしか存在せず、その物理エッジデバイスがクラウドに対する送信と受信の両方を行うのだとしても、Azure IoT Hub 上のデバイスの登録は最低限二つ存在しなければなりません。

クラウドに対する送信・受信用デバイスがAzure IoT Hub 上に別々に登録されている場合、その通信先が物理的に同一のエッジデバイスであっても、送受信は問題なく行われます。

一方、Azure IoT Hub 上のデバイスの登録が一つだけの場合、そこで送信用・受信用両方のリンクを管理する必要がありますが、Azure IoT Hub はそのような作りになっていません。

そのため、例えばクラウド→エッジデバイスへの通信が行われると、エッジデバイス→クラウド側のリンクが切れてクラウドへのデータアップロードが出来なくなり、意図した動作にはなりません。

基本編:http トリガー関数でクラウドから単純な文字列データ送信

まずは最もシンプルな形で、単純な文字列データをクラウドからエッジデバイスへ向けて送信するプログラムを、http トリガー関数の形で作成してみましょう。

当記事では、クラウドから送信されたデータを PC 上の Azure CLI ツールの機能を用いて確認します。そのため、まだ専用の受信用エッジデバイスを用意する必要はありません。
受信用エッジデバイス側での受信プログラムの書き方は、次回の記事でご紹介します。

http トリガー関数のひな形作成

前回記事内の「簡単なHTTPトリガー関数を作成」項目を参照して、http トリガー関数を作成してください。
(「ローカルでの動作確認」項目やそれ以降の手順は、まだ行わなくて大丈夫です。)

Microsoft.Azure.Devices ライブラリをプロジェクトへ追加

作成した関数内からMQTTによる送信を行うには、Microsoft.Azure.Devices ライブラリが必要です。

Visual Studio Code 上で NuGet を用いてライブラリをプロジェクトへ追加してください。
(Visual Studio Code へ NuGet をインストール方法については、前回記事の「拡張機能 Nuget Package Manager のインストール」項目をご参照ください。)

次の手順で MQTT 送信に関するコードを HttpTrigger1 クラス内に追加します。

クラウドからの MQTT 送信に必要な手順
  • IoT Hub サービスへの接続文字列を取得
  • サービスへの接続とMQTT送信の処理を追加
  • デバイスを指定して文字列送信
MQTT送信に必要な処理を追加された HttpTrigger1 のソースコードのイメージ

IoT Hub サービスへの接続文字列を取得

クラウドからの MQTT 送信では、IoT Hub サービスへのサービス接続を介して MQTT による送信を実行します。

そのためにまず、IoT Hub サービスへのプライマリ接続文字列を取得します。

以前の記事 (Azure連携(2):デバイスからIoT HubへのMQTT送信(Python)) では、エッジデバイスからクラウドへ MQTT 送信を行う際にデバイスのプライマリ接続文字列を使用していました。
一方、今回のクラウドからエッジデバイスへのMQTT送信では、サービスのプライマリ接続文字列を使用します。
この二つは目的が異なる物なので注意してください。
接続文字列の種類が違うため、使用するライブラリも呼び出すメソッドも異なります。

サービスへの接続とMQTT送信の処理を追加

STEP
using の追加

追加で使用するライブラリなどの名前空間を以下の様に using で導入してください。System.Text は文字列処理の部分で Encoding を使用するため導入しています。

using Microsoft.Azure.Devices;
using System.Text;
using System.Threading.Tasks; // コード内に見当たらない場合は追加してください。
STEP
変数の追加

HttpTrigger1 クラス内に以下のメンバ変数を追加してください。これらは IoT Hub サービスへの接続を仲介するのに必要です。

// デバイスへのメッセージ送信用オブジェクト
static ServiceClient serviceClient;
// IoT-Hub の共有アクセスポリシーから取得したサービス用プライマリ接続文字列を "***" の部分へ記述してください。
static string serviceConnectionString = "*********************";
STEP
メソッドの追加

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 を用いた受信により確認してみましょう。

STEP
ログイン:az login

まず、「クラウド受信データのモニタツール:Azure CLI」の記事の「az コマンドによるログイン」の項目を参考に、PCのコマンドライン上で az login コマンドを用いて Azure へログインしてください。

STEP
クラウドからデバイスへデータを送信

前述の「プログラムを実行」の項目を参考に http トリガー関数を実行し、クラウドからデバイスへデータを送信してください。

STEP
デバイスとしてデータ受信:az iot device c2d-message receive …

コマンドライン上で以下の様にコマンドを入力してください。青線部は、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 receiveAzure CLI の az コマンドにより、クラウドからデバイスへ送られたメッセージをデバイスとして受信します。
–hub-nameIot Hub の名前を指定します。(上記コマンド例では IoTHub-article)
–device-idメッセージを受信側のデバイス ID を指定します。(上記コマンド例では axc_f_2152_dwn_article)
コマンドの解説

クラウドから送信されたデータを受信できていれば、下記のように表示されます。

Azure CLI で受信確認

このコマンドは、エッジデバイスのふりをしてクラウドからのデータを受信する機能です。
受信データがなければ何も表示されないまま数秒で自動終了します。受信データがある場合は、データを一つ表示したら終了します。複数のデータが送られている場合は、このコマンドを繰り返し実行することで、すべて受信しきることができます。
もし連続的にデータを受信したい場合は az iot device simulate コマンドを使うことも出来るので、azure の情報サイト等で調べて見てください。

az iot device c2d-message receive や az iot device simulate コマンドはあくまでもテスト用に提供されているツールなので、実用向けではありません。
実用的な受信デバイス側のプログラムコード (Python) は次回の記事で紹介予定です。

応用編:エッジデバイスからの受信データ処理結果をエッジデバイスへ送信

最後に実用的な例として、エッジデバイスからクラウドへ送られたデータをクラウド上で処理し、結果を別のエッジデバイスへ送信する例を紹介します。

今度は固定の文字列を送信するのではなく、クラウドで受信したセンサ値と警報判定用閾値を比較し、警報発報が必要であるか否かの判断結果を json にまとめたうえでエッジデバイスへ向けて送信するようにしてみましょう。

クラウドからデータを送信する先のエッジデバイスは、物理的に同じデバイスであってもかまいません。
ただし、前述の「デバイスを追加登録する理由」でご紹介した通り、IoT Hub 上にはデータ送信・受信それぞれ用に別々のデバイス登録を行っておく必要があります。
物理的に同一のエッジデバイスから、IoT Hub 上に登録されたデータ送信用デバイスと受信用デバイスに適切にアクセスることは全く問題ありません。

前回記事の CosmodDB トリガー関数を改造

前回の記事「Azure連携(4):IoT Hub受信データをAzure Functionsで処理(C#)」で作成したプロジェクトを用意して下さい。今回はこれを改造します。

前回記事の CosmosDB トリガー関数の改造イメージ

Microsoft.Azure.Devices ライブラリをプロジェクトへ追加

前述の「Microsoft.Azure.Devices ライブラリをプロジェクトへ追加」と同じ手順でライブラリをプロジェクトへ追加してください。

サービスへの接続とMQTT送信の処理を追加

前述の http トリガー関数における「サービスへの接続とMQTT送信の処理を追加」と同じ手順で using、変数、メソッドを追加してください。

警報判定処理を追加

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 を用いて受信確認します。

STEP
エッジデバイスからクラウドへのデータ送信

前回記事の「最終動作確認」の項目を参照して CosmosDBトリガー関数のデプロイ (クラウドへの配置) と動作チェックを行い、Azure ポータル上のログに想定通りの値が出ることを確認してください。

エッジデバイスからクラウドへデータを送信
Azure ポータルのログで CosmosDB トリガ関数の動作を確認
STEP
CLI でクラウドから送信されたデータを受信

前述の「CLI でクラウドから送信されたデータを受信」と同様の手順で、クラウドからのデータを受信してください。

正しいデータをクラウドから受信できていることを確認

このようにして、エッジデバイス -> クラウド -> エッジデバイス の間を MQTT 通信で連携させることができます。

<< 前の記事:「Azure連携(4):IoT Hub受信データをAzure Functionsで処理(C#)」
>> 次の記事:「Azure連携(6):クラウドからのMQTT通信を受信(Python)」 … <準備中>

  • URLをコピーしました!
目次