arrow-right hamburger logo-mark social-facebook social-github social-twitter
2017.09.21

Node.jsとNefry BTで監視システムを作ろう! for Pepper #iotlt

のびすけ

Nefry BT
    このエントリーをはてなブックマークに追加  

こんにちは、のびすけです。

IoTLT vol31で話をしたNefry BT(ESP32系ボード)を使ってネットワークカメラを作ってみたいと思います。

発表資料もご参照ください。

Pepper連携の監視システム? #iotlt by n0bisuke

Nefry BTのハンズオンも開催しますので合わせてご覧ください。

やりたいこと

Pepperを監視したい。

謎ですが、Pepperを監視したいです。

IoTLT vol31で話をした内容ですが、Pepperが動いてくれないので、Pepperがいつ動き出しても大丈夫なようにPepperを見守りします。

巷では「Pepperが見守り」だったり、「Pepperが防犯」みたいな先進的な取り組みがありますが、僕はあえてPepperを見守ります。

完成イメージ

こんな感じで見守ります。

そして、外からでもブラウザでPepperを見守るストリーミングサービスっぽいものを作ります。

使うもの

デバイスはNefry BTとGroveカメラの二つです。 (ちょっとジャンパワイヤ使います。)

Nefry BT

ESP32ベースのArduino互換開発ボードです。 千石電商さんでも購入できます。

Grove シリアルカメラキット

Grove対応のカメラです。

スイッチサイエンスさんなどで購入できます。

構成イメージ

カメラで撮影した画像をNefry BT経由でサーバーにアップロードします。 アップロードされた画像はNode.jsで立てたサーバーで受け取り、WebSocket(Socket.io)でブラウザに配信します。

作り方

3つに分けて紹介します。

デバイス(Nefry BT)で撮影した画像をサーバーに送りサーバーからブラウザに配信します。

データの流れ的にデバイス -> サーバー -> ブラウザという流れです。

全体のコードはこちらのGitHubリポジトリにあります。

デバイス側 - Arduino

デバイス側はGrove - Serial Camera Kitの公式Wikiにあるサンプルコードをもとに作っています。

dotstudioのものづくりアーティストうこさんがメインで作ってくれました。

少し長いのでGitHubのリポジトリを参照しましょう。

このプログラムをNefry BTに書き込みましょう。

hostの箇所に画像アップロード先のサーバーを指定します。

配線

配線はD3,D4に繋ぎます。

プログラム的には

#define SEREAL_RX 19 //Nefry BT D3
#define SEREAL_TX 18 //Nefry BT D4

の部分が該当します。NefryBTの仕様書を見ると分かりますが、ESP32のGPIO 19番がNefryBTのD3にあたり、18番がD4になります。

サーバー側 - Node.js

Node.jsで画像を受信するサーバーを作ります。

無難にSocket.ioとexpressを使います。 Node.jsのバージョンは8.4です。 7系以前だと動かない可能性があります。

npm i --save socket.io express
//server.js
'use strict';

const fs = require('fs');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const express = require('express');
const PORT = process.env.PORT || 3000;
app.use(express.static(__dirname));

const {promisify} = require('util');
const writeFileAsync = promisify(fs.writeFile);

io.on('connection', (socket) => console.log('a user connected')); //socket.ioのコネクション

app.get('/', (req, res) => res.sendFile('./index.thml'));
app.post('/', (req, res) => {
    let buffers = [];
    let cur = 0;
    const len = parseInt(req.headers['content-length'], 10);

    req.on('data', (chunk) => {
        buffers.push(chunk);
        cur += chunk.length;
        console.log(`Downloading...${(100.0 * cur / len).toFixed(2)}%`);
    });

    req.on('end', async () => {
        console.log(`\n[done] Image upload`);
        req.rawBody = Buffer.concat(buffers);
        const base64image = req.rawBody.toString('base64'); //base64変換
        await writeFileAsync('./img.jpeg', req.rawBody, 'utf-8')
        console.log(`[done] Image Save`);
        io.sockets.emit('new image',base64image); //画像送信
    });

});

http.listen(PORT, () => console.log(`listening on *:${PORT}`));

通常Expressを使う際にbody-parserをよく使いますが、今回はあえて使わずに、dataイベントchunkを拾って画像アップロードのプログレス表示を実装しています。

Nefry BTも含めてこういったマイコンボードはスマートフォンやPCに比べるとCPUパワーが弱いため、画像アップロードに時間がかかることがあります。

サーバー側でどれくらいのデータ送信が完了しているのかの進捗が分かると安心して開発できます。 画像を受信したらtoString('base64')でBase64の文字列に変換をしています。

io.sockets.emit()の箇所では変換した文字列をSocket.io経由でブラウザに配信しています。

ブラウザ側 - JavaScript

ブラウザ側では、文字列で送られた画像データを受け取り、Canvasに流し込みます。 HTML側ではid="myCanvas"のcanvasを作り、socket.ioの読み込みと以下で作るapp.jsの読み込みをします。

<html>
<head>
    <meta charset="utf-8" />
    <title>Nefry BT Camera</title>
</head>

<body style="background-color:#D0D0D0;">
    <canvas id="myCanvas" width="640" height="480" style="background-color:#FFFFFF;"></canvas>
    <script src="/socket.io/socket.io.js"></script>
    <script src="/public/app.js"></script>
</body>
</html>

draw()では、canvasにdrawImage()で画像を描画します。 Base64の場合はsrcが画像パスではなくdata:image/jpeg;base64,~~~~~という指定方法です。

// /public/app.js
'use strict';

const socket = io();

const draw = (imageData = '') => {
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    const img = new Image();
    img.src = `data:image/jpeg;base64,${imageData}`; //基本base64の文字列
    if(imageData === 'init') img.src = `./img.jpeg`; //初期実行時のみサーバーのimg.jpegを取得
    img.onload = () => {
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0, 640, 480);
    }
    console.log(`update ${new Date()}`);
}

socket.on('new image', draw); //画像更新時
draw('init'); //初期実行

socket.on('new image', draw)でサーバーからデータが送られてくるたびにdraw()を実行します。

使ってみた感想

1週間ほど動かしていますが、問題なくずっと稼働写真を送り続けてくれるので意外とラズパイなどで作るシステムより安定しているかもしれません。

あと、発表のオチだったのですが、Pepperの調子が悪く被写体が動かないので成功してるのか分かりにくいです苦笑

まとめ

Nefry BTとNode.jsを使ってネットワークカメラを作ることができました。

GroveのカメラとNefry V2を使って過去に【大島薫×IoT】浮気男を社会的に制裁するマシーンを作ってみたこともありましたが、ネットワークカメラを作りたい需要はそこそこあると思うので、その際の参考になれば幸いです。

    このエントリーをはてなブックマークに追加