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]
Package | Version |
build-essential | 12.1ubuntu2 |
gyp | 0.1+20150913git1f374df9-1ubuntu1 |
mercurial | 3.7.3-1ubuntu1 |
cmake | 3.5.1-1ubuntu3 |
git | 1:2.7.4-0ubuntu1.4 |
python2.7 | 2.7.12-1ubuntu016.04.3 |
valgrind | 1:3.11.0-1ubuntu4.2 |
python-pip | 8.1.1-2ubuntu0.4 |
2. 以下のコマンドを実行してMbed OS用ビルドツール mbed CLIをインストールします。
$ sudo -H pip install mbed-cli
3. GNU Arm Embedded Toolchainをインストールします。
- 下記URLからLinux用パッケージ (gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2) をダウンロードしてください。
https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2?revision=2cc92fb5-3e0e-402d-9197-bdfc8224d8a5?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,6-2017-q2-update
- ダウンロードしたファイルを任意のディレクトリに展開します。
(以下、展開先を${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』と接続します。
シリアルポートは下記設定としてください。
Baud rate | 115200 | |||
Data | 8 bit | |||
Parity | none | |||
Stop | 1 bit | |||
Flow control | none |
5. 正常に書込みが終了したらGR-PEACHのRESETボタンを押下します。
6. ターミナルS/W上に下記メッセージが表示されます。
7. Enterキーを押下してコード入力モードに移行します。
8. 実行するJavascriptコードをターミナルソフトウェアに貼り付けてEnterキーを押下すると、実行開始します。Javascriptコード例については、test/case.txtを参照ください。
Diff: test/case.txt
- Revision:
- 1:c3d69f309845
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/case.txt Thu Jul 11 18:49:37 2019 +0900 @@ -0,0 +1,1122 @@ +■テストケースについて + + 次の行で囲まれた部分がそれぞれのテストケースになります。 + //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)