nodejsでmindstrom-EV3を制御するev3dev-lang-js入門

## はじめに
EV3で動くLinux環境ev3devを使って開発します。この記事は、EV3にev3devをインストールし、SSH接続できている状態であることを前提にしています。
環境構築ができていない方はこちらの記事を参考にしてください。
>
[mindstorm-EV3をLinuxで制御しよう! ev3dev OSのインストールととSSH接続](http://qiita.com/murotani/items/46f905bec33b1673c41a)

この記事ではnodejsからev3devを制御するライブラリ[ev3dev-lang-js](https://github.com/WasabiFan/ev3dev-lang-js)を、サンプルコードとともに紹介します。

## nodejsのインストール
ev3devにnodejsをインストールします。

```
robot@ev3dev:~$　sudo apt-get update
robot@ev3dev:~$　sudo apt-get install nodejs
```

##ev3dev-lang-jsのインストール
[ev3dev-lang-js](https://github.com/wasabifan/ev3dev-lang-js)はjavascriptを使ってEV3のモーターやLEDの制御、センサー値の取得などを実現するライブラリです。npmでインストールできます。

```
robot@ev3dev:~$　 npm install ev3dev-lang
```

##バッテリー情報を取得
EV3の電源供給系統のデータを取得するには[PowerSupplyクラス](http://wasabifan.github.io/ev3dev-lang-js/classes/_extras_.powersupply.html)を利用します。
####battery-metadata.js

```js:battery-metadata.js
var ev3dev = require('ev3dev-lang');

function printBatteryInfo(label, battery) {
	console.log(label + " --------------");
	
	if(battery.connected) {	
		console.log('  Technology: ' + battery.technology);
		console.log('  Type: ' + battery.type);
		
		console.log('  Current (microamps): ' + battery.measuredCurrent);
		console.log('  Current (amps): ' + battery.currentAmps);
		
		console.log('  Voltage (microvolts): ' + battery.measuredVoltage);
		console.log('  Voltage (volts): ' + battery.voltageVolts);
		
		console.log('  Max voltage (microvolts): ' + battery.maxVoltage);
		console.log('  Min voltage (microvolts): ' + battery.minVoltage);
	}
	else
	    console.log("  Battery not connected!");
}

var defaultBattery = new ev3dev.PowerSupply();
printBatteryInfo("Default battery", defaultBattery);
```

####実行結果

```
robot@ev3dev:~/ev3dev-lang-js-test$ node battery-metadata.js
Default battery --------------
  Technology: Li-ion
  Type: Battery
  Current (microamps): 233333
  Current (amps): 0.233333
  Voltage (microvolts): 8465666
  Voltage (volts): 8.465666
  Max voltage (microvolts): 7500000
  Min voltage (microvolts): 7100000
```

##LEDを制御
EV3のLEDを制御するには[Ev3Ledsクラス](http://wasabifan.github.io/ev3dev-lang-js/classes/_index_.ev3leds.html)を利用します。
EV3のLEDは右側と左側がそれぞれ、赤色と緑色のLEDによって構成されています。つまりそれらのLEDの明るさの強弱を変化させて重ね合わせることで黄色やオレンジ色も表現することが可能です。

詳しくはこちらを参考
>
[ev3devでmindstorm-EV3のLEDを制御しよう](http://qiita.com/murotani/items/59805240048082833dee)


####fade-led.js

```js:fade-led.js
var ev3dev = require('ev3dev-lang');

console.log('fading LEDs from green to red...');

for (var pct = 0; pct < 100; pct += 1) {
    var brightnessVal = (pct / 100);
    var invertedBrightnessVal = 1 - brightnessVal;

    ev3dev.Ev3Leds.left.setColor([ brightnessVal, invertedBrightnessVal ]);
    ev3dev.Ev3Leds.right.setColor([ brightnessVal, invertedBrightnessVal ]);

    if(pct % 10 == 0)
        console.log(pct + '%');

    {   //Hack to sleep for time
        //    SHOULD NOT BE USED IN PRODUCTION CODE
        var start = new Date().getTime();
        while(new Date().getTime() < start + 100) {
            ;
        }
    }
}
```

setColorメソッドの第一引数には配列で赤色、緑色LEDの出力の強さを0~1の範囲で指定します。
```ev3dev.Ev3Leds.left.setColor([ brightnessVal, invertedBrightnessVal ]);
```


####実行結果

```
robot@ev3dev:~/ev3dev-lang-js-test$ node fade-leds.js
fading LEDs from green to red...
0%
10%
20%
30%
40%
50%
60%
70%
80%
90%
done
```

（クリックで動画再生）
[![YouTube](http://i.ytimg.com/vi/PyGdc3c57ro/hqdefault.jpg)](https://www.youtube.com/watch?v=PyGdc3c57ro)


##モーターの回転とLEDの色を同期させてみる
EV3のモーターを制御するには[Motorクラス](http://wasabifan.github.io/ev3dev-lang-js/classes/_motors_.motor.html)を利用します。

####led-from-motor-speed.js

```js:led-from-motor-speed.js
var ev3dev = require('ev3dev-lang');
var stoppedBlinkInterval = 200;

if(!ev3dev.Ev3Leds.isConnected) {
    console.error("This sample can only run on the EV3 brick. Other platforms are not supported by this script.");
    process.exit(1);
}

var motor = new ev3dev.Motor();

if(!motor.connected) {
    console.error("No valid motor was found. Please connect a tacho motor and try again.");
    process.exit(1);
}

console.log("Connected to motor " + motor.address);
motor.stopAction = motor.stopActionValues.coast;

console.log("Timer running... Rotate the motor and watch the on-board LEDs.");

setInterval(function() {

    if(motor.speed > 1) {
        var rpsSpeed = Math.min(Math.abs(motor.speed) / motor.countPerRot, 1);
        var ledColor = [rpsSpeed, 1 - rpsSpeed];
        ev3dev.Ev3Leds.left.setColor(ledColor);
        ev3dev.Ev3Leds.right.setColor(ledColor);
    }
    else {
        ev3dev.Ev3Leds.left.setColor([0, 1]);
        ev3dev.Ev3Leds.right.setColor([0, 1]);
    }

}, 80);
```

####実行結果
EV3インテリジェントブロックのAポートにLモーターを接続してから実行します。

```
robot@ev3dev:~/ev3dev-lang-js-test$ node led-from-motor-speed.js
Connected to motor outA
Timer running... Rotate the motor and watch the on-board LEDs.
# モーターを手で回転させてみましょう
# Ctrl-cで終了
```

（クリックで動画再生）
[![YouTube](http://i.ytimg.com/vi/oHIErPJ7yrc/hqdefault.jpg)](https://www.youtube.com/watch?v=oHIErPJ7yrc)

##タッチセンサーを使う
EV3のタッチセンサーを制御するには[TouchSensorクラス](http://wasabifan.github.io/ev3dev-lang-js/classes/_sensors_.touchsensor.html)を利用します。

####raw-device-events.js

```js:raw-device-events.js
var ev3dev = require('ev3dev-lang');

var greenANSI = "\033[42m";
var redANSI = "\033[41m";
var resetANSI = "\033[0m";

var touchSensor = new ev3dev.TouchSensor();
if (!touchSensor.connected) {
    console.error("No touch sensor could be found! Please verify that a touch sensor is plugged in and try again.");
    process.exit(1);
}

touchSensor.registerEventCallback(function(error, touchInfo) {
    if(error) throw error;
    console.log("Sensor is " + (touchInfo.lastPressed ? greenANSI + "PRESSED" : redANSI + "RELEASED") + resetANSI);
},
    function(userData) {
        var isPressed = touchSensor.isPressed;
        var changed = isPressed != userData.lastPressed;

        userData.lastPressed = isPressed;
        return changed;
    }, false, { lastPressed: false });

 console.log("Press the touch sensor to trigger the press event.");
```
registerEventCallbackメソッドの定義は以下のとおりです。コールバック関数内でタッチイベント情報を取得しています。

[registerEventCallback](http://wasabifan.github.io/ev3dev-lang-js/classes/_sensors_.touchsensor.html#registereventcallback)
>
registerEventCallback(
callbackFunction: function, 
eventPredicate: function, 
firstTriggerOnly?: boolean,
userCallbackData?: any)
: void

####実行結果
EV3インテリジェントブロックの1番ポートにタッチセンサーを接続してから実行します。

```
robot@ev3dev:~/ev3dev-lang-js-test$ node raw-device-events.js
Press the touch sensor to trigger the press event.
# タッチセンサーを押したり離したりしてみましょう
Sensor is PRESSED
Sensor is RELEASED
```

##特定のポートに接続したモーターを制御
接続ポートを指定してモーターを制御する方法です。

####run-specific-motor.js

```js:run-specific-motor.js
var ev3dev = require('ev3dev-lang');

var motor = new ev3dev.Motor(ev3dev.OUTPUT_A);
if(!motor.connected) {
    console.error("No motor was found on port A. Please connect a tacho motor to port A and try again.");
    process.exit(1);
}

motor.runForDistance(360 * 10, 500, motor.stopActionValues.brake);

console.log("Running the motor for 180 tacho counts...");

// Prevent Node from exiting until motor is done
var cancellationToken = setInterval(function() {
    if(!motor.isRunning)
        clearInterval(cancellationToken);
}, 200);
```
Aポートを使用する設定

```var motor = new ev3dev.Motor(ev3dev.OUTPUT_A);
```
runForDistanceメソッドは第一引数に回転角度、第二引数に回転速度、第三引数に動作停止パラメーターを設定します。

```motor.runForDistance(360 * 10, 500, motor.stopActionValues.brake);
```

####実行結果
EV3インテリジェントブロックのAポートにLモーターを接続してから実行します。

```
robot@ev3dev:~/ev3dev-lang-js-test$ node run-specific-motor.js
Running the motor for 180 tacho counts...
# モーターが10回転します
```

##タッチセンサーとモーターを連携
タッチセンサーを押している間、モーターが回転し続けるコードは以下のようになります。
##touch-sensor-motor-control.js

```js:touch-sensor-motor-control.js
var ev3dev = require('ev3dev-lang');

var touchSensor = new ev3dev.TouchSensor();
if(!touchSensor.connected) {
    console.error("No touch sensor could be found! Please verify that a touch sensor is plugged in and try again.");
    process.exit(1);
}

var motor = new ev3dev.Motor();
if(!motor.connected) {
    console.error("No valid motor was found. Please connect a tacho motor and try again.");
    process.exit(1);
}

console.log("Connected to touch sensor at address " + touchSensor.address + " and tacho motor at address " + motor.address);
console.log("Press the touch sensor to spin the motor.");

setInterval(function() {
    motor.start(motor.maxSpeed * touchSensor.getValue(0), motor.stopActionValues.hold);
}, 10);
```


####実行結果

```
robot@ev3dev:~/ev3dev-lang-js-test$ node touch-sensor-motor-control.js
Connected to touch sensor at address in1 and tacho motor at address outA
Press the touch sensor to spin the motor.
# タッチセンサーを押下している間モーターが回転します
```

（クリックで動画再生）
[![YouTube](http://i.ytimg.com/vi/1to_JLEpQ_8/hqdefault.jpg)](https://www.youtube.com/watch?v=1to_JLEpQ_8)


## 参考
サンプルプログラムは[ev3dev-lang-js](https://github.com/WasabiFan/ev3dev-lang-js)のサンプルコードを利用しています。