ラベル OpenBlocks関連 の投稿を表示しています。 すべての投稿を表示
ラベル OpenBlocks関連 の投稿を表示しています。 すべての投稿を表示

2018-06-21

[VX2] Movidius Neural Compute Stick (Myriad2) NCSDK2の導入方法

Movidius Neural Compute Stickは、VPUと呼ばれる、ディープラーニング推論のためのアクセラレータです。
本製品は、Movidius Myriad2 VPUを使用したもので、開発用としてUSBスティックタイプのものが用意されています。

今回の記事は、本製品のSDKである、NCSDKの最新バージョンである、V2.04をOpenBlocks IoT VX2(VX1)で利用するためのTipsとなります。
( 2018/7/11 時点では、2.0.5がリリースされており、Bug Fixに記述がありませんが、アプリケーション実行後の再起動時にdevice Stalledの状態になった場合、デバイスが復帰するようになりました。
2018/7/15 追記 2.05においては、別の致命的なエラーが出ることを確認しています。現状では1.x系を使った方がいいかもしれません。
)

Intel® Movidius | Neural Compute Stick | AI Programming



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

<検証環境>
OpenBlocks IoT VX2 FW3.1



1. ncsdk2のインストール

ncsdk v2のブランチを利用するには、以下のように指定します。

git clone -b ncsdk2 https://github.com/movidius/ncsdk.git

先に、ncsdkディレクトリ下で

./install.sh

とするのですが、NCSDKは、Ubuntu16.04またはRasbian9に対応していますが、debian9などの他のディストリビューションには対応していません。
そのため、今回はRasbian9と認識させてインストールさせます。
install.shの以下の部分(30行目以降)に変更を加えます。

#    OS_DISTRO="${DISTRO:-INVALID}"
#    OS_VERSION="${VERSION:-255}"
    OS_DISTRO="raspbian"
    OS_VERSION="91"

このスクリプトが終了したら、api/src の下で、

make install

として、sdkをインストールしてください。

・tensorflowについて

インストールは一通り終わりますが、インストールされたpython用のtensorflowはno gpuバージョンであっても、現在配布されているバージョンのモジュールはAVX命令をenableとしてビルドしているため、そのままでは使用できません。
tensorflowは別途SSE2のみでビルドする必要があります。
(VX2でセルフコンパイルをするのはリソース的に厳しいため、他のdebian/amd64のPCなどでwhlファイルを作成することをおすすめします)

bazelを使ったインストールはtensorflowのサイトのfrom Sourcesに従って行います。

Installing TensorFlow from Sources

bazelをAPT repositoryよりインストールした場合は、0.14.1がインストールされますが、tensorflowのサイトサンプルのようにgit checkout r1.0のように指定すると、古いリリースのものがcheckoutされてbazelの古いバージョンのものが必要となります。

git clone https://github.com/tensorflow/tensorflow
cd tensorflow
git checkout v1.9.0

tensorflow-hubなども使えるよう、新しいリリースのものをcheckoutします。
bazelのオプションは今回は以下のように設定しました。

bazel build -c opt --copt=-msse4.2 //tensorflow/tools/pip_package:build_pip_package

ビルド、パッケージ作成、インストールが終わったら、以下のスクリプトがエラーなく動作することを確認してください。

# Python3
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

ここで、warningが出る場合は以下のモジュールアップデートが必要です。

pip3 install --upgrade h5py

・OpenCVについて

opencvもそのままでは不完全にインストールされるため、別途ソースコードからインストールしなおすか、暫定でpython対応だけでよいならば、

pip3 install opencv-python

としてインストールしてください。

opencvをビルドする場合のcmakeのオプションのサンプルは以下の通りです。

cmake -G "Unix Makefiles" --build . -D BUILD_CUDA_STUBS=OFF -D BUILD_DOCS=OFF \
-D BUILD_EXAMPLES=OFF -D BUILD_JASPER=OFF -D BUILD_JPEG=OFF -D BUILD_OPENEXR=OFF \
-D BUILD_PACKAGE=ON -D BUILD_PERF_TESTS=OFF -D BUILD_PNG=OFF -D BUILD_SHARED_LIBS=ON \
-D BUILD_TBB=OFF -D BUILD_TESTS=OFF -D BUILD_TIFF=OFF -D BUILD_WITH_DEBUG_INFO=ON \
-D BUILD_ZLIB=OFF -D BUILD_WEBP=OFF -D BUILD_opencv_apps=ON -D BUILD_opencv_calib3d=ON \
-D BUILD_opencv_core=ON -D BUILD_opencv_cudaarithm=OFF -D BUILD_opencv_cudabgsegm=OFF \
-D BUILD_opencv_cudacodec=OFF -D BUILD_opencv_cudafeatures2d=OFF -D BUILD_opencv_cudafilters=OFF \
-D BUILD_opencv_cudaimgproc=OFF -D BUILD_opencv_cudalegacy=OFF -D BUILD_opencv_cudaobjdetect=OFF \
-D BUILD_opencv_cudaoptflow=OFF -D BUILD_opencv_cudastereo=OFF -D BUILD_opencv_cudawarping=OFF \
-D BUILD_opencv_cudev=OFF -D BUILD_opencv_features2d=ON -D BUILD_opencv_flann=ON \
-D BUILD_opencv_highgui=ON -D BUILD_opencv_imgcodecs=ON -D BUILD_opencv_imgproc=ON \
-D BUILD_opencv_java=OFF -D BUILD_opencv_ml=ON -D BUILD_opencv_objdetect=ON \
-D BUILD_opencv_photo=ON -D BUILD_opencv_python2=OFF -D BUILD_opencv_python3=ON \
-D BUILD_opencv_shape=ON -D BUILD_opencv_stitching=ON -D BUILD_opencv_superres=ON \
-D BUILD_opencv_ts=ON -D BUILD_opencv_video=ON -D BUILD_opencv_videoio=ON \
-D BUILD_opencv_videostab=ON -D BUILD_opencv_viz=OFF -D BUILD_opencv_world=OFF \
-D CMAKE_BUILD_TYPE=RELEASE -D WITH_1394=ON -D WITH_CUBLAS=OFF -D WITH_CUDA=OFF \
-D WITH_CUFFT=OFF -D WITH_EIGEN=ON -D WITH_FFMPEG=ON -D WITH_GDAL=OFF -D WITH_GPHOTO2=OFF \
-D WITH_GIGEAPI=ON -D WITH_GSTREAMER=OFF -D WITH_GTK=ON -D WITH_INTELPERC=OFF -D WITH_IPP=ON \
-D WITH_IPP_A=OFF -D WITH_JASPER=ON -D WITH_JPEG=ON -D WITH_LIBV4L=ON -D WITH_OPENCL=ON \
-D WITH_OPENCLAMDBLAS=OFF -D WITH_OPENCLAMDFFT=OFF -D WITH_OPENCL_SVM=OFF -D WITH_OPENEXR=ON \
-D WITH_OPENGL=ON -D WITH_OPENMP=OFF -D WITH_OPENNI=OFF -D WITH_PNG=ON -D WITH_PTHREADS_PF=OFF \
-D WITH_PVAPI=OFF -D WITH_QT=ON -D WITH_TBB=ON -D WITH_TIFF=ON -D WITH_UNICAP=OFF \
-D WITH_V4L=ON -D WITH_VTK=OFF -D WITH_WEBP=ON -D WITH_XIMEA=OFF -D WITH_XINE=OFF \
-D WITH_LAPACKE=ON -D WITH_MATLAB=OFF ..


・NCSの動作確認

NCSデバイスを認識すると、hello_ncs.pyの結果が以下のように表示されます。

#cd ~/ncsdk/examples/apps/hello_ncs_py
# python3 hello_ncs.py
D: [         0] ncDeviceCreate:221      ncDeviceCreate index 0

D: [         0] ncDeviceCreate:221      ncDeviceCreate index 1

D: [         0] ncDeviceOpen:415        File path /usr/local/lib/mvnc/MvNCAPI-ma2450.mvcmd

I: [         0] ncDeviceOpen:421        ncDeviceOpen() XLinkBootRemote returned success 0

I: [         0] ncDeviceOpen:450        XLinkConnect done - link Id 0

D: [         0] ncDeviceOpen:464        done

I: [         0] ncDeviceOpen:466        Booted 1.1-ma2450 -> VSC

I: [         0] getDevAttributes:287    Device attributes

I: [         0] getDevAttributes:290    Device FW version: 2.4.2450.e4

I: [         0] getDevAttributes:292    mvTensorVersion 2.4

I: [         0] getDevAttributes:293    Maximum graphs: 10

I: [         0] getDevAttributes:294    Maximum fifos: 20

I: [         0] getDevAttributes:296    Maximum graph option class: 1

I: [         0] getDevAttributes:298    Maximum device option class: 1

I: [         0] getDevAttributes:299    Device memory capacity: 522081584

Hello NCS! Device opened normally.
I: [         0] ncDeviceClose:656       closing device

Goodbye NCS! Device closed normally.

NCS device working.



2.ncappzooのv2用ブランチの入手

ncappzooもv2用のブランチが用意されています。
以下のオプションにてgit cloneしてください。

git clone -b ncsdk2 https://github.com/movidius/ncappzoo.git

READMEに従って必要なgraphファイルを作成するとともに、gstreamerなど、要求されるモジュールは別途インストールしてください。

また、opencvは、linuxで利用する場合は、ディスプレイとしてXを使います。
OpenBlocks IoT VX2(VX1)にはディスプレイヘッドはありませんので、Xサーバを別に用意する必要があります。


stream_inferの実行例
( 液晶モニタがmonitorとして認識されている )


3.最後に

ストレージ(eMMC)の制約から、VX2でなくVX1を使う場合は、外付けのUSB HDDなどを使用して環境のビルドを行ってください。
また、セルフビルドを行う場合、以下のようにTMPDIRの設定が必要な場合があります。

export TMPDIR=/var/tmp

なお、本SDKおよびサンプルアプリケーションであるncappzooをインストールしたdockerコンテナを作成しておりますので、別途公開する予定です。

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に連動して、起動、停止を行うためには、プロセスを外部から制御する実装を行ってください。





2017-06-23

[.tested][IoT] Omron ヒューマンビジョンコンポ (HVC-P2) 評価用カスタムハンドラ

Omron HVC-P2は、人の状態を認識できる画像センサモジュールです。


HVC-P2|HVC(Human Vision Components )シリーズ|製品ラインナップ|オムロン人画像センシングサイト:+SENSING

SwapImage





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

<検証環境>
OpenBlocks IoT BX1/EX1/VX1 基本ソフトウェア Ver2.11 / kernel 3.10.17-poky-edison

githubに評価用ソースコードをcommitしましたので、ご利用ください。
本コードは、IoT基本ソフトウェア向けのカスタムハンドラとして動作します。
ユーザハンドラを定義して、WebUIにて送信先などの設定を行ってください。
すでに別のカスタムハンドラをご利用の場合は、ソースコードのunix domain socket名を変更する必要があります。

https://github.com/goto2048/hvcp2_for_openblocksiot


・注意点

OpenBlocks IoT BX1で使用する場合、内蔵のモデムが/dev/ttyACM0から始まるモデムデバイスとなります。
HVC P2も同様にttyACMxxというデバイスとして認識されるため、udev等で/dev/ttyACM7に固定しないと、本デバイスが先に認識されてしまう関係上、3G通信が出来なくなる可能性があります。
それぞれdmesgでデバイス名の確認をし、ソースコード上のSERIAL_PORTの定義を変更してください。


顔認識の最大数は35ですが、35人分のJSONデータは基本ソフトウェアの機能で送信出来るペイロードのサイズ(4KB)を超えますので、適宜、最大数を設定する/定義したkey名を短くするなどのパッチをソースコードに当ててください。

評価用として、gotoがquick hackしたものですので、未サポートとなります。


2016-08-26

OpenBlocks IoT シリーズのAzure Event Hubへの接続について


前回、Azure IoT Hubへの接続の手順、および、Stream Analytics jobを経由したPower BIへのデータの受け渡し手順について、解説しました。
今回は、IoT Hubではなく、Event Hubへ接続する方法について簡単に説明します。
なお、全体の流れは前回の作業に沿って行ったものですので、WebUIでのデバイスの登録や、Stream Analytics、PowerBIの設定についての詳細な説明は行いません。




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

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

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


Azureアカウントにサインインし、新規メニューより、Event Hubを検索します。



Event Hubを選択し、作成を押しすと、旧ポータルへ飛ばされますので、今回はそのまま旧ポータルで作業を行います。
IoTハブ名など、この先でリソース名として受け渡す可能性のあるものには"-"をつけないことをお勧めします。
例えば、Stream Analytics jobのクエリの記述パネルでは"-"は使用できません。

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

イベントハブ名
  obsevhub
リージョン
  東日本

名前空間は自動で入力されます。 obsevhubというハブ名の場合は、obsevhub-nsとなります。

 

作成が終わって以下の画面から、作成したEvent Hub名をクリックします。

 

共有アクセスポリシーを作成します。
今回はtestという名前とし、権限を設定し、画面下部の「保存」を押します。




共有アクセスキー生成コンポーネントにある、ポリシー名、プライマリーキーを、次の項目で説明する、WebUIのSASキーの項目へ入力します。



Event Hubでの定作業は以上で終了です。

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


設定の全体の流れは前回の記事を参照してください。
作業として異なるのは、Azure IoT Hubへの送信が、Azure Event Hubへの送信となることです。 

・Azure Event Hubへの送信設定


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

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


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

インターバル
 収集したデータを送信する間隔です。
 この例ではデータの送信確認のために5秒としています。
 (実際には5分~1時間以上に設定されることが多いと思います)
有効時間
 データ送信できない場合において、保持する時間を設定します。
 0を指定した場合、データ送信が完了するまで保持し続けます。
ドメイン名
 送信するIoT Hubのホスト名を設定します。
 デフォルトでは、servicebus.windows.net が入力されています。
名前空間
 上記の管理ポータルで作成された名前空間を使用してください。
ポート番号
 送信先のポート番号を設定します。5670となっていますが、5671としてください。

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




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

センサー信号強度[dbm]
 センサーに信号強度を設定できる機種の場合、設定したい信号強度を入力します。
 設定した信号強度が無い場合、近似値またはデフォルト値が設定されます。
取得時間間隔[ms]
 センサーからデータを取得する時間間隔を数字で設定します。単位はmsecです。送信先設定
 送信先をチェックします。同時に2か所までの送信が可能です。
 Event Hubにチェックします。
Event Hub名
 Event Hub名を設定します。今回はobsevhubとなっています。
SASポリシー
 管理ポータルで作成した共有アクセスポリシーの名前を入力します。
SASキー
 管理ポータルで作成された共有アクセス生成キーコンポーネントよりプライマリーキーを
 コピーし、入力します。




保存を押し、収集ログタブにて、Event Hubへデータが送信されていることを確認してください。



4.Stream Analytics jobの設定について


前回との変更点として、入力がIoT HubからEvent Hubになり、リソース名も変わります。
また、サービス名を変更した場合は、クエリのFROMの記述も変更します。

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


Power BIにサインインすると、データセットに設定したデータセット名が表示されています。
以下の画面では、Stream Analyticsでデータセット名を変えていますので、前回と違うデータセットが表示されています。





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

6.さいごに


今回は、Event Hubへ変更した部分だけを説明しました。
WebUIでのポート番号だけは5671であることをご確認ください。



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としては"-"を使わない。これにつきます。