ESP8266(ESPr Developer)とMilkcocoaとローテーショナルサーボで簡易ラジコン

## 1. 概要
簡単に動きを制御できるプラットフォームを作りたくて、まずは簡単にラジコンを作ってみることにしました。
スマホを傾けることで操作できるようになりますが、操作しにくいです。
まだとりあえず動いただけなので今後改良していくつもりですが、何かの役に立てばと思いまとめました。

## 2. 用意するもの
 - [ESPr Developer（ESP-WROOM-02開発ボード） - スイッチサイエンス](https://www.switch-science.com/catalog/2500/)
 - [ローテーショナルサーボGWS S35 STD](http://akizukidenshi.com/catalog/g/gM-08305/)


## 3. 作業環境
 - MacBook Air (13-inch, Mid 2012) & Mac OS 10.11.4
 - Arduino IDE 1.6.5


## 4. 参考サイト
 - [建築発明工作ゼミ2008: Arduino サーボ制御](http://kousaku-kousaku.blogspot.jp/2008/06/arduino.html)
 - [ESP8266 (ESP-WROOM-02) でサーボを制御する - Qiita](http://qiita.com/exabugs/items/1c454d3d31d7561dcdd0)
 - [スマホの向きをPCのブラウザで表示 Tutorial | Milkcocoa](https://mlkcca.com/tutorial/page1.html)

## 5. ローテーショナルサーボとは
 - 通常のサーボが-180~180度といった角度制限があるのに比べて、連続で回転が可能なサーボ
 - ステッピングモータと違いドライバが不要なため簡単に車輪を制御できる(はず？)

## 6. ローテーショナルサーボの使い方(Arduino UNOでテスト)
 - まずはArduino Unoで動かしてみる


### 6.1 接続図

| GWS S35 STD | Arduino Uno |
|:------------|:------------|
| 白          | Pin 3       |
| 赤          | 5V          |
| 黒          | GND         |

![IMG_3359.JPG](https://qiita-image-store.s3.amazonaws.com/0/94069/dac1a221-0249-7a83-12b7-71b8c1764e2c.jpeg)


### 6.2 プログラム
```java:Rotational_Servo_UNO.ino
// Arduino UNOでローテーショナルサーボGWS S35 STDを制御

#include<Servo.h>

Servo servo;  // サーボのインスタンス

void setup() {
  // サーボの信号線を3番ピンに接続
  servo.attach(3);
}

void loop() {
  // サーボ出力 1.5ms(停止)を中心に、1.0ms~2.0msのパルス幅で動く。パルス幅によって速度が変わる
  servo.writeMicroseconds(1600);
  delay(500);
  servo.writeMicroseconds(1500);
  delay(500);
  servo.writeMicroseconds(1400);
  delay(500);
  servo.writeMicroseconds(1500);
  delay(500);
  
  servo.writeMicroseconds(2000);
  delay(500);
  servo.writeMicroseconds(1500);
  delay(500);
  servo.writeMicroseconds(1000);
  delay(500);
  servo.writeMicroseconds(1500);
  delay(500);
}
```

### 6.3 実行結果

 - 実行画面(20160621 Rotational Servo Test - YouTubeに飛びます) 

[![20160621 Rotational Servo Test - YouTube](http://img.youtube.com/vi/9uUK7AxtThw/0.jpg)](http://www.youtube.com/watch?v=9uUK7AxtThw)

## 7. ラジコン化
 - 　サーボを2つにしてmilkcocoaとESP8266を使ってスマホから動かしてみる
 - 　スマホの本体の傾きによって前進、後退、右、左、その場で右回転、その場で左回転
 - 　速度は傾きに関係なく一定

### 7.1 接続図

 - サーボの定格は5Vですが、とりあえず3.3Vで動かしています。
 - タイヤはタミヤの工作キットで適当に作ってみました。
    - 使ったのはこの辺りだと思います。サーボは穴をつなげた溝に挟んでるだけです。
    - [楽しい工作シリーズ-No-157-ユニバーサルプレート-2枚セット-70157](http://www.amazon.co.jp/dp/B001VZHRXG)
    - [楽しい工作シリーズ-No-140-プーリー-Sセット-70140](http://www.amazon.co.jp/dp/B001VZHRV8)
    - [楽しい工作シリーズ-No-141-プーリー-Lセット-70141](http://www.amazon.co.jp/dp/B001VZHRVI)

| GWS S35 STD | ESP8266     |
|:------------|:------------|
| 白          | IO12 / IO13 |
| 赤          | 3.3V        |
| 黒          | GND         |

![20160621_Rotational_Servox2_Test.JPG](https://qiita-image-store.s3.amazonaws.com/0/94069/77bafb66-2183-bb37-c1d7-b48f44cbe74e.jpeg)

### 7.2 プログラム(ESP8266)
* wifiのSSIDとPASS、Milkcocoaのapp idは各自のものをご使用ください

```java:Rotational_Servo_ESP8266.ino

// ESP8266でローテーショナルサーボGWS S35 STDを制御

#include <ESP8266WiFi.h>
#include <Milkcocoa.h>
#include<Servo.h>

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "*****"  // 各自の環境に応じて変更してください
#define WLAN_PASS       "*****"  // 各自の環境に応じて変更してください

/************************* Your Milkcocoa Setup *********************************/

#define MILKCOCOA_APP_ID      "*****"  // 各自の環境に応じて変更してください
#define MILKCOCOA_DATASTORE   "gravity"

/************************* Harware Setup *********************************/

#define MotorPinR 12 // PWM用ピンアサイン GPIO 12 -> GWS S35 STD Signal
#define MotorPinL 13 // PWM用ピンアサイン GPIO 13 -> GWS S35 STD Signal
Servo servoR;  // サーボのインスタンス
Servo servoL;  // サーボのインスタンス

/************* Milkcocoa Setup (you don't need to change this!) ******************/

#define MILKCOCOA_SERVERPORT  1883

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;

const char MQTT_SERVER[] PROGMEM    = MILKCOCOA_APP_ID ".mlkcca.com";
const char MQTT_CLIENTID[] PROGMEM  = __TIME__ MILKCOCOA_APP_ID;

Milkcocoa milkcocoa = Milkcocoa(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.println(F("Milkcocoa SDK demo"));

  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.println( milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush) );
  
  // サーボの信号線を12番ピンに接続
  servoR.attach(MotorPinR);
  servoL.attach(MotorPinL);

}

void loop() {
  milkcocoa.loop();  
}

void onpush(DataElement *elem) {

  int gravityX = elem->getInt("x");  // 
  int gravityY = elem->getInt("y");  // 

  Serial.print("x:");
  Serial.print(gravityX);
  Serial.print(" y:");
  Serial.println(gravityY);

  if(gravityY > 1){
    if(gravityX > 1){  
      servoR.writeMicroseconds(1000);
      servoL.writeMicroseconds(1500); // stop
    }
    else if(gravityX < -1){
      servoR.writeMicroseconds(1500); // stop
      servoL.writeMicroseconds(2000);
    }
    else if(gravityX >= -1 && gravityX <= 1){   // 直進
      servoR.writeMicroseconds(1000);
      servoL.writeMicroseconds(2000);
    }
  }
  else if(gravityY < -1){
    if(gravityX > 1){  
      servoR.writeMicroseconds(1500); // stop
      servoL.writeMicroseconds(2000);
    }
    else if(gravityX < -1){
      servoR.writeMicroseconds(1000);
      servoL.writeMicroseconds(1500); // stop
    }
    else if(gravityX >= -1 && gravityX <= 1){   // 後退
      servoR.writeMicroseconds(2000);
      servoL.writeMicroseconds(1000);
    }
  }
  else if(gravityY >= -1 && gravityY <= 1){   // Break
    if(gravityX > 1){  
      servoR.writeMicroseconds(1000);
      servoL.writeMicroseconds(1000);
    }
    else if(gravityX < -1){
      servoR.writeMicroseconds(2000);
      servoL.writeMicroseconds(2000);
    }
    else if(gravityX >= -1 && gravityX <= 1){   // 停止
      servoR.writeMicroseconds(1500);
      servoL.writeMicroseconds(1500);
    }
  }
  
  delay(10);
  
};
```

### 7.3 プログラム(スマホ側)
* スマホ側のプログラムは[jsdo.it](http://jsdo.it/)を利用
* QRコードを使ってスマホで実行することで、スマホの傾きのデータがMilkcocoaに送信される
* [スマートフォン側のコードを書く Tutorial | Milkcocoa](https://mlkcca.com/tutorial/page3.html)をベースにしました。
* 一旦ロック画面などになって反応しない時はページを再読み込みするとうまくいくことも
* app_idは自分のものを使用してください

``` js:javascript
window.onload = function(){

  window.setInterval(sendToMilkcocoa, 100);
    
  //var currentMode = 'portrait';
  var output = document.getElementById('output');
  var prevRoundX = 0;
  var prevRoundY = 0;

  // app_idは自分のものに書き換えてください
  var milkcocoa = new MilkCocoa("*****.mlkcca.com");
  var ds = milkcocoa.dataStore('gravity');

  window.addEventListener('devicemotion', function(e){
    gravity = e.accelerationIncludingGravity;

    output.innerHTML = 'x方向: '+Math.round(gravity.x) + '<br>y方向: '+Math.round(gravity.y);

    sendModeFromGravityValue(gravity);

  },true);

    
    function sendToMilkcocoa(){
        var roundX = Math.round( gravity.x ) ;
        var roundY = Math.round( gravity.y ) ;
        if (roundX != prevRoundX){
            //ds.push({x: roundX});
            if (roundY != prevRoundY){
                ds.push({x: roundX, y: roundY});
            }
            else{
                ds.push({x: roundX, y: PrevRoundY});
            }
        }
        else if (roundY != prevRoundY){
            ds.push({x: PrevRoundX, y: roundY});
        }

        PrevRoundX = roundX;
        PrevRoundY = roundY;
    }
};
```

```html:html
<body>
<div id="output"></div>
<script src='https://cdn.mlkcca.com/v2.0.0/milkcocoa.js'></script>
<script src="main-sp.js"></script>
</body>

```

```css:css
* {
  margin: 0;
  padding: 0;
  border: 0;
}

body {
  background: #dff;
  font: 30px sans-serif;
}
```

### 7.4 実行結果

 - 実行画面(20160621 Rotational Servo Test - YouTubeに飛びます) 
    - 動かし方に戸惑っている様子がお分かりいただけると思います。

[![20160621 Rotational Servo Test - YouTube](http://img.youtube.com/vi/bvnCpWYjagA/0.jpg)](http://www.youtube.com/watch?v=bvnCpWYjagA)