ラベル Azure IoT の投稿を表示しています。 すべての投稿を表示
ラベル Azure IoT の投稿を表示しています。 すべての投稿を表示

2018-04-25

OpenBlocks IoT FW3.xシリーズで下流方向の制御を行うハンドラの設定とテンプレート (C言語)

OpenBlocks IoT VX2およびVX1では、基本ソフトウェアとしてFW3.xが用意されます。
FW3.xシリーズでは、2.xシリーズの各機能に加え、以下のようなアップデートが行われています。

・メッセージの完全な双方向化
・Dockerに対応。エッジ処理モジュールをDockerコンテナで提供可能
・エッジ処理のみをNode-REDでも記述可能
・Luaによるデバイスハンドラ記述が可能
・Node-REDの最新環境を提供し、カスタマイズを容易に

Firmware 3.0 Architecture スタック

今回は、FW3.xで実装された、メッセージの双方向化における、下流方向のデータを用いたデバイスハンドラについて、まずは、C言語による記述と、設定方法について簡単に解説します。

メッセージの完全な双方向化に関しての詳細な情報は以下のドキュメントをご覧ください。

OpenBlocks IoT Family データハンドリングガイド

OpenBlocks IoT Family データハンドリング設定リファレンスガイド

先にご理解いただきたいのですが、メッセージの双方向化に関しては、現在は、
MQTT系の接続サービス、TCP接続、当社PD Exchangeとの接続、当社独自仕様のWebサーバーのみでご利用いただけます。対応の詳細に関しましては、上記ドキュメントを参照してください。

OBDN技術ブログによる動作検証は、該当するデバイスやソフトウェアの動作について、保証およびサポートを行うものではありません。
内容に関するご指摘などありましたら、ブログ記事の担当までご連絡下さい。


1.Userデバイスの作成と送受信設定


Userデバイスの作成
 サービスメニューより、基本機能、Userデバイスタブ、の順に選択します。
ユーザーメモを入力し、保存すると、一覧に作成したUserデバイスが表示されます。

送受信設定
サービスメニューより、IoTデータ、送受信設定の順に選択します。
今回は、例としてAzure IoT Hubへの送受信設定を行っています。


 上記、送受信の設定を行った後、Userデバイスに対してのデバイス設定を行います。
デバイス設定メニュー内の送受信設定にて、iothubにチェックを入れ、接続に必要なデバイスIDおよびデバイスキーを入力します。
なお、Azure IoT Hubへの接続方法に関しては、以下のOBDNマガジンの記事にて解説を行っています。

OpenBlocks IoT シリーズをMicrosoft Azure IoT Hubへ接続し、Stream Analyticsを経由してPowerBIへデータを受け渡す手順について

下流方向への制御を行うには、「受信設定」を「有効」にします。
制御に使用する、Unixドメインソケットは、作成したデバイス番号より生成されます。

2.送受信に使用するUnixドメインソケット

制御メッセージの送受信に使用するUnixドメインソケットは、データハンドリングガイドにて解説しています。

PD Repeaterからの下流方向

¥0/pd_handler/<デバイス番号>.sock

PD Repeaterへの上流方向


¥0/pd_repeater/<デバイス番号>.sock

今回の設定例では、デバイス番号はuserdev_0000001となります。

3.Cで記述した下流方向制御アプリケーションのサンプル

本サンプルは、Cで記述した下流方向制御アプリケーションのサンプルです。
PD Repeater側がクライアントとなり、下流方向の制御アプリケーションはUnixドメインソケットを使うサーバ側の実装となります。
本プログラムは下流制御機能の評価用に作成されたもので、Userデバイスとして定義されるUnixドメインソケットのuserdev_0000000からuserdev_0000031までをポーリングして受信したデータを表示します。

# apt-get update
# apt-get install libssl-dev

インストールされていないならば、libssl-devパッケージをインストールし、

# make rd_header

シンプルにmakeしてください。

/*
 *  Copyright (c) 2018
 *       Plat'Home CO., LTD. <support@plathome.co.jp>. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Plat'Home CO., LTD. nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <openssl/md5.h>
#ifdef __linux__
#include <sys/epoll.h>
#endif

#define N_DEVICE 32
#ifdef __linux__
#define ROOT_PATH "/pd_handler"
#else
#define ROOT_PATH "/tmp/pd_handler"
#endif
#define LOCALNAME "userdev_"

#define SOCKET_BUF 4096

#ifdef __linux__
#define MAX_SOCKET_CONN 512
#else
#define MAX_SOCKET_CONN 64
#endif

#define SELECT_TIMEOUT 0
#define SELECT_UTIMEOUT 1
#define EPOLL_WAIT 1

struct s_socket_t {
int fd;
struct sockaddr_un addr;
};

struct c_socket_t {
unsigned char id;
int fd;
struct sockaddr_un addr;
};

int main(int argc,char *argv[])
{
int i,j,k,m,n,rc;
char *buf, *socket_buf, *msg_buf;

#ifdef __linux__
int num_fd, epfd;
struct epoll_event ev;
struct epoll_event events[MAX_SOCKET_CONN];
int fcntl_flag;
#else
struct timeval timeout;
fd_set rd_set, rd_set_orig;
#endif
struct timeval tv;
socklen_t len;
struct s_socket_t *ss[N_DEVICE];
struct c_socket_t *cs[MAX_SOCKET_CONN - N_DEVICE];
u_int16_t topic_size;
char *p, *topic, *topic_p, *msg_buf_p;
unsigned char pl;
unsigned char ph;
unsigned char cloud_id, sub_id;
unsigned char md5[MD5_DIGEST_LENGTH];
char hash[MD5_DIGEST_LENGTH * 2 + 1];

buf = (char *)malloc(sizeof(char)*BUFSIZ);
if(!buf) {
perror("buf = malloc()");
exit(1);
}

socket_buf = (unsigned char *)malloc(sizeof(unsigned char)*SOCKET_BUF);
if(!socket_buf) {
perror("socket_buf = malloc()");
exit(1);
}

msg_buf = (unsigned char *)malloc(sizeof(unsigned char)*SOCKET_BUF);
if(!msg_buf) {
perror("msg_buf = malloc()");
exit(1);
}

topic = (char *)malloc(sizeof(char)*BUFSIZ);
if(!topic) {
perror("socket_buf = malloc()");
exit(1);
}

#ifdef __linux__
if ((epfd = epoll_create(MAX_SOCKET_CONN)) < 0) {
perror("epfd = epoll_create()");
exit(1);
}
#else
FD_ZERO(&rd_set_orig);
#endif

for(i=0;i<N_DEVICE;i++) {
ss[i] = (struct s_socket_t *)malloc(sizeof(struct s_socket_t));
if(!ss[i]) {
perror("ss[] = malloc()");
exit(1);
}
memset(ss[i], 0, sizeof(struct s_socket_t));

ss[i]->fd = socket(AF_UNIX, SOCK_STREAM, 0);
ss[i]->addr.sun_family = AF_UNIX;
#ifdef __linux__
snprintf(ss[i]->addr.sun_path + 1,sizeof(ss[i]->addr.sun_path) - 1,"%s/%s%07d.sock",
ROOT_PATH, LOCALNAME, i);
unlink(ss[i]->addr.sun_path);

rc = bind(ss[i]->fd, (struct sockaddr *)&ss[i]->addr, sizeof(sa_family_t) + strlen(ss[i]->
addr.sun_path + 1) + 1);
#else
snprintf(ss[i]->addr.sun_path,sizeof(ss[i]->addr.sun_path),"%s/%s%07d.sock",
ROOT_PATH, LOCALNAME, i);
unlink(ss[i]->addr.sun_path);

rc = bind(ss[i]->fd, (struct sockaddr *)&ss[i]->addr, sizeof(ss[i]->addr));
#endif
if (rc) {
snprintf(buf,sizeof(char)*BUFSIZ,"failed to bind(%s)",ss[i]->addr.sun_path);
perror(buf);
exit(1);
}

rc = listen(ss[i]->fd, N_DEVICE);
if (rc) {
snprintf(buf,sizeof(char)*BUFSIZ,"failed to listen(%s)",ss[i]->addr.sun_path);
perror(buf);
exit(1);
}

#ifdef __linux__
memset(&ev, 0, sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = ss[i]->fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, ss[i]->fd, &ev);
#else
FD_SET(ss[i]->fd,&rd_set_orig);
#endif
printf("listen %s/%s%07d.sock\n", ROOT_PATH, LOCALNAME, i);
}

for(i=0;i<(MAX_SOCKET_CONN - N_DEVICE);i++) {
cs[i] = (struct c_socket_t *)malloc(sizeof(struct c_socket_t));
if(!cs[i]) {
perror("cs[] = malloc()");
exit(1);
}
memset(cs[i], 0, sizeof(struct c_socket_t));
cs[i]->fd = -1;
}

while(1) {
#ifdef __linux__
num_fd = epoll_wait(epfd, events, MAX_SOCKET_CONN, EPOLL_WAIT);
for(i=0;i<num_fd;i++) {
n=1;
for (j = 0; j < N_DEVICE; j++) {
if (events[i].data.fd == ss[j]->fd) {
for(k=0;k<(MAX_SOCKET_CONN - N_DEVICE);k++) {
if(cs[k]->fd == -1) {
break;
}
}
if(k == (MAX_SOCKET_CONN - N_DEVICE)) {
printf("exceeded maximum fd(%d) for accept()", MAX_SOCKET_CONN - N_DEVICE);
}
else {
len = sizeof(cs[k]->addr);
cs[k]->fd = accept(ss[j]->fd, (struct sockaddr *)&cs[k]->addr, &len);
if (cs[k]->fd != -1 ) {
cs[k]->id = j;
fcntl_flag = fcntl(cs[k]->fd, F_GETFL, 0);
fcntl(cs[k]->fd, F_SETFL, fcntl_flag | O_NONBLOCK);
memset(&ev, 0, sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = cs[k]->fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, cs[k]->fd, &ev);
}
}
n = 0;
}
}
if(n) {
for(j=0;j<(MAX_SOCKET_CONN - N_DEVICE);j++) {
if ( events[i].data.fd == cs[j]->fd ) {
memset(socket_buf, 0, sizeof(char)*SOCKET_BUF);
rc = read(cs[j]->fd,(char *)socket_buf,SOCKET_BUF);
if(rc > 0) {
p = socket_buf;
cloud_id = *p++;
sub_id = *p++;
pl = (unsigned char)*p++;
ph = (unsigned char)*p++;
for(m=0;m<MD5_DIGEST_LENGTH;m++)
md5[m] = (unsigned char)*p++;
for(m=0; m<MD5_DIGEST_LENGTH;m++)
snprintf(hash + m + m, sizeof(char)*3, "%.2x", md5[m]);
topic_size = (u_int16_t)((ph & 0x00ff) * 256 + (pl & 0x00ff));
topic_p = topic;
for(m=0;m<topic_size;m++)
*topic_p++ = *p++;
*topic_p = 0x00;
msg_buf_p = msg_buf;
while(*p != 0x00)
*msg_buf_p++ = *p++;
*msg_buf_p = 0x0;
printf("subscrib: topic_size=%d topic=%s payload=%s md5=%s\n",
topic_size, topic, msg_buf, hash);
}
epoll_ctl(epfd, EPOLL_CTL_DEL, cs[j]->fd, &ev);
shutdown(cs[j]->fd,SHUT_RDWR);
close(cs[j]->fd);
cs[j]->fd = -1;
}
}
}
}
#else
timeout.tv_sec = SELECT_TIMEOUT;
timeout.tv_usec = SELECT_UTIMEOUT;

memcpy(&rd_set,&rd_set_orig,sizeof(rd_set_orig));

if(select(FD_SETSIZE,&rd_set,(fd_set *)NULL,(fd_set *)NULL,&timeout) == -1) {
perror("select()");
exit(1);
}

for(i=0;i<FD_SETSIZE;i++) {
if(FD_ISSET(i, &rd_set)) {
n=1;
for(j=0;j<N_DEVICE;j++) {
if ( i == ss[j]->fd ) {
for(k=0;k<(MAX_SOCKET_CONN - N_DEVICE);k++) {
if(cs[k]->fd == -1) {
break;
}
}
if(k == (MAX_SOCKET_CONN - N_DEVICE)) {
printf("exceeded maximum fd(%d) for accept()", MAX_SOCKET_CONN);
}
else {
len = sizeof(cs[k]->addr);
cs[k]->fd = accept(i, (struct sockaddr *)&cs[k]->addr, &len);
if (cs[k]->fd != -1 ) {
setsockopt(cs[k]->fd, SOL_SOCKET, SO_RCVTIMEO,(char*)&timeout, sizeof(struct timeval));
FD_SET(cs[k]->fd, &rd_set_orig);
cs[k]->id = j;
}
}
n = 0;
}
}
if(n) {
for(j=0;j<(MAX_SOCKET_CONN - N_DEVICE);j++) {
if (i == cs[j]->fd ) {
memset(socket_buf, 0, sizeof(char)*SOCKET_BUF);
rc = read(i,(char *)socket_buf,SOCKET_BUF);
if(rc > 0) {
p = socket_buf;
cloud_id = *p++;
sub_id = *p++;
pl = (unsigned char)*p++;
ph = (unsigned char)*p++;
for(m=0;m<MD5_DIGEST_LENGTH;m++)
md5[m] = (unsigned char)*p++;
for(m=0; m<MD5_DIGEST_LENGTH;m++)
snprintf(hash + m + m, sizeof(char)*3, "%.2x", md5[m]);
topic_size = (u_int16_t)((ph & 0x00ff) * 256 + (pl & 0x00ff));
topic_p = topic;
for(m=0;m<topic_size;m++)
*topic_p++ = *p++;
*topic_p = 0x00;
msg_buf_p = msg_buf;
while(*p != 0x00)
*msg_buf_p++ = *p++;
*msg_buf_p = 0x0;
printf("subscrib: topic_size=%d topic=%s payload=%s md5=%s\n",
topic_size, topic, msg_buf, hash);
}
shutdown(i,SHUT_RDWR);
close(i);
FD_CLR(i,&rd_set_orig);
cs[j]->fd = -1;
}
}
}
}
}
#endif
}
}




4.[補足] Cで記述したPD Repeaterへの上流方向へのデータ送信

他の記事で上流方向へデータを送信するカスタムハンドラについて記述していますが、FW3.xでの書き込み方法としては、以下のコードを参考にしてください。

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

/*
write data to unix domain socket */
write_uds( uint8_t *jbuf ) {
    int uds, ret;
    socklen_t socklen;
    struct sockaddr_un addr;

    uds = socket(AF_UNIX, SOCK_STREAM, 0);
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path+1, "/pd_repeater/userdev_0000001.sock");
    socklen =  sizeof(sa_family_t) + strlen(addr.sun_path+1)+1;
    ret = connect(uds, (struct sockaddr *)&addr, socklen);

    if (ret < 0) {
        int err = errno;
        printf("connect NG (errno: %d)\n", err);
       close(uds);
       return 1;
    }
    else {
        printf("connect OK\n");
    }
    write(uds, jbuf, strlen(jbuf));
   
    close( uds );
    return 0;
}

5.さいごに

下流方向の制御に関して、PD Repeaterがクライアントであり、制御アプリケーションはサーバとしての実装が必要になります。

また、WebUIに連動して、起動、停止を行うためには、プロセスを外部から制御する実装を行ってください。





2016-08-05

OpenBlocks IoT シリーズをMicrosoft Azure IoT Hubへ接続し、Stream Analyticsを経由してPowerBIへデータを受け渡す手順について

OpenBlocks IoT シリーズの基本ソフトウェア 1.0.10より、Azure IoT HubおよびAzure Event HubへのWebUIでの接続設定に対応しました。
本記事では、Azure IoT Hubへの接続の手順、および、Stream Analytics jobを経由したPower BIへのデータの受け渡し手順について、解説しています。




OBDN技術ブログによる動作検証は、該当するデバイスやソフトウェアの動作について、保証およびサポートを行うものではありません。
内容に関するご指摘などありましたら、ブログ記事の担当までご連絡下さい。

<検証環境>
ゲートウェイ
 OpenBlocks IoT BX1 Debian wheezy 7.8 / kernel 3.10.17-poky-edison
 IIJmio 高速モバイル/D SIM
センサー
 FWM8BLZ02A-109047 温度/3軸加速度センサービーコン (BLE)
クラウド
 Microsoft Azure

1. 事前準備


・アカウント

Microsft Azureのアカウントが必要です。
Microsoft Azureのサービスポリシーに従って適切に設定してください。

注意点としてはAzure IoT Hubや、Stream Analyticsなどは個人アカウントでも開始できますが、Power BIを使用する場合は、試用であっても学校または職場のアカウントが必要となり、gmailでの個人登録アカウントなどではサインアップできません。
また、職場のアカウントで無料サインアップ出来ない場合は、職場のMS Azure管理権限のある担当に相談してください。

2.Microsoft Azure IoT Hubのデプロイと設定


 Azureアカウントにサインインします。

 

新規メニューより、IoT Hubを検索します。



IoT Hubを選択し、作成を押し、IoTハブのパネルで名前、サブスクリプション、リソースグループ、場所を入力して、作成を押します。
IoTハブ名など、この先でリソース名として受け渡す可能性のあるものには"-"をつけないことをお勧めします。
例えば、Stream Analytics jobのクエリの記述パネルでは"-"は使用できません。

今回の設定例は以下の通り。

名前:
  obxbx1
価格とスケールティア
  S1 - Standard 400kメッセージ/ユニット/日 ( F1 Freeの場合は8kメッセージ/ユニット/日 )
IoT Hub ユニット
  1
Device-to-cloud パーティション
  4パーティション
サブスクリプション
  Microsoft Azure エンタープライズ
リソースグループ
  plathome-goto
場所
  東日本

ダッシュボードにピン留めする場合は、チェックボックスにチェックを入れます。

 

 デプロイが開始されます。



デプロイ完了を確認します。



3.Device Explorerの導入と設定


以下のドキュメントを参考に、導入と設定を進めていきます。

How to use Device Explorer for IoT Hub device

Getting Device Explorerの項目より、以下のDevice Explorerのダウンロードページを開きます

https://github.com/Azure/azure-iot-sdks/releases


ページの最下部にある、Downloadsより、

SetupDeviceExplorer.msi

をダウンロードし、インストールして下さい。

実行すると、以下のパネルが表示されます。



上記で作成した、Azure IoT Hubより、「すべての設定」から、「共有アクセスポリシー」のパネルを呼び出します。



iothubownerを選択します。



接続文字列ープライマリーキーをコピーします。



コピーした文字列を、Device ExplorerのIoT Hub Connection Stringの入力パネルに貼り付け、Updateを押します。



4.OpenBlocks IoT 基本ソフトウェアのWebUI設定

 
WebUIの初期設定、ネットワーク設定、BLEセンサービーコンの登録


OpenBlocks IoT Family向けWEB UIセットアップガイド

に従って設定してください。
今回は富士通コンポーネントの温度/3軸加速度センサー付きのBLEユニットを使用しています。

出力される項目が変わりますが、標準で対応しているセンサー付きのBLE製品としては、FCL製品以外では、CC2650STK/CC2451 Sensor Tagがあります。
また今後他社製品(ALPS/OMRON/他)にも対応予定ですが、ALPS IoT Smart module開発キット(BLE)は8月中旬以降リリース予定の1.0.11で、それ以外のデバイスは1.0.11より先のリリースで対応を予定しています。
アドバタイズデータにセンサーデータを載せるタイプの製品は、データであれば、ビーコン送信機能で送信可能ですが、送信するJSON上のデータが一連のデータ列のままdataとして名前付けされますので、クラウド側で変換する必要があります。

また、基本ソフトウェアのPD Handlerで標準対応している、EnOceanセンサ、Wi-SUNによるBルートでの電力計測データも利用出来ます。EnOceanセンサは変換モードを有効にすることで多くのアーミンセンサでプロファイルに応じてデータを変換可能ですが、対応していないものは変換前のデータのままrawモードでの送信となりますので、クラウド側で変換が必要となります。

カスタムハンドラを作成される場合は、最終的に扱いたいデータの構造でJSON形式の送信データを構成するといいでしょう。

・Azure IoT Hubへの送信設定


以下のドキュメントに従って設定を進めていきます。

OpenBlocks IoT Family向け データ収集ガイド


先に、送信先設定を行います。
MS Azure IoT Hubを「使用する」にチェックし、各項目を設定してください。

インターバル
 収集したデータを送信する間隔です。
 この例ではデータの送信確認のために5秒としています。
 (実際には5分~1時間以上に設定されることが多いと思います)
有効時間
 データ送信できない場合において、保持する時間を設定します。
 0を指定した場合、データ送信が完了するまで保持し続けます。
ドメイン名
 送信するIoT Hubのホスト名を設定します。
 デフォルトでは、azure-devices.net が入力されています。
ポート番号
 送信先のポート番号を設定します。通常は5671から変更する必要はありません。
IoT Hub名
 上記で設定したIoT Hub名を入力します。

デバイス一括設定
 ビーコン及びデバイスの送信対象設定が”送信する”となっている各対象の
 送信先設定を一括で有効/無効を選択できます。



デバイス情報送信設定を行います。
送信対象を、「送信する」にチェックし、各項目を設定してください。

センサー信号強度[dbm]
 センサーに信号強度を設定できる機種の場合、設定したい信号強度を入力します。
 設定した信号強度が無い場合、近似値またはデフォルト値が設定されます。
取得時間間隔[ms]
 センサーからデータを取得する時間間隔を数字で設定します。単位はmsecです。送信先設定
 送信先をチェックします。同時に2か所までの送信が可能です。
 IoT Hubにチェックします。
デバイスID(IoT Hub)
 デバイスIDを設定します。今回はBLEデバイスのMACアドレスを使用しています。

 上記で設定したIoT Hub名を入力します。
デバイスキー
 IoT Hubに送信する際のデバイスキーを設定します。
 今回デバイスキーは下記の手順によりDevice Explorerで取得しています。

デバイスキーの取得方法

Device Explorerの「Management」タブを開き、「Create」ボタンより入力パネルを出し、上記のWebUIにて使用するデバイスIDを入力して「Create」を押します。



「Device Created」と表示されたパネルの文字列をCtrl-A/Ctrl-Cとしてコピーします。



この文字列に含まれる、PrimaryKeyをデバイスキーとしてWebUIの画面で入力し、設定を保存してください。



5.Device Explorerによる通信確認


先に、WebUIの収集ログにより、pd-handler-stdout.logおよびpd-emitter-lite.logを確認して、センサービーコンとの通信確認、およびAzure IoT Hubとの通信確認を行います。

pd-handler-stdout.logの確認



pd-emitter-lite.logの確認




次に、Device Explorerの「Data」タブより「Monitor」ボタンを押して確認してください。




6.Power BIの準備


Power BIにサインイン出来るように、準備をします。
すでに、お使いの方はこの作業は必要ありません。
以下のページより、サインアップを進めてください。



冒頭に書いた通り、職場または学校のアカウントであれば無料でサインアップできます。
無料の使用範囲は、データ容量1GB/ユーザー、ストリーミングデータが10k行/時間となり、グループでのチームコラボレーションは出来ません。
職場のアカウントでサインアップ出来ない場合は、職場の担当者に相談して下さい。




6.Stream Analytics jobのデプロイと設定


Azureのダッシュボードより、Stream Analytics jobを追加します。
 


 作成を押します。

 

ジョブ名、サブスクリプション、リソースグループ、場所を設定し、作成を押します。
今回の設定例は以下の通りです。

名前:
  streamtobi
サブスクリプション
  Microsoft Azure エンタープライズ
リソースグループ
  plathome-goto
場所
  東日本



デプロイを待ちます。



作成済と表示されたのち、ジョブトポロジを設定します。



入力のパネルをクリックし、、「+追加」を押して設定を行います。



入力のエイリアスを入力し、ソースをIoT Hubにすると、各項目は自動的に設定されますので、問題がなければ作成を押してください。(今回は入力のエイリアスはobxbx1と設定しています)



出力のパネルをクリックし、、「+追加」を押して設定を行います。



出力エイリアスを設定して、シンクのメニューよりPower BIを選択します。
今回は、出力エイリアスはpowerbibx1と設定しています。



接続の承認ボタンを押すと、サインイン画面が出ますので、サインインを行うと承認が終了しますので、データセット名とテーブル名を入力して作成を行います。
今回は、データセット名をfclsensor、テーブル名をsensorとしています。



クエリをクリックして、編集し、保存します。



変換などを行わいない場合は、以下のように入出力のエイリアスを設定するだけです。


SELECT
    *
INTO
    powerbibx1
FROM
    obxbx1



全て設定が終了したら、開始ボタンを押すとストリーミングジョブを開始します。







ストリーミングジョブが正常に開始されました、というメッセージを確認してください。



7.Power BIでのデータの確認と表示


Power BIにサインインすると、データセットに設定したデータセット名が表示されています。




表示されたデータセット名を押すと視覚化のメニューが出ます。



温度と時間を選択し、折れ線グラフを表示させてみます。



上記の画面が確認できれば、今回のチュートリアルは終了です。

8.さいごに


サンプル画面数が非常に多い記事となりましたが、Azureでの作業はデプロイ時間が短いこともあり、作業自体は非常に短時間で終わると思います。

とりあえず、tipsとしては"-"を使わない。これにつきます。