[ESP32]LINEbotからロボットアームを動かす/ロボットアーム編

※本記事は，[[ESP32]LINEbotからロボットアームを動かす/LINEbot編](https://qiita.com/fauntleroy/items/435e8d21adba9023078d)　と合わせて御覧ください．

#概要
ざっくりいうと，家の外からLINE botで指示を送信すると，自宅など別のネットワーク下にあるロボットアームが指示に応じて動いてリモコンを押す，というものを@fauntleroy 氏と共同で開発した．

私がロボットアーム側(クライアント)を担当し，@fauntleroy 氏がLINE bot側(サーバーサイド)を担当した．

イメージとしては，下記図の通り．

![https---qiita-image-store.s3.ap-northeast-1.amazonaws.com-0-288721-51bce6b7-43e8-2147-3e0b-9a1e789efee2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193813/412a041d-8d2d-20da-6341-5602aecbc74f.jpeg)
常にロボットアームからサーバーにGETリクエストを投げておき，LINE botから指示が投げられたときに`state = 1`など指示内容をロボットアーム側に受け渡している．

LINE側はこちらの@fauntleroy 氏の記事を御覧ください．
[[ESP32]LINEbotからロボットアームを動かす/ロボットアーム編](https://qiita.com/fauntleroy/items/435e8d21adba9023078d)　
(上記画像は@fauntleroy 氏より拝借しました．)


#使用したもの
- [ESP32] (https://www.amazon.co.jp/gp/product/B06XWP81GP/ref=ppx_yo_dt_b_asin_title_o01_s01?ie=UTF8&psc=1)
- [ロボットアーム](https://www.amazon.co.jp/gp/product/B0179BTLVQ/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1)
- LINE Messaging API
- heroku

# 環境設定
ESP32をMacのArduino用IDEで認識させるためには，通常のボードマネージャだけでなく，追加でドライバが必要．
このあたりのサイトを参照
[MacでESP32のLチカ（Arduino IDE版)](https://qiita.com/naohiro2g/items/b8baa3f0e1a880ae9d0a)

 - 簡単に方針を書いておくと，はじめに，ツール->ボード->ボードマネージャ からESP32を検索して入れる．
 - そのあと[こちら](https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md)．
 - [こちら](https://jp.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers#mac)のドライバをインストール．

※なお，私の環境では非常に上記ドライバが不安定で，macでESP32が認識されない問題があった．対応策としては，都度少し古いバージョンのドライバを入れ直すことだが，明確な解決方法は見つかっていない．

# 実装方法
#### 諸々初期化
サーバー通信用と，ロボットアームの制御用で分けて初期化用のコードを書いた．

```c

#include <ESP32Servo.h>
#include <WiFi.h>
#include <string> 

//ロボット用宣言
Servo servo1,servo2,servo3;

int servo3Pin = 25;
int servo2Pin = 2;
int servo1Pin = 15;

int minUs = 500;
int maxUs = 2400;
int pos = 0;   

//server用宣言
WiFiClient client;
//wifiの設定
const char* ssid     = "ここにSSIDをかく";
const char* password = "ここにWIFIのパスワードを書く";

//接続先サーバー
const char* server     = "hogehoge.com";
//ポート番号は適宜．今回は80番だった．
const int httpPort = 80; 

int fsr_array[10];
int count = 0;

```

#### void setup()
`void setup()`はコードが実行されるときにはじめに一度だけ読まれる部分である．
本来初期化をここに書くことが多い．

```c

void setup() {

  Serial.begin(9600);
  delay(5000);
  
  //サーボポート等初期化
  servo1.attach(servo1Pin, minUs, maxUs);
  servo2.attach(servo2Pin, minUs, maxUs);
  servo3.attach(servo3Pin, minUs, maxUs);
  //向き固定
  servo1.write(0);
  servo2.write(45);
  servo3.write(180);

  
  Serial.println("===ロボット設定完了===");

  //wifi接続
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());


  //サーバー接続
  if (client.connect(server,httpPort)) {
    Serial.println("Client Connected");
  } else {
    Serial.println("No Connection　desu");
  }

}


```


#### void loop()

setup()が呼び出された後は，基本的にこちらが呼ばれる．
まず，loop内で使用する関数の定義．今回は，指示を受けたら，ボタンを押すのに適切な速さでロボットアームを動かす関数を実装した．

```c

void ButtunPush(Servo servo2){

   //リモコンにロボットアームを近づける
   for (pos = 45; pos >= 30; pos -= 1) { //45度から30度まで1度ずつ減らす
    servo2.write(pos);
    delay(50);  //1度減らすごとに0.5秒待つ．
  }

   //リモコンのボタンを押す
   for (pos = 30; pos >= 0; pos -= 1) { //30度から0度まで1度ずつ減らす
    servo2.write(pos);
    delay(15);             //1度減らすごとに0.15秒待つ．
  }

  //リモコンを初期位置に戻す
  for (pos = 0; pos <= 45; pos += 1) { //0度から45度まで増やす．
    servo2.write(pos);
    delay(50);
  }
}
```

最後に，loop内を実装．

```c

void loop() {

  if (client.connect(server,httpPort)) {
    Serial.println("Client Connected on loop"+(String)count+"回目のループ");
  } else {
    Serial.println("No Connection on loop");
  }
  count++;

  // リクエスト用URIを作成
  // GETリクエストを投げたい場所をかくところ．
  String url = "/ToI"; 

  Serial.print("Requesting URL: ");
  Serial.println(url);

  // ここでサーバーにリクエストを送信
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + server + "\r\n" + 
               "Connection: close\r\n\r\n");
  //一秒ごとに
   delay(1000);

  Serial.println("Respond:");
  int i = 0;
  String line;
  while(client.available()){
    line = client.readStringUntil('\r');//シリアルバッファから
  }
  
    line.toCharArray(number,line.length()+1);
    Serial.println("motion_numberは");
    Serial.println(number[1]);

    motion_number = number[1];
    
  Serial.println();
  Serial.println("closing connection");

  //受け取った値次第で変更
  //1が除湿，2が冷房
  if  (motion_number.equals("2")) {
      for (pos = 0; pos <= 5; pos += 1) {
          servo1.write(pos);
          delay(50);
  }
  ButtunPush(servo2);

   }else if (motion_number.equals("1")) { //除湿
          for (pos = 5; pos >= 0; pos -= 1) {degrees
        // in steps of 1 degree
        servo1.write(pos);
        delay(50);
      }
      ButtunPush(servo2);    
   }else {
    for (pos = 0; pos <= 5; pos += 1) {
          servo1.write(pos);
          delay(50);
      }
    }
}

```



# コード
最終的にまとめると，ロボットアーム側のコードは以下のようになる．

```c

#include <ESP32Servo.h>
#include <WiFi.h>
#include <string> 



//ロボット用宣言
Servo servo1,servo2,servo3; 

int servo3Pin = 25;
int servo2Pin = 2;
int servo1Pin = 15;

int minUs = 500;
int maxUs = 2400;
int pos = 0;

//server用宣言
WiFiClient client;
const char* ssid     = "ここにSSIDをかく";
const char* password = "ここにWIFIのパスワードを書く";
const char* server     = "hogehoge.com";
//ポート番号は適宜．今回は80番だった．
const int httpPort = 80; 

int fsr_array[10];
int count = 0;
String motion_number;
char number[1000];
char buff[256];
unsigned char a='A';


void setup() {

  Serial.begin(9600);
  delay(5000);
  
  //サーボポート等初期化
  servo1.attach(servo1Pin, minUs, maxUs);
  servo2.attach(servo2Pin, minUs, maxUs);
  servo3.attach(servo3Pin, minUs, maxUs);
  //向き固定
  servo1.write(0);
  servo2.write(45);
  servo3.write(180);

  
  Serial.println("===ロボット設定完了===");

  //wifi接続
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());


  //サーバー接続
  if (client.connect(server,httpPort)) {
    Serial.println("Client Connected");
  } else {
    Serial.println("No Connection　desu");
  }

}


void ButtunPush(Servo servo2){

   //リモコンにロボットアームを近づける
   for (pos = 45; pos >= 30; pos -= 1) { //45度から30度まで1度ずつ減らす
    servo2.write(pos);
    delay(50);  //1度減らすごとに0.5秒待つ．
  }

   //リモコンのボタンを押す
   for (pos = 30; pos >= 0; pos -= 1) { //30度から0度まで1度ずつ減らす
    servo2.write(pos);
    delay(15);             //1度減らすごとに0.15秒待つ．
  }

  //リモコンを初期位置に戻す
  for (pos = 0; pos <= 45; pos += 1) { //0度から45度まで増やす．
    servo2.write(pos);
    delay(50);
  }
}




void loop() {

  if (client.connect(server,httpPort)) {
    Serial.println("Client Connected on loop"+(String)count+"回目のループ");
  } else {
    Serial.println("No Connection on loop");
  }
  count++;

  // リクエスト用URIを作成
  String url = "/ToI";

  Serial.print("Requesting URL: ");
  Serial.println(url);

  // サーバーにリクエストを送信
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + server + "\r\n" + 
               "Connection: close\r\n\r\n");
  //一秒ごとに
   delay(1000);

  Serial.println("Respond:");
  int i = 0;
  String line;
  while(client.available()){
    line = client.readStringUntil('\r');//シリアルバッファから
  }
  
    line.toCharArray(number,line.length()+1);
    Serial.println("motion_numberは");
    Serial.println(number[1]);

    motion_number = number[1];
    
  Serial.println();
  Serial.println("closing connection");

  //受け取った値次第で変更
  //1が除湿，2が冷房
  if  (motion_number.equals("2")) {
      for (pos = 0; pos <= 5; pos += 1) {
          servo1.write(pos);
          delay(50);
  }
  ButtunPush(servo2);

   }else if (motion_number.equals("1")) { //除湿
          for (pos = 5; pos >= 0; pos -= 1) {
        servo1.write(pos);
        delay(50);
      }
      ButtunPush(servo2);    
   }else {
    for (pos = 0; pos <= 5; pos += 1) {
          servo1.write(pos);
          delay(50);
      }
    }
}
```