はつねの日記

Kinect, Windows 10 UWP, Windows Azure, IoT, 電子工作

M5Stackで.NET nanoFrameworkからAzure IoT Hubに繋いでみた

前回

hatsune.hatenablog.jp
前回は、.NET nanoFrameworkでの開発準備とM5Stack Core 2に接続したセンサーから気圧、気温、湿度などを取得しました。
今回は、M5Stack Core 2からAzure IoT Hubに取得したセンサー値を送信してみたいと思います。

事前準備

センサーについて

M5Stack Core 2のPORT.A.I2C端子に接続して使用するENV-IIIユニットを使用します。

Install the nanoFramework firmware

M5Stackに使われているESP32にはnanoFrameworkは標準インストールされていないので、nanoFrameworkをインストールします。

接続ポート番号の確認

USBでM5Stack Core 2をPCに接続したら、デバイスマネージャーで「CP210x」が接続されているポート番号を確認します。今回の環境では「COM4」が該当しました。

Flasherアプリケーションのインストール(インストール済の場合は不要)

OSの[スタート]メニューから「Developer PowerShell for VS2022」を起動します。

dotnet tool install --global nanoff

nanoFrameworkfファームウェアをインストール(インストール済の場合は不要)

今回使用するM5Stackは、M5Stack Core 2になります。
このM5Stack Core 2をターゲットにして「COM4」経由でnanoFrameworkをアップロードします。

nanoff --target M5Core2 --update --serialport COM4

Device Explorerでの接続確認

nanoFrameworkファームウェアをインストールしたあとにVisual Studioを起動してDevice Explorerをみると、「M5Core2 @ COM4」と表示されるようになります。

nanoFrameworkファームウェアは、インストールしたあとにnanoFramework以外の別イメージをM5Stack Core 2に転送(Burn)してしまうと消えてしまうので、Device Explorerに表示がないような場合は、再度、インストールをしてからVisual Studioを起動すれば表示されるようになります。

Visual Studioの準備

Visual Studioを機能拡張する

Visual Studioには.NET nanoFramework Extensionをインストールして機能拡張しておきます。
この機能拡張によりnanoFrameworkアプリのテンプレートが追加されます。
このテンプレートで作成したプロジェクトには、NuGetから自動的に「nanoFramework.CoreLibrary」ライブラリが追加されています。

「nanoFramework.M5Core2」ライブラリを追加する

今回は、M5Stack Core 2を使うので、「nanoFramework.M5Core2」ライブラリを手動でNuGetから追加します。

ここまでが、M5Stack Core 2を使うアプリ開発を行う上での共通の事前準備となります。

Azureの準備

Azrue側のサービスは、Azure IoT Hubを使います。
Azure IoT Hubを使うことで、ゼロから設計および構築をしなくても「セキュリティが担保された通信」「スケーリング」「正常性監視」「可用性」を確保しつつデバイスからのセンサー値の収集が容易にできます。
また、運用自体も利用しているAzure全体の運用の中に組み込むことで、運用負荷が著しく増加するということもありません。

IoT Hubの使用プロトコルについて

Azure IoT Hubは、MQTT (Message Queueing Telemetry Transport)、Http、AMQP (Advanced Message Queuing Protocol)など様々なプロトコルに対応しています。
今回のサンプルでは、「小さい」「大量」のデータを「低遅延」で「双方向」に通信できるMQTTを使います。

Azure IoT Hubを準備するには

Azure IoT Hubを使用するためには、まずは、Azure IoT Hub自体を作成して、そこに接続するデバイスを定義していきます。

Azure IoT Hubを作成する

Azure IoT Hubの作成は、Azure管理ポータルでリソースを作成するだけで簡単にできます。
Azure管理ポータルにサインインしたら、[リソースの作成]で「IoT Hub」を検索し、[作成]をクリックしてリソースを追加します。

プランで無料を選択する

Azure IoT Hubの料金プラン (課金額) は、作成時や作成後に指定できます。無料プランは、IoT Hubでは「F1:Freeレベル」という表記になります。

スケーリングが1だったり、「Defender for IoT」も有効にできなかったりといくつかの制限はありますが、無料プランでも基本的な機能は備えていますので、動作確認や数台を接続して試験するような場合に便利です。

Azure IoT Hubにデバイスを定義する

IoT Hubが作成できたら、その中に「IoTデバイス」を定義します。

[IoTデバイスの追加]をクリックして、「デバイスID」「認証の種類」を選んで、[自動生成キー]にチェックを入れて追加をすれば、IoTデバイスからIoT Hubに接続するための接続文字列が生成されます。

M5StackでAzure送信

M5Stack Core 2をAzure IoT Hubに接続するには「nanoFramework.Azure.Devices.Client」ライブラリを使います。

依存関係にあるライブラリを確認してみると、MQTT関連のライブラリも含まれていますね。

Azure TLS証明書をM5Stack Core 2に登録

.NET nanoFrameworkでAzure IoT Hubに接続するためには、ルートCA証明書が必要になります。

ルートCA証明書の取得

ルートCA証明書は、https://docs.microsoft.com/ja-jp/azure/security/fundamentals/tls-certificate-changesの中から「Baltimore CyberTrust Root」をダウンロードして取得します。

ルートCA証明書の登録

取得したルートCA証明書は次のいずれかの方法で利用します。

  1. ソースコードに埋め込む
  2. リソースとしてバイナリに同封
  3. バイスにあらかじめアップロード

今回は「リソースとしてバイナリに同封」方法を採用します。
まず、プロジェクトにリソースファイルを追加します。

次にリソースファイルで[リソースの追加]-[既存ファイルの追加]でダウンロードしたルートCA証明書ファイルを追加します。

WiFiと接続

WiFiとの接続には、最寄りのアクセスポイントへの接続情報が必要です。
gist.github.com
WiFiNetworkHelper.ConnectDhcpメソッドでアクセスポイントのSSIDとパスワードを指定して接続が成功すれば、IPアドレスが割り当てられて、メソッドはTrueを返却してきます。
ここではWi-Fi接続が成功するまで、0.5秒間隔で自動リトライするようにしています。
例えば、アクセスポイントの電源がはいっていないなどであれば電源がはいれば自動的に接続が確立できます。

Azure IoT Hubと接続

gist.github.com

自動リトライについて

実際に動作させてみると、IoT Hubとの接続に失敗するケースがあります。
リトライで解消はできるのでサンプルコードのように自動再接続のロジックを入れておくのがよいでしょう。

ルートCA証明書について

事前にリソースとして組み込んでいるルートCA証明書を使ってX509Certificateを生成してから接続します。IoT Hubとの接続方式はSAS(対象キー)方式ですが、このようにルートCA証明書を使っての接続になります。
AzureポータルでDeviceを登録するときには「X509CA署名済」という認証方式もありますので、SAS方式の場合はルートCA証明書などは不要かと思ってしまうかもしれませんが、対象キー方式の時はデバイス側にルートCA証明書を入れておくと覚えておくとよいでしょう。

Azure IoT Hubへデータ送信

gist.github.com

Azure Iot Hubからデータ受信

gist.github.com

実機へのデプロイが失敗したときは

Visual Studioから実機へのデプロイは失敗します。対処方法はいろいろあります。

  1. Device Explorerの再表示(COMポート再スキャンが行われるので)
  2. M5Stack Core 2の電源ボタン長押しで強制終了と、電源ボタン再度クリックによる電源再投入
  3. M5BurnerによるCore2FactoryTestイメージの再投入(nanoFrameworkなども消えます)

改良が必要なポイント

ネットワークが途中で切断されてしまった場合

WiFi再接続、IoT Hub再接続などが必要ですが、本サンプルはそのあたりの対応が抜けています。
SendMessageの戻り値がfalseのときは、Client.IsConnectedプロパティを確認してfalseだったらネットワーク再接続を再実施するようなロジックが必要です。

バイスIDをAzrue IoT Hubへデバイス側から登録

今回のサンプルでは、デイバスIDをAzure IoT Hub側で登録して、それをコードの中に記述していました。
利用デバイスが1つだけであればそれでもいいのですが、複数のデバイスをサポートし、また、どのデバイスからのデータかを判断したいような場合は、

  1. コードでデバイス自体からユニークなデバイスIDを取得
  2. 取得したデバイスIDでAzrue IoT Hubにデバイスを登録

というような流れが必要です。

データ取得間隔の安定化

今回のサンプルでは、「Azure IoT Hubにデータ送信後10秒経過したらセンサー値を取得して再度送信」という時間間隔でした。
しかし、この方法ではデータ送信などが遅延した場合などにセンサーからのデータ取得時間がずれていく可能性があります。
センサー値自体の取得間隔をきちんとしたいということであれば、

  1. タイマーなどで一定間隔でセンサー値取得
  2. 取得したセンサー値はキューに保存
  3. 別スレッドでキューを取り出してシーケンシャルにAzure IoT Hubに送信

のような処理方式が必要です。

最後に

.NET nanoFrameworkには、Azure IoT Hubのライブラリもあり比較的簡単にコードを書くことができました。
しかし、ルートCA証明書が必要な点などが明確に書かれた情報が少なく、DeviceClientのOpenをしたときのエラーをなかなかとることができませんでした。
このブログエントリで同じことに悩む人が1人でもすくなくなってくれたら嬉しいです。

次回

次回は、センサーデータを蓄積してグラフ表示してみたいと思います。
もちろん、IoT HubにBLOBやPowerBIなどを組み合わせて作っていってもいいのですが、そのあたりもサポートしているSaaS製品であるAzure IoT Centralを使って最小手順で構築してみたいと思います。
hatsune.hatenablog.jp

M5Stack Core 2で始めるAzure IoT Hub入門(その5)

すっかり書いた気になっていたM5Stack Core 2でArduinoを使って、Azure IoT Hubにデータを送信するお話となります。
いま、.NET nanoFrameworkを使って同じ流れをおさらいしていますが、まずは、そこに先行して、こちらのお話を完結させたいと思います。

前回

hatsune.hatenablog.jp
前回はAzure IoT Hubの準備が完了したところまで進みましたので、今回は、M5Stack Core 5からAzure IoT Hubに「気圧」「気温」「湿度」を送信するためのプログラム(スケッチ)を紹介します。

Setupへの追加ロジック

センサー値を取得するスケッチのSetupに2つのロジックを追加します。

Wi-Fi初期化

gist.github.com
関連するライブラリはWiFi.hにあるので、スケッチの先頭でインクルードしておきましょう。

#include

MQTT初期化

Azure IoT Hubに接続するためのライブラリは、Visual Studio CodeでCTRL+SHIFT+Pで「Arduino Board Manager」起動して「M5Stack」をインストールすると「AzureIotHub」と「Esp32MQTTClient」のライブラリがダウンロードされてきます。

この「Esp32MQTTClient」のライブラリをスケッチの先頭でインクルードしておきます。

#include "Esp32MQTTClient.h"

次にSetUpの中でMQTTを初期化します。
gist.github.com
「connectionString」をAzure IoT Hub上で表示されているプライマリ接続文字列を設定します。

Loopへの追加ロジック

初期化が終わったらセンサー値を取得しているLoopの中にMQTT送信用のコードを入れます。
gist.github.com
主要部分は次のようになっています。

IoT Hubに送信するメッセージの作成

送信するメッセージは次のようなJSONフォーマットです。

このフォーマットに対して、snprintf文を使ってセンサー値を入れたものを「messagePayload」変数に設定します。

snprintf(messagePayload, MESSAGE_MAX_LEN, messageData, messageCount++, Tmp, Hum, Pressure);

IoT Hubへの送信

JSON文字列が出来上がったならば、MQTTに送信します。

EVENT_INSTANCE* message = Esp32MQTTClient_Event_Generate(messagePayload, MESSAGE);
Esp32MQTTClient_SendEventInstance(message);

これで、取得したセンサー値を「INTERVAL」ミリ秒ごとに送信することになります。

最後に

データをWEBでリアルタイム表示

IoT Hubに送信されたデータを「リアルタイム」で、つまり、Azure上のストレージなどにデータを格納せずに届いたタイミングでWEBアプリで表示してみましょう。
IoT Hubのサービス用の共通アクセスポリシーを含んだ接続文字列と、組込みエンドポイントのコンシューマーグループをWEBアプリ側で指定することでAzure IoT Hubと接続できます。

このあたりは、.NET nanoFrameworkを使ったときも同じになるので、機会があればそちらの流れでWEBアプリについては掘り下げたいと思います。

Microsoft MVP for Windows Developmentを再受賞しました。

2022-2023 Microsoft MVP for Windows Developmentを受賞しました。
2008年から15年連続となります。

コロナ禍での影響で今までの形での活動からチェンジが必要だったり、昨年は自動再受賞だったのでチェンジした活動がコミュニティに貢献できていたのか暗中模索を続けている状況でしたが、なんとか再受賞できたというのが正直な感想です。

また、オフラインイベントなども増えてきましたし、オンライン中心からの再シフトなどもあるかもしれませんが、状況に応じて自分ができる範囲で無理せずコミュニティ活動を続けていけたらと思います。

今年は、.NET nanoFramework、そして、AzureのAI関係を基軸にして、MRTKやOpenXRについてもアウトプットを増やしていけたらと思っています。
引き続きよろしくお願いします。

M5Stack Core 2で基本に帰ってLチカしてみよう

初めに

PORT.A.I2Cは、I2Cシリアル通信をサポートしているので接続しているモジュールのアドレスが分かると、あとはデザインシートをよみとってセンサー値の取得ができます。
運が良ければ、モジュールに対応したライブラリが公開されているので、そうするとかなり楽になります。
hatsune.hatenablog.jp

それでは、PORT.A.I2C以外のインターフェースを使う場合はどうでしょうか。
今回は、GPIOを使って、その先にLEDをつなげて、C#でLEDをチカチカさせるプログラムを作ってみたいと思います。
やはり、組込み系にはLチカ(LEDをチカチカさせること)が、Hello Worldですよね。

M5Stck Core 2側の事前準備

Install the nanoFramework firmware

M5Stackに使われているESP32にはnanoFrameworkは標準インストールされていないので、nanoFrameworkをインストールします。

接続ポート番号の確認

USBでM5Stack Core 2をPCに接続したら、デバイスマネージャーで「CP210x」が接続されているポート番号を確認します。今回の環境では「COM4」が該当しました。

Flasherアプリケーションのインストール(インストール済の場合は不要)

OSの[スタート]メニューから「Developer PowerShell for VS2022」を起動します。

dotnet tool install --global nanoff

nanoFrameworkfファームウェアをインストール(インストール済の場合は不要)

今回使用するM5Stackは、M5Stack Core 2になります。
このM5Stack Core 2をターゲットにして「COM4」経由でnanoFrameworkをアップロードします。

nanoff --target M5Core2 --update --serialport COM4

Device Explorerでの接続確認

nanoFrameworkファームウェアをインストールしたあとにVisual Studioを起動してDevice Explorerをみると、「M5Core2 @ COM4」と表示されるようになります。

nanoFrameworkファームウェアは、インストールしたあとにnanoFramework以外の別イメージをM5Stack Core 2に転送(Burn)してしまうと消えてしまうので、Device Explorerに表示がないような場合は、再度、インストールをしてからVisual Studioを起動すれば表示されるようになります。

Visual Studioの準備

Visual Studioを機能拡張する

Visual Studioには.NET nanoFramework Extensionをインストールして機能拡張しておきます。
この機能拡張によりnanoFrameworkアプリのテンプレートが追加されます。
このテンプレートで作成したプロジェクトには、NuGetから自動的に「nanoFramework.CoreLibrary」ライブラリが追加されています。

「nanoFramework.M5Core2」ライブラリを追加する

今回は、M5Stack Core 2を使うので、「nanoFramework.M5Core2」ライブラリを手動でNuGetから追加します。

ここまでが、M5Stack Core 2を使うアプリ開発を行う上での共通の事前準備となります。

Lチカプログラム

接続インターフェースについて

M5Stack Core 2の裏の「CORE 2」を書かれている白いふたを外して、M-Busを露出させます。

M-Busについて
  • G0:I2S LRCK PDM CLK
  • G1:TXD0
  • G2:I2S DOUT
  • G3:RXD0
  • G13:RXD2
  • G14:TXD2
  • G18:SCK
  • G19:GPIO
  • G21:intSDA
  • G22:intSCL
  • G23:MOSI
  • G25:DAC
  • G26:DAC
  • G27:GPIO
  • G32:PA_SDA (PORT.A.I2Cと同じ)
  • G33:PA_SCL (PORT.A.I2Cと同じ)
  • G34:PDM DAT
  • G35:ADC
  • G36:ADC
  • G38:MISO
  • GND
  • 3.3V
  • 5V
  • RST
  • BAT

今回はG19のGPIOを使います。

GPIOについて

GPIOは「General-purpose input/output」といってデジタル(ONとOFFの二値)値の入出力ができるインターフェースです。
ONにすることで5Vが出力され、OFFにすると0Vが出力されます。

回路

M5Stack Core 2からのGPIO出力をそのままLEDに繋ぐと電圧が高すぎるため150Ωくらいの抵抗を繋いで次のような回路を作ります。

赤ケーブルをM-Busのピン19、そして青ケーブルをGNDに繋ぎます。

コードについて

gist.github.com

GPIOコントローラー

GPIOを使うときは、まずはGPIOコントローラーを生成します。

var gpio = nanoFramework.M5Stack.M5Core2.GpioController;

LEDの接続をオープン

LEDを光らせるためには、GPIOに対して出力をすることになるので、ピン19を指定して出力モードでオープンします。

var led = gpio.OpenPin(19, PinMode.Output);

ONとOFFを交互に実行する

少し下手なコードですが2秒ごとにONとOFFを無限ループするようにしています。
GPIOでONにするコードは次のようになります。

led.Write(PinValue.High);

GPIOでOFFにするコードは次のようになります。

led.Write(PinValue.Low);

実行

youtu.be

M5Stackで.NET nanoFrameworkからセンサー使ってみた

前回

hatsune.hatenablog.jp

前回は、.NET nanoFrameworkでの開発準備とM5Stack Core 2での簡単なアプリの実行まで実施しました。
今回は、M5Stack Core 2のPORT Aと呼ばれる外部端子にセンサーを接続して、気圧や気温、湿度などを画面に表示してみたいと思います。

事前準備

センサーについて

この記事で使用するセンサーは、手元にあったM5StackユニットのENV-IIを使います。
M5Stackユニットは、M5Stack Core 2のPORT.A.I2C端子に接続できるセンサーです。
PORT.A.I2Cは、その名の通りI2Cと呼ばれるシリアスバス規格となります。
ENV-IIは、気圧センサーとしてBMP280、気温と湿度のセンサーとしてSHT30を内蔵したユニットとなります。
ENV-IIとM5Stack Core 2を接続し、更にM5Stack Core 2をUSBでPCに接続して、準備完了です。

Install the nanoFramework firmware

M5Stackに使われているESP32にはnanoFrameworkは標準インストールされていないので、nanoFrameworkをインストールします。

接続ポート番号の確認

USBでM5Stack Core 2をPCに接続したら、デバイスマネージャーで「CP210x」が接続されているポート番号を確認します。今回の環境では「COM4」が該当しました。

Flasherアプリケーションのインストール(インストール済の場合は不要)

OSの[スタート]メニューから「Developer PowerShell for VS2022」を起動します。

dotnet tool install --global nanoff

nanoFrameworkfファームウェアをインストール

今回使用するM5Stackは、M5Stack Core 2になります。
このM5Stack Core 2をターゲットにして「COM4」経由でnanoFrameworkをアップロードします。

nanoff --target M5Core2 --update --serialport COM4

Device Explorerでの接続確認

nanoFrameworkファームウェアをインストールしたあとにVisual Studioを起動してDevice Explorerをみると、「M5Core2 @ COM4」と表示されるようになります。

nanoFrameworkファームウェアは、インストールしたあとにnanoFramework以外の別イメージをM5Stack Core 2に転送(Burn)してしまうと消えてしまうので、Device Explorerに表示がないような場合は、再度、インストールをしてからVisual Studioを起動すれば表示されるようになります。

Visual Studioの準備

Visual Studioを機能拡張する

Visual Studioには.NET nanoFramework Extensionをインストールして機能拡張しておきます。
この機能拡張によりnanoFrameworkアプリのテンプレートが追加されます。
このテンプレートで作成したプロジェクトには、NuGetから自動的に「nanoFramework.CoreLibrary」ライブラリが追加されています。

「nanoFramework.M5Core2」ライブラリを追加する

今回は、M5Stack Core 2を使うので、「nanoFramework.M5Core2」ライブラリを手動でNuGetから追加します。

ここまでが、M5Stack Core 2を使うアプリ開発を行う上での共通の事前準備となります。

M5Stackで気圧表示(BMP280)

ライブラリの追加

.NET nanoFrameworkにはBMP280用のライブラリである「nanoFramework.Iot.Device.Bmxx80」がNuGetで提供されています。
.NET nanoFramrworkアプリケーションテンプレートで新規プロジェクトを作成したら、NuGetでBmxx80ライブラリを追加します。

BMP280から気圧を取得して画面に表示するC#コード

ENV-IIのI2Cアドレスを調べる

I2Cシリアル通信では、PORT.A.I2C端子につながっているセンサーから特定のセンサーを指定するために「アドレス」を指定します。
ENV-IIの中のBMP820と接続するためにアドレスに指定する値は、ENV-IIの仕様書をみると書かれています。
ENV II Unit with Temperature Humidity Environment Sensor (SHT30+BMP280) | m5stack-store

Description
ENV II is an environmental sensor which can sense temperature, humidity and atmospheric pressure. It integrates the SHT30 and BMP280 sensors and is programmed over the I2C protocol(SHT30:0x44, BMP280:0x76).

このアドレスを指定してI2C通信を行うことでBMP280より気圧値を取得できます。
nanoFrameworkでは、I2C通信用の「System.Device.I2c」クラスがあるので次のようなコードでI2C通信経路を確立できます。
gist.github.com

このコードは、「nanoFramework.M5Core2」ライブラリをNuGetから追加すると次のように簡単になります。
gist.github.com

センサー値を取得する

全体的なコードは次のようになります。
gist.github.com
センサー値の取得部分は次のところです

UnitsNet.Pressure pressure;
if (Bmp.TryReadPressure(out pressure))

[UnitsNet.Pressure」ライブラリは、「nanoFramework.IoT.Device.Bmxx80」ライブラリをNuGetで入れると自動的に追加されるBMP280で気圧を取得するためのライブラリです。
I2Cシリアル通信は、バイナリデータの送受信となりセンサー仕様のバイナリ形式から必要な数値形式に変換など地味にコード量が多くなる傾向にあります。
[UnitsNet.Pressure」ライブラリは、BMP280でのそのあたりを隠蔽して、更にヘクトパスカル表示(最近はミリバールとは言いません)のための単位変換もやってくれます。

M5Stackで気温と気圧表示

BMP280から気圧を無事取得できたので、次は、SHT30から気温と湿度を取得してみます。

ライブラリの追加

.NET nanoFrameworkにはSHT30用のライブラリである「nanoFramework.Iot.Device.Sht3x」がNuGetで提供されています。

SHT30から気温と湿度を取得して画面に表示するC#コード

ENV-IIのI2Cアドレスを調べる

ENV-IIのSHT30のアドレスは仕様書から「0x44」なのでそれを指定して通信を確立します。
gist.github.com
最近のC#では左辺の型がきまっているときはnewの後のクラス名を省略できるので下記のように書くこともできます。

Sht = new (M5Core2.GetI2cDevice(0x44))

センサー値を取得する

全体的なコードは次のようになります。
gist.github.com
センサー値の取得部分は次のところです

Console.WriteLine($"Temperature:{Sht.Temperature.DegreesCelsius:F} C");
Console.WriteLine($"Humidity:{Sht.Humidity.Percent:F} %");

M5Stack Core 2での動作

ENVIIから気圧、気温、湿度を取得するコードが完成したので、Visual Studioで[デバッグ]-[デバッグの開始]メニューでM5Stack Core 2への転送と実行を行います。

時間とセンサー値が1秒ごとに更新表示されます。

ENV-IIが入手できない

今回利用したENV-IIですが、残念ながら、2022年6月現時点では販売は終了しており、後継品のENV-IIIに代わっていました。
www.switch-science.com

ENV-IIとENV-IIIの違いを確認する

ENV-IIとENV-IIIの違いは気圧センサーがBMP280からQMP6988に変更されている点です。
そのため、ENV-II対応のプログラムからBMP280関連部分は変更が必要になります。

ENV-IIIのI2Cアドレスを調べる

I2Cシリアル通信では、PORT.A.I2C端子につながっているセンサーから特定のセンサーを指定するために「アドレス」を指定します。
ENV-IIIの中のQMP6988と接続するためにアドレスに指定する値をENV-IIIの仕様書から読み取ります。
ENV III Unit with Temperature Humidity Air Pressure Sensor (SHT30+QMP6988) | m5stack-store

I2C interface (SHT30:0x44 , QMP6988:0x70)

SHT30についてはENV-IIと同様に「0x44」ですが、QMP6988は「0x70」となります。

ENV-IIIのライブラリを調べる

SHT30については「nanoFramework.Iot.Device.Sht3x」ライブラリが使えますが、QMP6988を使うためのライブラリは2022年6月現時点ではNuGetに公開されていません。
そこで自作でQMP6988用のクラスを作成する必要があることが分かりました。

QMP6988のデータシートを読み解く

QMP6988用のクラスを作成するためには、必要なQMP6988の情報をデータシートで確認することになります。
https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/enviii/QMP6988%20Datasheet.pdf
データシートを読んでみるとQMP6988は気圧以外にも気温が取得できるようです。この気温は、気圧の補正に使用します。
データシートには、そのほかにもQMP6988の気圧解像度(正確性)が0.06 Pa = 6 hPaであるなどの情報も記載されている。気圧解像度が6 hPaということは、6 hPa以下の精度は出ないということになります。

センサー値の理解

QMP6988はレジスタと呼ばれる1byte(=8bit)の領域を介して値のやりとりを行います。
データシートの「4.4 Implementing Register List」によればレジスタは全部で38個あります。

気圧データは0x07~0x09の3つのレジスタ、つまり3byte値で取得できます。
3byteの中のデータフォーマットには、22bitモード、23bitモード、24bitモードの3つのデータフォーマットがあります。
データ形式は、アドレスが小さい方に上位ビットが来る「BigEndian」形式となります。

センサーの生データは「符号なし24bitデータ」とのことで、データシートによれば24bitモードでは2の23乗をマイナスする必要があると書かれています。
このあたりのbitフォーマットからの数値化の方法も記載があり次のようにすればよいことが分かります。

Dp = ((PRESS _ TXD2) <<16) + ((PRESS _ TXD1) << 8) + (PRESS _ TXD0) − pow(2,23)

.NET nanoFrameworkには便利な関数が用意されておりシフト演算をしなくても「BigEndian」形式からuint型へ変換してくれます。

System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(data) - Math.Pow(2, 23)

QMP6988の設定

QMP6988の設定初期は以下の通りです。この設定は、CTRL_MEASレジスタ(0xF4)により変更できます、

  • 出力モード:測定しない
  • パワーモード:Sleep


QMP6988には3種類の出力モードがあり、パワーモードをNormalにして、気温は22bitモード、気圧は24bitモードで出力したいときは、「0x1F」を設定します。
気温を22bitモードにしているのは、気圧補正用の気温のため精度はそれほど重要でないため一番低い解像度でよいからです。

パワーモードについて

パワーモードを変更すると計測と消費電力の関係は次のようになります。

  • Sleep:消費電力は最小ですが、測定はできません。
  • Force: 測定時だけ測定用に回路に通電し、測定が終わると必要最小限の消費電力に落とすモードです。
  • Normal:定期的に測定しつづけるモードです。測定間隔の初期値は1msとなります。

測定間隔は、IO_SETUPレジスタのt_standby領域で指定が可能で、1ms~4秒までの間で指定ができて、測定間隔が長いほどトータル消費電力は下がります。もし、4秒よりも長い測定間隔でよい場合は、パワーモードをForceにするのが適切です。

QMP6988用クラスのC#コード

I2Cシリアル通信を確立する

IC2シリアル通信の確立は、QMP6988変わっても同様に「M5Core2.GetI2cDevice(0x70)」で実現できます。
BMP280用ライブラリと同様に、この戻り値をQMP6988クラスに渡します。
gist.github.com

モードを指定する

解像度やパワーモードを指定するためには、CTRL_MEASレジスタ(0xF4)に1バイトのデータを書き込むことで実現します。
gist.github.com

気圧を読み取る(3バイト領域)

QMP6988が気圧をレジスタに代入するタイミングは2つあります。
パワーモードがNormalの場合は、t_standbyに設定された間隔で自動的にレジスタに気圧が記録されますので、値を取得するときはレジスタにすでに格納されている値を読み取るのみです。
Forceの場合は、レジスタ読み取り時に気圧を測定してレジスタに格納します。
レジスタから値を取得するには、

  1. レジスタアドレスをWriteする
  2. 返却サイズに応じたバイト配列を指定してReadする

ことで3バイトの気圧データを取得できます。

気圧を読み取る(4バイト領域)

しかし、C#の変数で3バイト長の変数は存在しないため、少しおさまりが悪いです。そこで4バイト領域に3バイト分のデータを読み込みます。
gist.github.com
このコードの中で少し馴染みの薄い部分は次のところでしょう。

this.I2c.Read(readBuff.Slice(1))

この記述により合計で3バイトのデータをbyte[4]の2バイト目以降に受信します。
こうして得られたbyte[4]のバイト配列は次のコードでuint型の数値にできます。

System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(data)

気圧の生データを実地に変換

取得できた気圧の生データは、そのままでは正しい気圧値を表していないため、気温と合わせて補正計算をしてから16分の1の数値にします。
gist.github.com
BMP280もそうでしたが気圧センサーのデータ取り扱いの肝は「ConvTx02e」「Pressure02e」などで記載している補正関数になりますが、今回は説明が冗長になるので詳細は割愛します。

ENV-IIIから気圧、気温、湿度を取得して画面に表示するC#コード

QMP6988用のクラスが作成できたら、それを使ってENV-II用のコードを書き換えてENV-III用のアプリを作成しましょう。

ENV-IIIのI2Cアドレスを調べる

ENV-IIIのQMP6988のアドレスは仕様書から「0x70」なのでそれを指定して通信を確立します。

Qmp = new (M5Core2.GetI2cDevice(0x70))

センサー値を取得する

全体的なコードは次のようになります。
gist.github.com
センサー値の取得部分は次のところです

Console.WriteLine($"DateTime:{DateTime.UtcNow:yyyyMMdd HHmmss}");
Console.WriteLine($"Pressure:{Qmp.CalcPressureHectopascals():F} hPa");
Console.WriteLine($"Temperature:{Sht.Temperature.DegreesCelsius:F} C");
Console.WriteLine($"Humidity:{Sht.Humidity.Percent:F} %");

M5Stack Core 2での動作

ENV-IIIから気圧、気温、湿度を取得するコードが完成したので、Visual Studioで[デバッグ]-[デバッグの開始]メニューでM5Stack Core 2への転送と実行を行います。

時間とセンサー値が1秒ごとに更新表示されます。

最後に

M5Stack Core 2にENV-IIユニットやENV-IIIユニットを繋いでC#で取得・表示するコードを紹介しました。
ユニットに使われているセンサーチップに対応したライブラリがNuGetにあると非常に簡単にコードが書けます。
そしてライブラリがない場合もデータシートの読みポイントさえ見つけることができれば、バイナリーデータ送受信とフォーマット変換のコードへとデータシートの内容を読み落としていく工程は、簡単とはいえませんが不可能ではありません。
NuGetにライブラリがないからと言って使うのをあきらめたりせず、データシートを読み解きながら実装していくのも、センサー系開発の醍醐味です。

センサーの取り扱いが整理できて来たので、次回は、今回取得したセンサーデータをAzureに送信するコードに挑戦します。
hatsune.hatenablog.jp

M5Stackで.NET nanoFramework動かしてみた

やっぱりSoCにアプリ組み込んで動かすのが好きです。
そして、それがC#だともっと好き(書きたいことが書きたいように書けるから)。

昨年は、Arduinoを開発環境にしてM5Stackのアプリを色々作成していました。
少しでも使いやすい開発環境でということでVisual Studio Codeなども活用しました。

そんなM5Stackアプリ開発ですが、気づいてみたら、.NET nanoFrameworkで開発できるようになっていました。Arduinoとの違いは、.NET nanoFrameworkを使えばC#でコードが書けるという点です。すごい!

そもそも

M5 Stackとは

M5Stackは、ESP32というbluetoothWi-Fiを内蔵するSoCを使って、それに320x240のTFTカラー液晶、microSDカードスロット、スピーカーを組み合わせたコンパクトで便利な開発モジュールになります。
ESP32はArudino環境で開発ができるのでM5Stackで動作するアプリもArduino環境で開発ができます。
M5Stackには様々な拡張モジュールがあり、M5Stack Coreと呼ばれるM5Stack本体に拡張モジュールを積み重ねていくことでいろいろな機能が拡張ができます。

M5Stack Core 2とは

今回使うM5Stack Core 2は、M5StackのCore機能を改良した2022年6月時点での最新世代(第二世代)のCoreデバイスとなります。
www.switch-science.com
M5Stack Core 2のハード仕様などは下記の過去記事が参考になります。
hatsune.hatenablog.jp

.NET nanoFrameworkとは

.NET nanoFrameworkは、ESP32などのMCU(マイクロコントローラユニット)などの組込み用デバイス向けに.NETアプリの作成を可能にする無償のオープンソースプラットフォームです。
C# + .NET 6でデスクトップアプリを作成した経験が組込みシステム開発で活かせることを想定しているそうです。

.NET Core IoTとの違いは?

.NET環境での小型デバイス向けの開発には、.NET Core IoTと呼ばれているものもあります。
.NET Core IoTは、.NET Core+IoT向けライブラリで構成されています。
.NET Coreを動作させるので組込みといってもある程度のリソースが必要で、ターゲットとするハードは、Raspberry Piが想定さてています。
一方、.NET nanoFrameworkは動作環境でもあり、.NET Coreのランタイムではなく、.NET nanoFrameworkのランタイムでアプリが動作します。その代わり、組込み用デバイスでは不要な部分がなく、.NET Core IoTよりも更に小さなリソースで動作します。

事前準備

.NET nanoFramework Extensionを追加

.NET nanoFrameworkのプロジェクトをVisual Studioで作成できるように、Visual Studioを拡張(機能追加)しましょう。
そのために使用する拡張機能が「.NET nanoFramework Extension」です。
marketplace.visualstudio.com
Visual Studio 2022を起動したら、[拡張機能]-[拡張機能の管理]メニューで管理ウィンドウを開いてから「nano」で検索して.NET nanoFramework Extensionを探し出して適用します。

Install the nanoFramework firmware

M5Stackに使われているESP32にはnanoFrameworkは標準インストールされていないので、nanoFrameworkをインストールします。

接続ポート番号の確認

USBでM5Stack Core 2をPCに接続したら、デバイスマネージャーで「CP210x」が接続されているポート番号を確認します。今回の環境では「COM4」が該当しました。

Flasherアプリケーションのインストール

OSの[スタート]メニューから「Developer PowerShell for VS2022」を起動します。

dotnet tool install --global nanoff

nanoFrameworkfファームウェアをインストール

今回使用するM5Stackは、M5Stack Core 2になります。
このM5Stack Core 2をターゲットにして「COM4」経由でnanoFrameworkをアップロードします。

nanoff --target M5Core2 --update --serialport COM4

正常に動作すると次のようなログが表示されます。
gist.github.com

M5StackでHelloWorld

M5Stackアプリのプロジェクトを作成

.NET nanoFrameworkのテンプレートでプロジェクトを新規作成

機能拡張がうまくいくとVisual Studoの新規プロジェクト作成のテンプレートに「.NET nanoFramework」のテンプレートが追加されています。

まずは、「HelloWorld」というプロジェクト名で新規プロジェクトを作成して、M5Stack Core 2の液晶画面に「Hello World」と表示するアプリを作ってみます。

Device ExplorerでM5Core2を選択

プロジェクトが作成されたら、Device Explorerを開いてM5Core2との接続を確認します。
Device Explorerは、Visual Studioの「検索」機能から開くのが艦隊です。

テンプレートの初期コードを実行

M5Stack Core 2との接続や事前準備が正常に終わっていると、新しいプロジェクトを作成したときに自動生成されているコードのビルドおよび実行が正常に行われます。
自動生成コードは「Debug.WriteLine("Hello from nanoFramework!");」が実行される簡単なコードなので、実行が成功するとイミディエイトウィンドウに「Hello from nanoFramework!」と表示されます。

M5Stack Core 2の画面に「HelloWorld」

M5Stack Core 2用のライブラリを追加

M5Stack Core 2の画面などハードウェアにアクセスするために、M5Stack Core 2用のライブラリを追加します。

初めてのM5Stack C#プログラミング

M5Stackの画面に文字を表示する簡単な方法は「nanoFramework.M5Stack.Console.WriteLine」メソッドを使う方法です。
Console.WriteLineと書くとSystem.Consoleと名前が重複するのでusingでConsoleはnanoFramework.M5Stack.Consoleのことであることを宣言してあります。
gist.github.com

M5Stack Core 2のボタンに反応するように拡張

起動して「HelloWorld」と表示されるだけでは味気ないので、M5Stack Core 2についている3つのボタンをクリックしたら画面にボタンの種類を表示するようにしてみましょう。
M5Stack Core 2用のライブラリでボタンクリックイベントは「TouchEvent」としてイベントが発生します。
そして、そのイベント引数から3つのボタンのどれがクリックされたかをif文で判定します。
gist.github.com
イベントハンドラーを書き始めるとC#っぽい感じがすごくでてきますね。

Visual Studioから切り離してM5Stack Core 2だけで動作させる

Visual Studioデバッグ実行をとめるとM5Stack側も動作がとまってしまいます。
M5Stackだけで動作させるには、M5Stackで電源OFF/ONをしてアプリをリスタートしてあげると実行できます。

まとめ

ファームウェアの転送など少しだけ事前準備が必要ですが、比較的簡単に使える環境が整ってきた感じがします。
ファームウェアは1度実施すれば(更新はあとであるかもですが)、毎回する必要はないのですが、Device Explorerでつながっているかを確認するのは忘れがちで、デバッグ実行したときなどに「あれ?転送されない」と焦ることがあるので注意しましょう。
また、Device Explorerで「disable」すると、再度、enableするためにはデバイスマネージャーの方で該当COMポートを「無効」にして「有効」にしないといけないので、「なんだろ?」とか思ってDevice Explorerでdisable device watchesしないように注意しましょう。

次回

次回は、ENV.II SENSORを繋いで気圧を画面に表示するアプリをC#で作成します。
hatsune.hatenablog.jp

Azure CognitiveServices TextAnalyticsの「テキスト要約」をクライアントアプリから使ってみる(REST API編)

前回:
hatsune.hatenablog.jp

前回は、AzureポータルでTextAnalyticsを有効にして、Azure.AI.TextAnalytics SDKを使って「テキスト要約」を行うクライアントアプリの動作を確認してみました。
今回は、クライアントアプリからREST APIにて同様の動作を実現してみます。

準備

.NET 6ベースのWPFアプリとしてクライアントアプリを作成します。

新しいプロジェクトの作成

Visual Studio 2022で、新しいプロジェクトの作成で「WPFアプリケーション」のテンプレートを選択すると、.NET Frameworkベースではなく.NET 6ベースのWPFアプリが作成できます。
名前は「TextSummarizationAPISample」と名付けることにしましょう。

コードを記述

サンプルコードは、MVVMデザインパターンで作成しているので、画面はViewsフォルダ配下に「MainWindows.xaml」として記述し、REST APIを呼び出すロジックはModelsフォルダ配下に「TextSummarizationModel.cs」として記述します。

画面を作成

MVVMの良いところは、ViewとModelが分離できるところです。前回のSDKからの変更はModelの差し替えだけになるので、画面については、SDKのサンプル画面と同じく「入力欄」「実行ボタン」「出力欄」として、XAMLコードなどは名前空間だけかえてそのままコピペしてきます。名前空間変更前のものを再掲しておきます。
gist.github.com
このXAMLのx:classとxmlns:localを「TextSummarizationSDKSample」から「TextSummarizationAPISample」に変更します。

ロジックを作成

TextSummarizationModelクラスとしてREST API呼び出し部分を記述してみましょう。
gist.github.com
「your key」や「your resourcename」のところは、AzureでTextAnalyticsを有効化したときに生成されたキーとリソース名を設定します。
SDKのときよりもかなり複雑なコードになっています。
最初にREST APIのBODYとしてパラメタをJSON文字列として設定するためのクラスにパラメタを設定しています(9~39行目)。
67行目でPostによりREST APIをたたいていますが、この戻り値が要約結果ではありません。
72行目で取得できるのは、要約結果を得るためのjob番号付きURLです。
86行目では、72行目で得られたob番号付きURLを指定して要約結果をGetしています。
もちろん非同期実行ですから、Azure側で要約が完成して初めてSendAsycメソッドは値を返します。
そして、97行目から100行目で戻ってきた要約(今回は3文)を取得しています。

なお、上記には載せていませんがJSON形式とやり取りするためのクラス定義も必要です。

実行

対象のテキストを入力

Language Studioで試したように現在開催されているMicrosoft Build 2022のBook of NewsにあるAzureAIの説明を要約してみましょう。
news.microsoft.com
場所は、「1.1 Azure AI」の部分にしてみましょう。

実行結果

[Exec]ボタンをクリックして少し待つとテキスト要約結果が表示されます。

Microsoft Azure AIは、開発者が高品質なモデルをAPIとしてデプロイし、言語機能をより効率的かつ責任を持ってアプリに注入できるように、Azure Cognitive Servicesに2つのアップデートを導入します。
Azure Cognitive Serviceの1つであるAzure OpenAI Serviceは、現在プレビューで利用可能です。
Azure Cognitive Service for Languageは、文書や会話の要約を提供する新機能で、開発者が文書やコンタクトセンターの通話に含まれる重要な情報(通話理由や解決策など)を迅速に表面化できるよう支援します。

まとめ

TextAnalytics用のREST APIを使ってテキスト要約を行うクライアントアプリを作成してみました。
同じ文章を入力すればSDK版と同じ結果が当たり前ですが得ることができました。
こうやって記載してもるとREST APIに比べて、SDKが使いやすいので非常にシンプルなコードで実装できるのが分かってもらえたら嬉しいです。