atelier:mitsuba

i love UI/UX, Blend, XAML, Behavior, P5, oF, Web, Tangible Bits and Physical computing. なにかあればお気軽にご連絡ください。atelier@c-mitsuba.com

AZ3166でIoTHubを経由して、センサーデータを保存する。

こんな箱に入った

f:id:c-mitsuba:20220302222203j:plain

これ。

f:id:c-mitsuba:20220302222450j:plain

いわゆる、Azure Certificate な IoT DevKitってやつ。
バイス仕様はこんなかんじ。
f:id:c-mitsuba:20220303061322p:plain
Microsoft Azure Certified IoT DevKit (IOT-AZ3166)ja.plugable.com



一応Arduino完全互換デバイスなのだけど、既存のESP32とかArduinoとかと違ってブートローダー的なのが入ってるのがいいかんじ。
WiFiの接続とかデバイス設定を、スケッチに埋め込まずにシェルだったりネットワーク越しに設定できる。


でもって、これをIoT Hubにつなげてみようっていうのが今回のお題。

1.まずはつなげ先のIoTHubを作る。
現状なにも作ってないので、新規作成。
f:id:c-mitsuba:20220303054958p:plain

適当にリソースグループと名前を設定して
f:id:c-mitsuba:20220303055155p:plain

今回は検証なので、フリー版を使って作成。
f:id:c-mitsuba:20220303055332p:plain

2.デプロイできたら、次はデバイス登録
作ったIoTHubの左にあるデバイスから、
f:id:c-mitsuba:20220303055701p:plain
f:id:c-mitsuba:20220303055726p:plain

こんなかんじで作成。
f:id:c-mitsuba:20220303055819p:plain

次に開発環境構築。
3.いわゆるArduinoIDEでも良いのだけど、Azureとかモニターの親和性を考えてVisual Studio Codeに環境を作るのがおすすめらしい。
というわけで、雑に上4つぐらいと、Arduino拡張をインストールする。
f:id:c-mitsuba:20220303062159p:plain
f:id:c-mitsuba:20220303062420p:plain

4.IoTHub Extensionでさっき作ったIoTHubを選択する。
f:id:c-mitsuba:20220303062745p:plain
選択すると、こんかなんじでIoTHubが選択されたよっていうメッセージが出力される。
f:id:c-mitsuba:20220303062613p:plain

5.次にArduinoのスケッチがビルドできるようにVSCodeを設定する。
残念ながらExtensionを入れても、VSCodeArduinoをビルドできるようになるわけではなくって、公式からダウンロードしたArduinoにパスを通して使えるようにしてくれるだけ。
f:id:c-mitsuba:20220303063612p:plain

あとはカスタムボードの設定も入れないといけないので、最終的にはこんなかんじ。
たぶんJSONで入れるのが一番ラクだと思う。
f:id:c-mitsuba:20220303063803p:plain

{
    "arduino.additionalUrls": ["https://raw.githubusercontent.com/VSChina/azureiotdevkit_tools/master/package_azureboard_index.json"],
    "arduino.path": "C:\\Program Files (x86)\\Arduino"
}

6.ここまで来たらVCodeでF1押して、Arduino Board Managerを起動。
IoTDevKitをインストール。
f:id:c-mitsuba:20220303064229p:plain

ここでいよいよIotDevKitをPCにつなぎます。
7.PCにつないで、もしちゃんとエクスプローラーとかデバイスマネージャで見えない人はドライバが入ってないので、ドライバを入れる。
www.st.com

8.最新バージョンは2.0.0なので、IoTDevKitのファームウェアバージョンも合わせておく。
最新のファームをここからダウンロードしてきて、エクスプローラーでぽとぺた置いて、Reset。
2.0.0が表示されればOK
microsoft.github.io

ちなみに、factory reset相当のことがやりたければ、同じバージョンで構わないのでファームを焼き直すのが手っ取り早い。

9.ついでにWiFi設定もしてしまおう。
Bボタンを押したままResetでAPモードに入から、適当なデバイスから接続。
おそらくディスプレイに192.168.0.1が表示されているので、ブラウザからアクセスすればWiFiの設定を書き込める。

こっから、開発スタート。
まずはサンプルコードを取ってくるとこから。今回はこれ。

10.VSCodeのF1からAzure IoT Device Workbench: Open Examples..を開いて
f:id:c-mitsuba:20220303095650p:plain

「Get Started」の「Open Sample」で新しいVSCodeを開く。
f:id:c-mitsuba:20220303095841p:plain

こんなかんじのコードがでてくればOK。
f:id:c-mitsuba:20220303105201p:plain

11.F1でビルドが通るかVerifyしてみる。
f:id:c-mitsuba:20220303105226p:plain

ちゃんと通ればOK。
f:id:c-mitsuba:20220303105322p:plain

もし通らなかったら、VSCodeの右下でデバイス設定がちゃんとされてるかチェック。
f:id:c-mitsuba:20220303105805p:plain

12.次にIoTHubに接続するキーになる接続文字列をAZ3166に書き込む。
F1でiot confとか打てばそれっぽいのが出てくる。
f:id:c-mitsuba:20220303105458p:plain

Connection stringを選んで、
f:id:c-mitsuba:20220303105529p:plain

IoTHubのプライマリ接続文字列を見える状態にして、貼り付け。

13.最後にデプロイしよう。
コードはArduinoだけど、AZ3166は普通のArduinoではないので、Workbenchの「Upload Device Code」でビルドしてデプロイしてくれる。
f:id:c-mitsuba:20220303105848p:plain

こんなかんじで、Doneになればデプロイ成功。
f:id:c-mitsuba:20220303110851p:plain

14.でも、現状のコードだと、気温と湿度が変わったときにしかメッセージを送らないので、毎秒送るように書き換える。
utility.cppの115行目-135行目あたり

    float t = readTemperature();
    float h = readHumidity();
    bool temperatureAlert = false;
    if(t != temperature)
    {
        temperature = t;
        *temperatureValue = t;
        json_object_set_number(root_object, "temperature", temperature);
    }
    if(temperature > TEMPERATURE_ALERT)
    {
        temperatureAlert = true;
    }
    
    if(h != humidity)
    {
        humidity = h;
        *humidityValue = h;
        json_object_set_number(root_object, "humidity", humidity);
    }
    serialized_string = json_serialize_to_string_pretty(root_value);

の前の値チェックを外して、こんなかんじ。

    float t = readTemperature();
    float h = readHumidity();
    bool temperatureAlert = false;
    temperature = t;
    *temperatureValue = t;
    json_object_set_number(root_object, "temperature", temperature);
    if(temperature > TEMPERATURE_ALERT)
    {
        temperatureAlert = true;
    }
    
    humidity = h;
    *humidityValue = h;
    json_object_set_number(root_object, "humidity", humidity);
    serialized_string = json_serialize_to_string_pretty(root_value);

Alertのコードが毎回falseになるけど、まぁいいや。
で、もっかいデプロイ。

きちんと成功していれば、Azure接続のLEDが光って、モニターにIoTHubに気温と湿度が投げつけられる様子が確認できる。
f:id:c-mitsuba:20220303043709j:plain

他のサンプルコードは読んでみるといいかな、と。

ただ、このままだとIoTHubにメッセージが届いてることはわかるけど、肝心のメッセージの内容がわかんない。
f:id:c-mitsuba:20220303115806p:plain

15.ちょっと高いけど(月1万円しないぐらい)、StreamAnalytics Jobでメッセージを処理してみる。
f:id:c-mitsuba:20220303115857p:plain

適当にこんなかんじでデプロイ。
f:id:c-mitsuba:20220303115950p:plain

16.デプロイできたら、入力にIoTHubを選択。
f:id:c-mitsuba:20220303120128p:plain
f:id:c-mitsuba:20220303120159p:plain


17.出力はいろいろあるけど、今回はBlobで。
f:id:c-mitsuba:20220303120321p:plain

ストレージアカウントがなければ適当につくって、こんなかんじ。
f:id:c-mitsuba:20220303120712p:plain

18.入力と出力が定義できたら、最後にそれをつなぐクエリを書く。
f:id:c-mitsuba:20220303120937p:plain

まぁそのまま受け取ったやつを流すだけ。

SELECT
    *
INTO
    blob
FROM
    IoTHub

19.これでしばらく放っておくと、実データを使ったクエリのテストができるようになる。
f:id:c-mitsuba:20220303214304p:plain

20.いい感じだったら、StreamAnalyticsを開始すれば、
f:id:c-mitsuba:20220303214355p:plain

Blobにこんなかんじで、年月日フォルダが作られて、
f:id:c-mitsuba:20220303214509p:plain

中に1日ごとのJSONが作られる。
f:id:c-mitsuba:20220303214614p:plain

{"messageId":882,"temperature":22.399999618530273,"humidity":51.200000762939453,"EventProcessedUtcTime":"2022-03-03T12:44:27.5334971Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:43:36.2700000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:43:36.2680000Z"}}
{"messageId":883,"temperature":22.399999618530273,"humidity":51.200000762939453,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:43:42.0520000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:43:42.0350000Z"}}
{"messageId":884,"temperature":22.399999618530273,"humidity":51.200000762939453,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:43:47.8190000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:43:47.8200000Z"}}
{"messageId":885,"temperature":22.399999618530273,"humidity":51.200000762939453,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:43:53.5760000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:43:53.5860000Z"}}
{"messageId":886,"temperature":22.399999618530273,"humidity":51.200000762939453,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:43:59.3460000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:43:59.3510000Z"}}
{"messageId":887,"temperature":22.399999618530273,"humidity":51.200000762939453,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:05.1120000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:05.1180000Z"}}
{"messageId":888,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:10.8780000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:10.8830000Z"}}
{"messageId":889,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:16.6650000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:16.6520000Z"}}
{"messageId":890,"temperature":22.399999618530273,"humidity":51.400001525878906,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:22.4160000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:22.4180000Z"}}
{"messageId":891,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:44:28.4387169Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:28.1810000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:28.1830000Z"}}
{"messageId":892,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:44:34.1943794Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:33.9630000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:33.9490000Z"}}
{"messageId":893,"temperature":22.399999618530273,"humidity":51.5,"EventProcessedUtcTime":"2022-03-03T12:44:39.8849782Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:39.7320000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:39.7300000Z"}}
{"messageId":894,"temperature":22.399999618530273,"humidity":51.400001525878906,"EventProcessedUtcTime":"2022-03-03T12:44:45.5875320Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:45.4980000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:45.4960000Z"}}
{"messageId":895,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:44:51.4924280Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:51.2640000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:51.2620000Z"}}
{"messageId":896,"temperature":22.399999618530273,"humidity":51.400001525878906,"EventProcessedUtcTime":"2022-03-03T12:44:57.1832940Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:44:57.0290000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:44:57.0280000Z"}}
{"messageId":897,"temperature":22.399999618530273,"humidity":51.400001525878906,"EventProcessedUtcTime":"2022-03-03T12:45:02.9777513Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:45:02.7950000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:45:02.7960000Z"}}
{"messageId":898,"temperature":22.299999237060547,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:45:08.7803588Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:45:08.5690000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:45:08.5620000Z"}}
{"messageId":899,"temperature":22.399999618530273,"humidity":51.400001525878906,"EventProcessedUtcTime":"2022-03-03T12:45:14.4601878Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:45:14.3340000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:45:14.3270000Z"}}
{"messageId":900,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:45:20.1493755Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:45:20.1000000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:45:20.0940000Z"}}
{"messageId":901,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:45:26.0582320Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:45:25.8650000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:45:25.8640000Z"}}
{"messageId":902,"temperature":22.399999618530273,"humidity":51.299999237060547,"EventProcessedUtcTime":"2022-03-03T12:45:31.8371634Z","PartitionId":1,"EventEnqueuedUtcTime":"2022-03-03T12:45:31.6310000Z","IoTHub":{"MessageId":null,"CorrelationId":null,"ConnectionDeviceId":"AZ3166","ConnectionDeviceGenerationId":"637818519466399285","EnqueuedTime":"2022-03-03T12:45:31.6300000Z"}}

今回はStreamAnalyticsでBlobに吐いたけど、Azure Data Explorerにつなげばトラディショナル大好きExcelにデータ流せるし、Power BIとかで解析してもいいし、Azure MLとかに流してもいいかも。

長々と書いたけど、ひとまずセンサーデータをファイルに書き出せるところまでできてよかったし、AZ3166だとサンプルがしっかりあってよかったかな。



参考
公式のドキュメント
microsoft.github.io


AZ3166リファレンス
microsoft.github.io