Xbox360コントローラーとmindstorm-EV3でラジコンを作る【後編・プログラム解説】

## 概要
Xbox360コントローラーを使ってmindstorm-EV3のモーターをコントロールします。
EV3で動くLinux環境ev3devを使って開発します。

前篇はこちらです
>
[Xbox360コントローラーとmindstorm-EV3でラジコンを作る【前編】](http://qiita.com/murotani/items/b1b14d4399cf182a7bde)

後編ではプログラムについて解説していきます。

## クライアント側
####index.html
controller.jsを読み込んでいます。

```html:index.html
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>XboxEv3Tank</title>
  </head>

  <body>
    <h2>XboxEv3Tank</h2>
    <pre id="controller"></pre>
  </body>
<script src="./controller.js"></script>
</html>
```
####controller.js
ブラウザ(Javascript)でXbox360コントローラーの入力を取得してサーバーに送信します。


```js:controller.js
(function(){
	if(!(window.Gamepad)) return;
	if(!(navigator.getGamepads)) return;
	var element = document.getElementById("controller");
	var lastCommand = {direction:0,launch:0};

	setInterval(function(){
		var str = "";
		var command = {direction:0,launch:0};
		// direction:0 => 停止
		// direction:1 => 前進
		// direction:2 => 右旋回
		// direction:3 => 左旋回

		var gamepad_list = navigator.getGamepads();

		for(var i = 0; i < gamepad_list.length; i++){

			var gamepad = gamepad_list[i];
			if(!gamepad) continue;
			str += "connected: " + gamepad.connected + "\n";
			var buttons = gamepad.buttons;
			str += "buttons: {\n";

			for(var j = 0; j < buttons.length; j++){
				str += "  \"" + j + "\": { ";
				str += "pressed:" + buttons[j].pressed + "}\n";
			}
			str += "}\n";

			if(buttons[6].pressed == true && buttons[7].pressed == true){
				command.direction=1
			}else if(buttons[6].pressed == false && buttons[7].pressed == true){
				command.direction=2
			}else if(buttons[6].pressed == true && buttons[7].pressed == false){
				command.direction=3
			}else{
				command.direction=0
			}

			if(buttons[1].pressed == true){
				command.launch=1;
			}else{
				command.launch=0;
			}

		}
		element.textContent = str;

		if(JSON.stringify(command) != JSON.stringify(lastCommand)){
				window.location.href = 'http://ev3dev.local:3000/'+command.direction + command.launch;
		}

		lastCommand=command;

	},50);
})();
```

controller.jsではGamepad APIを使ってXbox360コントローラーの入力を取得して、入力状態情報を載せたurlを以下のようにサーバーにリクエストしています。

```js:controller.js
window.location.href = 'http://ev3dev.local:3000/'+command.direction + command.launch;

```
command.direction,command.launchにはそれぞれ数値が入り、
command.direcrionにはロボットの進行方向（0 => 停止、1 => 前進、2 => 右旋回、3 => 左旋回）、command.launchにはボールの発射命令(0 => 何もしない、1 => 発射)を格納します。

##サーバー側
#### app.js
ev3dev上でWebサーバーを立ててindex.htmlとcontroller.jsを読み込ませています。
motorControl関数でサーバーに来るリクエスト文字列によってモーターの制御を切り替えています

```js:app.js
var http = require('http');
var server = http.createServer();
var fs = require('fs');
var settings = require('./settings');
var motorControlCommands = require('./motorControl');

motorControlCommands.setForward(500);

server.on('request', function(req, res) {
  var url = req.url;
  console.log(url);
  motorControl(url);
  if ('/' == url) {
    fs.readFile(__dirname + '/index.html', 'UTF-8', function (err, data) {
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write(data);
      res.end();
    });
  } else if ('/controller.js' == url) {
    fs.readFile(__dirname + '/controller.js', 'UTF-8', function (err, data) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.write(data);
      res.end();
    });
  }
})

function motorControl(url){
  motorControlId = url.slice(1,2);
  launchId = url.slice(2,3);
  if(launchId == 1) motorControlCommands.launch();

  if(motorControlId == 1){
    motorControlCommands.setForward(500);
    motorControlCommands.run();
  }else if(motorControlId == 0){
    motorControlCommands.stop();
  } else if(motorControlId == 1){
    motorControlCommands.setForward(500);
    motorControlCommands.run();
  } else if(motorControlId == 2){
    motorControlCommands.setRight(250);
    motorControlCommands.run();
  }else if(motorControlId == 3){
    motorControlCommands.setLeft(250);
    motorControlCommands.run();
  }
}

console.log(settings);
server.listen(settings.port,settings.host)
```
#### settings.js
settings.jsにはWebサーバーのIpアドレスとポートを定義しています。

```js:settings.js
var os = require('os');
var ifaces = os.networkInterfaces();

exports.port = 3000;
exports.host = ifaces.wlan0[0].address;
```
#### motorsetting.js
motorsetting.jsには接続されたモーターのファイルパスを設定します。
ファイルパスはロボットが再起動させる度に変わる可能性があるのでWebサーバーを立ち上げる前に正しく設定する必要があります。その設定を自動化したのがmotorSetup.shです。

```js:motorSetting.js
exports.outA = '/sys/class/tacho-motor/motor0';
exports.outC = '/sys/class/tacho-motor/motor1';
exports.outB = '/sys/class/tacho-motor/motor2';
```
#### motorSetup.sh
このスクリプトを実行することでmotorsetting.jsの設定を更新することができます。

```bash:motorSetup.sh
#!/bin/sh
cat /dev/null > motorSetting.js
for f in /sys/class/tacho-motor/*;
do
  echo exports.`cat $f/address` = \'$f\'\; >> motorSetting.js;
done
```
#### motorControl.js
モーターを制御する関数を定義しています。
こちらを参考に実装しています。
>
[mindstorm-EV3 ev3devでコマンドラインからモーター制御、センサー値取得する方法まとめ](http://qiita.com/murotani/items/d4e8b21b84fe0b99d579) 


```js:motorControl.js
var fs = require('fs');
var motorSetting = require('./motorSetting');

//モーター回転方向を前進にセット
exports.setForward = function(speed){
  fs.writeFileSync(motorSetting.outA + '/speed_sp',speed);
  fs.writeFileSync(motorSetting.outB + '/speed_sp',speed);
}

//モーター回転方向を右旋回にセット
exports.setRight = function(speed){
  fs.writeFileSync(motorSetting.outA + '/speed_sp',speed);
  fs.writeFileSync(motorSetting.outB + '/speed_sp',-speed);
}

//モーター回転方向を左旋回にセット
exports.setLeft = function(speed){
  fs.writeFileSync(motorSetting.outA + '/speed_sp',-speed);
  fs.writeFileSync(motorSetting.outB + '/speed_sp',speed);
}

//モーターを回転させる
exports.run = function(){
  fs.writeFileSync(motorSetting.outA + '/command','run-forever');
  fs.writeFileSync(motorSetting.outB + '/command','run-forever');
}

//モーターを停止させる
exports.stop = function(){
  fs.writeFileSync(motorSetting.outA + '/command','stop');
  fs.writeFileSync(motorSetting.outB + '/command','stop');
}

//ボールを発射させる
exports.launch = function(){
  fs.writeFileSync(motorSetting.outC + '/speed_sp',500);
  fs.writeFileSync(motorSetting.outC + '/position_sp',1080);
  fs.writeFileSync(motorSetting.outC + '/command','run-to-rel-pos');
}
```