Mbed OS version of IoT.js implementation running on GR-PEACH

ビルド方法

ビルド環境セットアップ

Host PC要件 : Ubuntu 16.04 (64-bit)



1. 以下のコマンドを実行し、下表に示すツールをインストールします。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install [Package]
PackageVersion
build-essential12.1ubuntu2
gyp0.1+20150913git1f374df9-1ubuntu1
mercurial3.7.3-1ubuntu1
cmake3.5.1-1ubuntu3
git1:2.7.4-0ubuntu1.4
python2.72.7.12-1ubuntu016.04.3
valgrind1:3.11.0-1ubuntu4.2
python-pip8.1.1-2ubuntu0.4

2. 以下のコマンドを実行してMbed OS用ビルドツール mbed CLIをインストールします。

$ sudo -H pip install mbed-cli

3. GNU Arm Embedded Toolchainをインストールします。

  • ダウンロードしたファイルを任意のディレクトリに展開します。
    (以下、展開先を${TOOLCHAIN}と記載します)

tar xjf gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2
  • ツールチェインのパスを設定します。

環境変数を使用する場合

$ export GCC_ARM_ROOT=${TOOLCHAIN}/gcc-arm-none-eabi-6-2017-q2-update/bin
$ export PATH=$GCC_ARM_ROOT:$PATH

mbed CLIで指定する場合

$ mbed config -G GCC_ARM_PATH $GCC_ARM_ROOT

iotjs環境のビルド

1. 本プログラムのクローン

下記コマンドを実行し、本プログラムをクローンしてください。

$ hg clone https://HinoNaka@os.mbed.com/users/HinoNaka/code/GR-PEACH_mbed-os-iotjs/

クローンが正常に終了すると、GR-PEACH_mbed-os-iotjs という名称のディレクトリが生成されますので、下記コマンドで当該ディレクトリへ移動してください。

$ cd GR-PEACH_mbed-os-iotjs

(Optional)
GR-PEACH-mbed-os-iotjsディレクトリを指す環境変数 ROOTを設定してください。以降の記載は本環境変数が設定されているものとします。

$ export ROOT=$(PWD)

2. 作業用ディレクトリ(e.g. work)を作成し、当該ディレクトリに移動してください。

$ mkdir -p work
$ cd work

(Optional)
作業用ディレクトリを指す環境変数 WORK を設定してください。以降の記載は本環境変数 が設定されているものとします。

$ export WORK=$(PWD)

3. iotjsのソースツリーを取得します。

$ git clone https://github.com/pando-project/iotjs

クローンが正常に終了するとiotjsというディレクトリが生成されますので、当該ディレクトリに移動してください。

$cd iotjs

コミットID: acae9c8b2d40e7598b8d39b630b79113ce880a7e を取得します。
(下記例では、あわせてposixというブランチを作成し、当該ブランチへ切り替えています)

$ git checkout -b posix acae9c8b2d40e7598b8d39b630b79113ce880a7e

4. mbed-osセットアップ
Mbed OSソースツリーを格納するフォルダを生成し、当該フォルダへ移動します。

$ mkdir -p $(WORK)/iotjs/src/platform/mbedos5
$ cd $(WORK)/iotjs/src/platform/mbedos5

Mbed OSのソースツリーをクローンします。

$ git clone -b mbed-os-5.9 https://github.com/ARMmbed/mbed-os.git

クローンが正常に終了するとmbed-osというディレクトリが生成されますので、当該ディレクトリに移動してください。

$ cd mbed-os

コミットID: 50bd61a4a72332baa6b1bac6caccb44dc5423309 を取得します。
(下記例では、あわせてposixというブランチを作成し、当該ブランチへ切り替えています)

$ git checkout -b posix 50bd61a4a72332baa6b1bac6caccb44dc5423309

5. sd-driverの取得
下記コマンドでmbedos5ディレクトリへ移動します。

$ cd $(WORK)/iotjs/src/platform/mbedos5

sd-driverをクローンします。

$ git clone https://github.com/ARMmbed/sd-driver.git

クローンが正常終了するとsd-driverディレクトリが生成されますので、当該ディレクトリへ移動します。

$ cd sd-driver

コミットID: c8ae38fb291e086232566b0f1372cfb69c277e84 を取得します。
(下記例では、あわせてposixというブランチを作成し、当該ブランチへ切り替えています)

$ git checkout -b posix c8ae38fb291e086232566b0f1372cfb69c277e84

6. mbed-gr-libsの取得
下記コマンドでmbedos5ディレクトリへ移動します。

$ cd $(WORK)/iotjs/src/platform/mbedos5

mbed-gr-libsをクローンします。

$ git clone https://github.com/d-kato/mbed-gr-libs

クローンが正常終了するとmbed-gr-libsディレクトリが生成されますので、当該ディレクトリへ移動します。

$ cd mbed-gr-libs

コミットID: d921d611d596ecaebaab49070ef82450c583309c を取得します。
(下記例では、あわせてposixというブランチを作成し、当該ブランチへ切り替えています)

$ git checkout -b posix d921d611d596ecaebaab49070ef82450c583309c

7. AsciiFONTの取得
下記コマンドでmbedos5ディレクトリへ移動します。

$ cd $(WORK)/iotjs/src/platform/mbedos5

AsciiFONTライブラリを追加します。

$ mbed add https://os.mbed.com/teams/Renesas/code/AsciiFont/

8. iotjs環境のビルド
下記コマンドでiotjs環境のビルドディレクトリに移動し、ビルドを実行します。

$ cd $(WORK)/iotjs
$ ./tools/build.py --no-snapshot

9. GR-PEACH用コードのコピー
(Optional) 既存のiotjs環境に上書きする場合、下記コマンドを実行してください。

$ rm -rf $(WORK)/iotjs/deps/posix
$ rm -rf $(WORK)/iotjs/src/ext-modules
$ rm -rf $(WORK)/iotjs/src/platform/mbedos5/iotjs_def.h

下記コマンドでGR-PEACH用コードをコピーします。

$ cp -pr $(ROOT)/src/iotjs/* $(WORK)/iotjs/

10. lwipパッチ適用
以下コマンドでlwipパッチを適用します。

$ cd $(WORK)/iotjs/src/platform/mbedos5/mbed-os
$ git apply lwip_improve.patch

11. ESP32 TRNG対応パッチ適用
以下コマンドでESP32のTRNGを活用するためのパッチを適用します。

$ cd $(WORK)/iotjs/src/platform/mbedos5/mbed-os
$ git apply trng_support.patch

12. Mbed OS版iotjs環境のビルド
下記コマンドを実行してMbed OS版iotjs環境をビルドします。
(リリースビルドの場合)

$ cd $(WORK)/iotjs/src/platform/mbedos5
$ make clean; make DEBUG=0

(デバッグビルドの場合)

$ cd $(WORK)/iotjs/src/platform/mbedos5
$ make clean; make DEBUG=1

13. ビルドが正常に終了すると、以下の通りバイナリが生成されます。
(リリースビルドの場合)

$ ls -l ${WORK}/iotjs/src/platform/mbedos5/BUILD/RZ_A1H/GCC_ARM-RELEASE/iotjs.bin

(デバッグビルドの場合)

$ ls -l ${WORK}/iotjs/src/platform/mbedos5/BUILD/RZ_A1H/GCC_ARM-DEBUG/iotjs.bin

実行方法

1. GR-PEACHにmicro SDカードを挿入します。

2. GR-PEACHのEthernetポートから遠い側のUSBポートとHost PCをマイクロUSBケーブルで接続します。

3. Host PCでGR-PEACHが『mbed:』ドライブとして認識されたら、iotjs.binをmbedドライブにコピーします。

4. ターミナルS/Wを立上げ、『mbed Serial Port』と接続します。
/media/uploads/HinoNaka/img1.jpg
シリアルポートは下記設定としてください。

Baud rate115200
Data8 bit
Paritynone
Stop1 bit
Flow controlnone

5. 正常に書込みが終了したらGR-PEACHのRESETボタンを押下します。

6. ターミナルS/W上に下記メッセージが表示されます。
/media/uploads/HinoNaka/img2.jpg

7. Enterキーを押下してコード入力モードに移行します。
/media/uploads/HinoNaka/img3.jpg

8. 実行するJavascriptコードをターミナルソフトウェアに貼り付けてEnterキーを押下すると、実行開始します。Javascriptコード例については、test/case.txtを参照ください。

test/case.txt

Committer:
Osamu Nakamura
Date:
2019-07-18
Revision:
4:52c937978bb3
Parent:
1:c3d69f309845

File content as of revision 4:52c937978bb3:

■テストケースについて

    次の行で囲まれた部分がそれぞれのテストケースになります。
    //boc ---------- (BeginOfCode)
    //eoc ---------- (EndOfCode)

・使用例)

1)下記の //boc の *前行* から //eoc の *次行* までをクリップボードにコピーしておきます。

//boc ---------- (BeginOfCode)
console.log("001");
//eoc ---------- (EndOfCode)

2)GR-Peach をリセット(or BREAK信号を送信)します。
    ※ GR-Peach には *必ず* microSD カードをセットしておいてください。

    IoT.js for mbed-os...
       mbed-os version: 5.9.3 
       build timestamp: MMM DD YYYY HH:MM:SS 

    input js fullpath here: 

3)[Enter]キーを送信して、コードの入力モードに移行します。

    input javascript code here: 

4)クリップボードの内容を貼り付けると処理が始まります。(手入力でもOK)

    input javascript code here: 
    //boc ---------- (BeginOfCode)
    console.log("001");
    //eoc ---------- (EndOfCode)

    ------- POSIX[  1][thr0] -->clock_getres(clk_id=6, res=0x2005aae4)
    ------- POSIX[  2][thr0] <--clock_getres(clk_id=6, res={0.001000000}) = 0(errno:0)
    ------- POSIX[  3][thr0] -->clock_gettime(clk_id=6, tp=0x2005aae4)
                :
                :



■いくつかのテストケース



//boc ---------- (BeginOfCode)
//■ process.platform が "mbedos" か確認
console.log( 'platform = ' + process.platform );
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ process 環境表示テスト
console.log( 'platform' + JSON.stringify( process, null, "    " ) );
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ カレントパスのテスト
console.log('current path: "' + process.cwd() + '"');
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ console 表示テスト (UTF8)
console.log("001");
console.log("002", "002");
var val = "string";
console.log("003", val);
val = 123;
console.log("004", val);
val = 3.14;
console.log("005", val);
console.log("nihongo", "UTF8");
console.log("nihongo", "日本語", "UTF8");
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ JSON.stringify、JSON.parse のテスト
var obj = {abc:10, pi:3.14, str:"moji"};
var json = JSON.stringify(obj);
console.log( json );
var copy = JSON.parse(json);
console.log( copy );
console.log( JSON.stringify(copy) );
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ chdir のテスト
function CHECK( number, val, must_be)
{
    console.log( 'check', number, ': must be', must_be, ':', val == must_be ? 'OK' : 'NG' );
}

var currentPath = process.cwd();
console.log('currentPath', process.cwd());

try {
    process.chdir('/sd');
} catch(err) {
    console.log('invalid path');
}

console.log('currentPath', process.cwd());
CHECK(1, process.cwd(), '/sd');

process.chdir(currentPath);
console.log('currentPath', process.cwd());

//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ Buffer のテスト (linux版と結果が同じであること)
var buf = new Buffer('hello world', 'ascii');
console.log(buf.toString('hex'));     // Prints: 68656c6c6f20776f726c64
console.log(buf.toString('base64'));  // Prints: aGVsbG8gd29ybGQ=
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ setTimeout() のテスト
setTimeout( function() {
    console.log( new Date().getTime(), 'kita' );
}, 3000);
console.log( new Date().getTime(), 'setTimeout started');
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ setInterval() のテスト
var count = 0;
var tid = setInterval( function() {
    console.log( new Date().getTime(), 'setInterval kita' );
    if( ++ count >= 5 ) clearInterval( tid );
}, 1000);
console.log( new Date().getTime(), 'setInterval started');
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ setTimeout(), setInterval() のテスト
setTimeout( function() {
    console.log( new Date().getTime(), 'setTimeout1 kita' );
}, 3500);
console.log( new Date(), 'setTimeout1 started');

setTimeout( function() {
    console.log( new Date().getTime(), 'setTimeout2 kita' );
}, 5500);
console.log( new Date().getTime(), 'setTimeout2 started');

var count = 0;
var tid = setInterval( function() {
    console.log( new Date().getTime(), 'setInterval kita' );
    if( ++ count >= 5 ) clearInterval( tid );
}, 2000);
console.log( new Date().getTime(), 'setInterval started');
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ fs モジュールのテスト (同期)
var fs = require('fs');
var fname = "/sd/_temp_code.js";
var stat = fs.statSync(fname);
console.log( JSON.stringify(stat) );
console.log( fs.readFileSync(fname) );
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ 正規表現のテスト
var re = /(\w+)\s(\w+)/;
var str = 'John Smith';
var newstr = str.replace(re, '$2, $1');
console.log(newstr);
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ 文字列分割テスト
var text = 'Some text\nAnd some more\r\nAnd yet\rThis is the end';
var lines = text.split(/\r\n|\r|\n/);
console.log(lines); // logs [ 'Some text', 'And some more', 'And yet', 'This is the end' ]
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ 多国語テスト
var text = 'Образец text на русском языке';
var regex = /[\u0400-\u04FF]+/g;

var match = regex.exec(text);
console.log(match[0]);        // logs 'Образец'
console.log(regex.lastIndex); // logs '7'

var match2 = regex.exec(text);
console.log(match2[0]);       // logs 'на' [did not log 'text']
console.log(regex.lastIndex); // logs '15'

// and so on
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
//■ 文字列操作テスト
var url = 'http://xxx.domain.com';
console.log(/[^.]+/.exec(url)[0].substr(7)); // logs 'xxx'
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■EventEmitterテスト
var EventEmitter = require('events').EventEmitter;

function asyncFunc() {
  var ev = new EventEmitter;
  console.log('in asyncFunc');
  setTimeout(function () {
    ev.emit('done', 'foo', 'bar');
  }, 1000);
  return ev;
}

var async = asyncFunc();
async.on('done', function(arg1, arg2) {
  console.log(arg1, arg2);
});
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■streamテスト

var Readable = require('stream').Readable;

function CHECK( number, val, must_be)
{
    console.log( 'check', number, ': must be', must_be, ':', val == must_be ? 'OK' : 'NG' );
}

var readable1 = new Readable();
var d = "";
var e = "";


readable1.on('error', function(err) {
  e += ".";
});

readable1.on('data', function(data) {
  d += data.toString();
});

readable1.on('end', function() {
  e += 'e';
});


readable1.pause();
readable1.push('abcde');
readable1.push('12345');
CHECK(1, d, '');
CHECK(2, e, '');

readable1.resume();
CHECK(3, d, 'abcde12345');
CHECK(4, e, '');

readable1.push('a');
readable1.push('1');
readable1.push('b');
readable1.push('2');
CHECK(5, d, 'abcde12345a1b2');
CHECK(6, e, '');

CHECK(7, readable1.isPaused(), false);
readable1.pause();
CHECK(8, d, 'abcde12345a1b2');
CHECK(9, e, '');
CHECK(10, readable1.isPaused(), true);

// Pause the readable again. This should do nothing.
readable1.pause();
CHECK(11, readable1.isPaused(), true);

readable1.push('c');
readable1.push('3');
readable1.push('d');
readable1.push('4');
CHECK(12, d, 'abcde12345a1b2');
CHECK(13, e, '');

readable1.resume();
CHECK(14, d, 'abcde12345a1b2c3d4');
CHECK(15, e, '');

readable1.push(null);
CHECK(16, d, 'abcde12345a1b2c3d4');
CHECK(17, e, 'e');

readable1.push('push after eof');
CHECK(18, d, 'abcde12345a1b2c3d4');
CHECK(19, e, 'e.');


// Create a readable stream without the new keyword.
var readable2 = Readable({encoding: 'utf8'});

// Read with irregular parameters from an empty stream.
CHECK(20, readable2.read(-2), null);
CHECK(21, readable2.read(0), null);

readable2.push('qwerty');
CHECK(22, readable2.read(6), 'qwerty');

// Throw not implemented Error when we trying to read less length data.
readable2.push('new-data');

var readable3 = new Readable();
var readable3End = false;
var paused = false;
var str = 'test';

readable3.on('data', function(data) {
  CHECK(23, paused, true);
  CHECK(24, data, str);
});

readable3.on('end', function() {
  readable3End = true;
});

readable3.pause();
readable3.push(str);
readable3.push(null);

setTimeout(function() {
  paused = true;
  readable3.resume();
}, 1000);


process.on('exit', function() {
  CHECK(25, readable2 instanceof Readable, true);
  CHECK(26, readable3End, true);
});

//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ dns モジュールの簡易テスト
var dns = require('dns');
dns.lookup('www.google.co.jp', function(err, address, family) {
    console.log('www.google.co.jp -> address: %j family: IPv%s', address, family);
});
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ nic モジュールの簡易テスト
var nic = require('nic');
// 一覧取得
var nics = nic.enumerate();
// 一覧確認
console.log( Object.keys( nics ).length + "個のNICが搭載されています" );
var index = 0;
Object.keys( nics ).forEach( function(key) {
    console.log( ++ index + "個目は:" + key );
});
// 使い方
var eth0 = nics["ETHERNET"];
if( eth0 ) {
    eth0.ifup();        // use DHCP
    eth0.ntpdate({ server:"ntp.nict.jp" });
    console.log( JSON.stringify( eth0.ifconfig() ) );
    console.log( new Date().toString() );
}
var wifi = nics["WIFI_BP3595"];
if( wifi ) {
    wifi.ifup( {
        wifi: {
            ssid: "<SSID>",
            password: "<PASSWORD>",
            security: "WPA_WPA2"    // 候補:WEP, WPA, WPA2, WPA_WPA2
        },
        ip: "192.168.10.123",
        netmask: "255.255.255.0",
        gateway: "192.168.10.1",
        dns: "8.8.8.8"
    });
    wifi.ntpdate();
    console.log( JSON.stringify( wifi.ifconfig() ) );
    console.log( new Date().toString() );
}
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ http クライアントのテスト

var cli = require('http');
require('fixup')('http');
cli.get({ host: 'www.google.com' }, function(res) {
	console.log( 'res.statusCode', res.statusCode);
	res.on('data', function(chunk) {
		console.log( chunk.toString() );
	});
	res.on('end', function() {
		console.log( 'end' );
	});
});

//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ https クライアントのテスト

var cli = require('https');
require('fixup')('https');
cli.get({ host: 'www.google.com' }, function(res) {
	console.log( 'res.statusCode', res.statusCode);
	res.on('data', function(chunk) {
		console.log( chunk.toString() );
	});
	res.on('end', function() {
		console.log( 'end' );
	});
});

//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ カメラ入力をJPEGファイルに保存する
var video = require('video');
var jpeg = require('jpeg');
var fs = require('fs');
var AlignedBuffer = require('aligned_buffer').AlignedBuffer;

var width = 480;
var height = 272;
var video_format = 'ycbcr422';
var pixel_bytes = 2;
var alignment = 32;
var video_buf = new AlignedBuffer(width * height * pixel_bytes, alignment);

var camera_config = {
    width : width,
    height : height,
    format : video_format,
    type : 'ov7725'
};
var jpeg_config = {
    width : width,
    height : height,
    format : video_format
};

var interval = 1*1000; // 画像保存周期(ms)
var count = 10; // 保存する画像ファイル数

// カメラの初期化
video.openCMOSCamera(camera_config, function(err, video_source){
    if(err) {
        console.log(err);
        return;
    }
    // 映像の取り込み開始
    video_source.start(video_buf, function(err) {
        if(err) {
            console.log(err);
            return;
        }
        // interval の周期で count 数のJPEG画像を保存
        var i = 0;
        var timer = setInterval(function() {
            if(i >= count) {
                clearInterval(timer);
                video_source.stopSync(); // 映像の取り込み停止
                video_source.closeSync(); // カメラリソースの解放
                return;
            }
            jpeg_config.bitmap = new AlignedBuffer(video_buf, alignment);
            var jpeg_data = jpeg.encodeSync(jpeg_config);
            fs.writeFileSync('/sd/image' + i + '.jpg', jpeg_data.toBuffer());
            console.log('/sd/image' + i + '.jpg');
            i++;
        }, interval);
    });
});
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ LCDのグラフィックス描画テスト
var display = require('display');
var Graphics = require('graphics').Graphics;
var AlignedBuffer = require('aligned_buffer').AlignedBuffer;

var width = 480;
var height = 272;
var pixel_bytes = 2;
var alignment = 32;

var buf_size = width * height * pixel_bytes;

var lcd_config = {
    type : '4.3inch'
};

// レイヤー0用フレームバッファ初期化
var format0 = 'rgb565';
var buf0 = new AlignedBuffer(buf_size, alignment);
var graphics0 = new Graphics({buf:buf0, width:width, height:height, format:format0});
graphics0.drawRect(0,0,width,height,0xFFFF,true); // 全領域を白で塗りつぶし

// レイヤー1用フレームバッファ初期化
var format1 = 'argb4444';
var buf1 = new AlignedBuffer(buf_size, alignment);
var graphics1 = new Graphics({buf:buf1, width:width, height:height, format:format1});
graphics1.drawRect(0,0,width,height,0x0000,true); // 全領域を完全透過

var interval = 1*1000; // LCD表示切り替え周期(ms)

// LCDの初期化
display.openLCD(lcd_config, function(err, lcd) {
    if(err) {
        console.log(err);
        return;
    }

    var i = 0;
    var timer = setInterval(function(){
        switch(i) {
        case 0:
            // レイヤー0の表示を開始(白背景)
            lcd.startSync(0, graphics0.frameBuffer, format0);
            break;
        case 1:
            // レイヤー1の表示を開始(白背景に赤の円)
            graphics1.drawCircle(100,100,50,0xFF00,true);
            lcd.startSync(1, graphics1.frameBuffer, format1);
            break;
        case 2:
            // レイヤー0のフレームバッファを切り替え(青背景に赤の円)
            var new_buf0 = new AlignedBuffer(buf_size, alignment);
            var new_graphics0 = new Graphics({buf:new_buf0, width:width, height:height, format:format0});
            new_graphics0.drawRect(0,0,width,height,0x001F,true); // 全領域を青で塗りつぶし
            lcd.updateSync(0, new_graphics0.frameBuffer);
            break;
        case 3:
            // レイヤー0の表示を停止(黒背景に赤の円)
            lcd.stopSync(0);
            break;
        case 4:
            // レイヤー1の表示を停止(黒背景)
            lcd.stopSync(1);
            break;
        default:
            // LCDリソースの解放
            lcd.closeSync();
            clearInterval(timer);
            break;
        }
        i++;
    }, interval);
});
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ カメラ入力をGoogle Vision APIに送信し、検出結果をLCDに描画するテスト
var display = require('display');
var video = require('video');
var jpeg = require('jpeg');
var https = require('https');
var AlignedBuffer = require('aligned_buffer').AlignedBuffer;
var Graphics = require('graphics').Graphics;
require('fixup')('https');

var width = 480;
var height = 272;
var video_format = 'ycbcr422';
var pixel_bytes = 2;
var alignment = 32;
var video_buf = new AlignedBuffer(width * height * pixel_bytes, alignment);

var result_format = 'argb4444';
var result_color = 0xF00F;
var result_pixel_bytes = 2;

var lcd_config = {
    type : '4.3inch'
};
var camera_config = {
    width : width,
    height : height,
    format : video_format,
    type : 'ov7725'
};
var jpeg_config = {
    width : width,
    height : height,
    format : video_format
};

var key = 'key=<your APIKey>';
var timeout = 10*1000;
var detection_type = 'FACE_DETECTION';
//var detection_type = 'OBJECT_LOCALIZATION';

var lcd;
display.openLCD(lcd_config, function(err, lcd_) {
    console.log('LCD Open Callback');

    if(!err) {
        lcd = lcd_;
        lcd.start(0, video_buf, video_format, function(err){
            if(!err) console.log('LCD Layer 0 Start');
            else     console.log(err);
        });

        console.log('Camera Open Start');
        video.openCMOSCamera(camera_config, function(err, video_source){
            if(!err) {
                video_source.start(video_buf, function(){
                    console.log('Camera Start');

                    // 10秒後にJPEGエンコード開始
                    setTimeout(function() {
                        console.log('JPEG Encode Start');
                        video_source.stopSync();
                        jpeg_config.bitmap = new AlignedBuffer(video_buf, alignment);
                        jpeg.encode(jpeg_config, function(err, data) {
                            console.log('JPEG Encode Callback');
                            if(!err && data) {
                                use_google_vision_api(data.toBuffer().toString('base64'));
                            } else {
                                console.log(err);
                            }
                        });
                    }, timeout);
                });
            }
            else {
                console.log(err);
            }
        });
    }
    else {
        console.log(err);
    }
});

console.log('LCD Open Start');

function use_google_vision_api(image) {
    var host = 'vision.googleapis.com';
    var port = 443;
    var path = '/v1/images:annotate';

    var data = '{"requests":[{"image":{"content":"'
            + image 
            + '"},"features":[{"type":"' + detection_type + '"}]},]}';

    var options = {
        host: host,
        port: port,
        path: path + '?' + key,
        method: 'POST',
        headers: {
            'Host': host + ':' + port,
            'Content-Type': 'application/json',
            'Content-Length': data.length
        }
    };

    var req = https.request(options, function(res) {
        var body = '';
        console.log( 'res.statusCode', res.statusCode);

        res.on('data',function(chunk) {
            body += chunk;
        });
        res.on('end',function() {
            console.log(body);
            drawResult(JSON.parse(body));
        });
    });
    req.on( 'error', function( e ) {
        console.log( 'error' );
        console.log( e.message );
    });

    req.writeEntire = function(data, block_size) {
        block_size = block_size || 1024;
        for(var i = 0; i < data.length; i += block_size) {
            this.write(data.slice(i, i + block_size));
        }
    };
    req.writeEntire(data);
    req.end();
}

function drawResult(res) {
    switch(detection_type) {
    case 'FACE_DETECTION':
        drawFace(res);
        break;
    case 'OBJECT_LOCALIZATION':
        drawObject(res);
        break;
    default:
        break;
    }
}

function drawObject(res) {
    var result_buf = new AlignedBuffer(width*height*result_pixel_bytes, alignment);
    var result = new Graphics({buf:result_buf, width:width, height:height, format:result_format});
    result.drawRect(0, 0, width, height, 0x0000, true);

    // response format
    // https://cloud.google.com/vision/docs/reference/rest/v1/images/annotate?hl=ja#LocalizedObjectAnnotation
    var objects = res.responses[0].localizedObjectAnnotations;
    objects.forEach(function(e, index){
        var rect = e.boundingPoly.normalizedVertices;
        rect.forEach(function(e) {
            e.x *= width;
            e.y *= height;
        });
        result.drawRect(rect[0].x,
                   rect[0].y,
                   rect[1].x - rect[0].x,
                   rect[3].y - rect[0].y,
                   result_color, false);
        result.drawText(e.name, rect[0].x + 1, rect[0].y + 1, 1, result_color, 0x0000);
    });

    lcd.startSync(2, result.frameBuffer, result_format);
}

function drawFace(res) {
    var result_buf = new AlignedBuffer(width*height*result_pixel_bytes, alignment);
    var result = new Graphics({buf:result_buf, width:width, height:height, format:result_format});
    result.drawRect(0, 0, width, height, 0x0000, true);

    var background_buf = new AlignedBuffer(width*height*result_pixel_bytes, alignment);
    var background_format = 'rgb565';
    var background = new Graphics({buf : background_buf, width:width, height:height, format:background_format});
    background.drawRect(0, 0, width, height, 0xFFFF, true);

    result.drawPolyline = function ( positions, close, color ) {
        for(var i = 0; i < positions.length - 1; i++) {
            this.drawLine(positions[i].x,
                              positions[i].y,
                              positions[i+1].x,
                              positions[i+1].y,
                              color);
        }
        if(close) {
            this.drawLine(positions[positions.length-1].x,
                              positions[positions.length-1].y,
                              positions[0].x,
                              positions[0].y,
                              color);
        }
    };

    // response format
    // https://cloud.google.com/vision/docs/reference/rest/v1/images/annotate?hl=ja#FaceAnnotation

    res.responses[0].faceAnnotations.forEach(function(faceAnnotation){

        var landmarks = faceAnnotation.landmarks;
        landmarks.findIndex = function(type) {
            var ret;
            this.forEach(function(e, index) {
                if(e.type == type) ret = index;
            });
            return ret;
        };

        var outline = [];
        outline.push(landmarks[landmarks.findIndex('LEFT_EAR_TRAGION')].position);
        outline.push(landmarks[landmarks.findIndex('CHIN_LEFT_GONION')].position);
        outline.push(landmarks[landmarks.findIndex('CHIN_GNATHION')].position);
        outline.push(landmarks[landmarks.findIndex('CHIN_RIGHT_GONION')].position);
        outline.push(landmarks[landmarks.findIndex('RIGHT_EAR_TRAGION')].position);
        result.drawPolyline(outline, false, result_color);

        var left_eyeblow = [];
        left_eyeblow.push(landmarks[landmarks.findIndex('LEFT_OF_LEFT_EYEBROW')].position);
        left_eyeblow.push(landmarks[landmarks.findIndex('LEFT_EYEBROW_UPPER_MIDPOINT')].position);
        left_eyeblow.push(landmarks[landmarks.findIndex('RIGHT_OF_LEFT_EYEBROW')].position);
        result.drawPolyline(left_eyeblow, false, result_color);

        var right_eyeblow = [];
        right_eyeblow.push(landmarks[landmarks.findIndex('LEFT_OF_RIGHT_EYEBROW')].position);
        right_eyeblow.push(landmarks[landmarks.findIndex('RIGHT_EYEBROW_UPPER_MIDPOINT')].position);
        right_eyeblow.push(landmarks[landmarks.findIndex('RIGHT_OF_RIGHT_EYEBROW')].position);
        result.drawPolyline(right_eyeblow, false, result_color);

        var left_eye = [];
        left_eye.push(landmarks[landmarks.findIndex('LEFT_EYE_TOP_BOUNDARY')].position);
        left_eye.push(landmarks[landmarks.findIndex('LEFT_EYE_RIGHT_CORNER')].position);
        left_eye.push(landmarks[landmarks.findIndex('LEFT_EYE_BOTTOM_BOUNDARY')].position);
        left_eye.push(landmarks[landmarks.findIndex('LEFT_EYE_LEFT_CORNER')].position);
        result.drawPolyline(left_eye, true, result_color);

        var eye = landmarks[landmarks.findIndex('LEFT_EYE')].position
        result.drawCircle(eye.x, eye.y, 2, result_color, true);
        eye = landmarks[landmarks.findIndex('RIGHT_EYE')].position
        result.drawCircle(eye.x, eye.y, 2, result_color, true);

        var right_eye = [];
        right_eye.push(landmarks[landmarks.findIndex('RIGHT_EYE_TOP_BOUNDARY')].position);
        right_eye.push(landmarks[landmarks.findIndex('RIGHT_EYE_RIGHT_CORNER')].position);
        right_eye.push(landmarks[landmarks.findIndex('RIGHT_EYE_BOTTOM_BOUNDARY')].position);
        right_eye.push(landmarks[landmarks.findIndex('RIGHT_EYE_LEFT_CORNER')].position);
        result.drawPolyline(right_eye, true, result_color);

        var nose = [];
        nose.push(landmarks[landmarks.findIndex('NOSE_BOTTOM_LEFT')].position);
        nose.push(landmarks[landmarks.findIndex('NOSE_TIP')].position);
        nose.push(landmarks[landmarks.findIndex('NOSE_BOTTOM_RIGHT')].position);
        nose.push(landmarks[landmarks.findIndex('NOSE_BOTTOM_CENTER')].position);
        nose.push(landmarks[landmarks.findIndex('NOSE_BOTTOM_LEFT')].position);
        nose.push(landmarks[landmarks.findIndex('MIDPOINT_BETWEEN_EYES')].position);
        nose.push(landmarks[landmarks.findIndex('NOSE_BOTTOM_RIGHT')].position);
        result.drawPolyline(nose, false, result_color);

        var mouth = [];
        mouth.push(landmarks[landmarks.findIndex('MOUTH_LEFT')].position);
        mouth.push(landmarks[landmarks.findIndex('UPPER_LIP')].position);
        mouth.push(landmarks[landmarks.findIndex('MOUTH_RIGHT')].position);
        mouth.push(landmarks[landmarks.findIndex('LOWER_LIP')].position);
        mouth.push(landmarks[landmarks.findIndex('MOUTH_LEFT')].position);
        mouth.push(landmarks[landmarks.findIndex('MOUTH_CENTER')].position);
        mouth.push(landmarks[landmarks.findIndex('MOUTH_RIGHT')].position);
        result.drawPolyline(mouth, false, result_color);

    });

    lcd.start(2, result.frameBuffer, result_format, function(){
        console.log('lcd start callback');
        setTimeout(function(){
            lcd.startSync(1, background.frameBuffer, background_format);
            lcd.stopSync(0);
        }, timeout);
    });
}
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ ランダムグラフィックス描画テスト
var display = require('display');
var Graphics = require('graphics').Graphics;
var AlignedBuffer = require('aligned_buffer').AlignedBuffer;

var width = 480;
var height = 272;
var graphics_format = 'rgb565';
var text_format = 'argb4444';
var pixel_bytes = 2;
var alignment = 32;
var color_mask = 0xFFFF;
var text_color = 0xF000;
var text_background = 0xFFFF;

var buf_size = width * height * pixel_bytes;
var interval = 10;
var count = 500;

var lcd_config = {
    type : '4.3inch'
};

var graphcis_buf = new AlignedBuffer(buf_size, alignment);
var canvas = new Graphics({buf:graphcis_buf, width:width, height:height, format:graphics_format});
canvas.drawRect(0,0,width,height,0xFFFF,true);
var text_buf = new AlignedBuffer(buf_size, alignment);
var text = new Graphics({buf:text_buf, width:width, height:height, format:text_format});
text.drawRect(0,0,width,height,0x0000,true);

var start_index = 0;
var drawRandomGraphics = [
    { type : "Line",      func : drawRandomLine },
    { type : "Rectangle", func : drawRandomRect },
    { type : "Arc",       func : drawRandomArc },
    { type : "Circle",    func : drawRandomCircle },
    { type : "Ellipse",   func : drawRandomEllipse },
    { type : "Polygon",   func : drawRandomPolygon }
];

function drawRandomLine(g) {
    var sx = Math.floor(Math.random() * width);
    var sy = Math.floor(Math.random() * height);
    var ex = Math.floor(Math.random() * width);
    var ey = Math.floor(Math.random() * height);
    var color = Math.floor(Math.random() * color_mask);
    g.drawLine(sx, sy, ex, ey, color);
}

function drawRandomRect(g) {
    var x = Math.floor(Math.random() * width);
    var y = Math.floor(Math.random() * height);
    var w = Math.floor(Math.random() * width);
    var h = Math.floor(Math.random() * height);
    var color = Math.floor(Math.random() * color_mask);
    var fill = Math.round(Math.random());
    g.drawRect(x, y, w, h, color, fill);
}

function drawRandomArc(g) {
    var x = Math.floor(Math.random() * width);
    var y = Math.floor(Math.random() * height);
    var radius = Math.floor(Math.random() * height);
    var start = Math.floor(Math.random() * 360);
    var end = Math.floor(Math.random() * 360);
    var color = Math.floor(Math.random() * color_mask);
    g.drawArc(x, y, radius, start, end, color);
}

function drawRandomCircle(g) {
    var x = Math.floor(Math.random() * width);
    var y = Math.floor(Math.random() * height);
    var radius = Math.floor(Math.random() * height);
    var color = Math.floor(Math.random() * color_mask);
    var fill = Math.round(Math.random());
    g.drawCircle(x, y, radius, color, fill);
}

function drawRandomEllipse(g) {
    var cx = Math.floor(Math.random() * width);
    var cy = Math.floor(Math.random() * height);
    var rx = Math.floor(Math.random() * height);
    var ry = Math.floor(Math.random() * height);
    var color = Math.floor(Math.random() * color_mask);
    var fill = Math.round(Math.random());
    g.drawEllipse(cx, cy, rx, ry, color, fill);
}

function drawRandomPolygon(g) {
    var x = Math.floor(Math.random() * width);
    var y = Math.floor(Math.random() * height);
    var radius = Math.floor(Math.random() * height);
    var sides = Math.floor(Math.random() * 10) + 3;
    var color = Math.floor(Math.random() * color_mask);
    var fill = Math.round(Math.random());
    g.drawPolygon(x, y, radius, sides, color, fill);
}

display.openLCD(lcd_config, function(err, lcd) {
    if(!err) {
        lcd.startSync(0, canvas.frameBuffer, graphics_format);
        lcd.startSync(1, text.frameBuffer, text_format);

        var draw = function(index) {
            var type = drawRandomGraphics[index].type;
            canvas.drawRect(0, 0, width, height, 0xFFFF, true);
            text.drawRect(0, 0, width, height, 0x0000, true);
            text.drawText('Draw Random ' + type, 0, 0, 2, text_color, text_background);

            var timer = setInterval(function() {
                drawRandomGraphics[index].func(canvas);
            }, interval);

            setTimeout(function() {
                clearInterval(timer);
                if(++index < drawRandomGraphics.length) {
                    draw(index);
                }
            }, interval * count);
        }
        draw(start_index);
    }
});
//eoc ---------- (EndOfCode)





//boc ---------- (BeginOfCode)
// ■ JPEG画像の連続表示テスト
// 事前に contents_microSD.zip を解凍し、frames フォルダを microSD カード直下に
// コピーしてください
var display = require('display');
var jpeg = require('jpeg');
var fs = require('fs');
var AlignedBuffer = require('aligned_buffer').AlignedBuffer;
var Graphics = require('graphics').Graphics;

var width = 480;
var height = 272;
var format = 'rgb565';
var pixel_bytes = 2;
var alignment = 32;
var buf = new AlignedBuffer(width * height * pixel_bytes, alignment);
var canvas = new Graphics({buf:buf, width:width, height:height, format:format});
canvas.drawRect(0,0,width,height,0xFFFF,true);

var lcd_config = {
    type : '4.3inch'
};

var file_num = 14315;

display.openLCD(lcd_config, function(err, lcd) {
    if(!err) {
        lcd.start(0, canvas.frameBuffer, format, function() {
            var i = 1;
            var interval = setInterval(function() {
                if(i > file_num) {
                    clearInterval(interval);
                    return;
                }
                body(i++);
            }, 1);
        });
    } else {
        console.log(err);
    }
});

function body(i) {
    var dir = ('000' + Math.floor(i / 100)).slice(-3);
    var file = ('00000' + i).slice(-5) + '.jpg';
    var path = '/sd/frames/' + dir + '/' + file;
    var jpeg_data = new AlignedBuffer(fs.readFileSync(path), alignment);

    var decode_config = {
        width : 384,
        height : 216,
        format : format
    };

    var image = jpeg.decodeSync(jpeg_data, decode_config);
    canvas.drawImage(image, 48, 28);
}
//eoc ---------- (EndOfCode)