EEZYbotARMにrampユニットとサーボを追加して動かす

# はじめに
ここでは，EEZYbotARM mk1のロボットアーム本体だけではなく、オプションパーツのスロープを使ってみます．

目次へ戻るには [ここ](https://qiita.com/hsgucci/items/5e3772c6e93bbfee74f7) をクリック

※本ページは実験のテキストです．

# 概要

前回はシリアル通信でEEZYbotARM mk1を動かしました．

今回は、EEZYbotARM mk1の公式オプションであるスロープ(ramp=坂)とボールを追加して動かします.

前回のシリアル通信のプログラムに書き加える形で，システム全体を動かしてみましょう．

# スロープ部品

スロープとボールは，下の図のような部品です．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/223e3c9f-689e-eb68-aa86-49dd96363e66.png)

次の参考動画のように動作します,

　参考動画：https://clipchamp.com/watch/EfhEGhqDTrg

ボールが上から転がってくるとスピードが出すぎて飛び出してしまうため,
サーボモータで作られたゲートで一旦ボールを受け止めて,その後でゲートを開いてゆっくりと送り出すためです．

## ゲートのサーボをPCA9685ボードに挿す

0～3番のピンは既にEEZYbotARMに使われているので,ゲートのサーボのコネクタは4番以降のピンに挿します．
ピンの色に注意して差し込んでください.

この記事中では,10番のピンに差し込みました．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/b57011d5-65b0-b55a-8921-f4beea9cb3fd.png)




# スケッチを書き込む

前回の`serial2servo.ino`に,更に書き加える形でプログラムを追加します.

完成すると，以下のようなプログラムになります．
前回の違いを出すために，ファイル名は `serial2servo_2.ino` としました。
このプログラムは10番ピンに追加する形で書いているので,違うピンに挿した人はそれに合わせてください.

```cpp:serial2servo_2.ino
// ライブラリの読み込み
#include <Wire.h>                     // I2C通信
#include <Adafruit_PWMServoDriver.h>  // PCA9685でサーボを動かす

// サーボのパルス幅(μs)
#define SERVO_MIN   800   // 最小パルス幅
#define SERVO_CENT 1500   // ニュートラル(センター)
#define SERVO_MAX  2200   // 最大パルス幅

// ロボットアームのピン番号
#define SERVO0_YAW    0
#define SERVO1_LINK1  1
#define SERVO2_LINK2  2
#define SERVO3_HAND   3
#define SERVO10_GATE  10

// サーボ制御クラスの作成
Adafruit_PWMServoDriver Servo = Adafruit_PWMServoDriver(0x40);  // PCA9685のI2Cアドレスは0x40番地

// サーボに出力するマイクロ秒の値を格納する変数
int sv0 = 1500; // 初期値は1500us
int sv1 = 1500;
int sv2 = 1500;
int sv3 = 1500;
int sv10 = 1500;

// 初期化関数
void setup() {
  Serial.begin( 9600 );     // シリアル通信を初期化する。通信速度は9600bps

  // PWMドライバを初期化する
  Servo.begin();

  // PWM周波数を50Hzに設定する（デフォルト値）
  Servo.setPWMFreq(50);

  // 1秒待機する
  delay(1000);

  // 初期姿勢をセット
  Servo.writeMicroseconds(SERVO0_YAW,   sv0);
  Servo.writeMicroseconds(SERVO1_LINK1, sv1);
  Servo.writeMicroseconds(SERVO2_LINK2, sv2);
  Servo.writeMicroseconds(SERVO3_HAND,  sv3);
  Servo.writeMicroseconds(SERVO10_GATE,  sv10);

  Serial.println( "Hello Arduino!" );   // 最初に1回だけメッセージを表示する
}

// ループ関数
void loop() {
  char key;     // 受信データを格納するchar型の変数

  // 受信データがあった時だけ、サーボの変数の値を変える
  if ( Serial.available() ) {       // 受信データがあるか？
    key = Serial.read();            // 1文字だけ読み込む

    // keyの文字に応じて、行う処理を切り替える
    switch ( key ) {
      // 土台の操作
      // qキーが押された時の処理
      case 'q':        sv0 += 100;        break;
      // aキーが押された時の処理
      case 'a':        sv0 = 1500;        break;
      // zキーが押された時の処理
      case 'z':        sv0 -= 100;        break;

      // リンク1操作
      // wキーが押された時の処理
      case 'w':        sv1 += 100;        break;
      // sキーが押された時の処理
      case 's':        sv1 = 1500;        break;
      // xキーが押された時の処理
      case 'x':        sv1 -= 100;        break;

      // リンク2操作
      // eキーが押された時の処理
      case 'e':        sv2 += 100;        break;
      // dキーが押された時の処理
      case 'd':        sv2 = 1500;        break;
      // cキーが押された時の処理
      case 'c':        sv2 -= 100;        break;

      // ハンド操作
      // rキーが押された時の処理
      case 'r':        sv3 += 100;        break;
      // fキーが押された時の処理
      case 'f':        sv3 = 1500;        break;
      // vキーが押された時の処理
      case 'v':        sv3 -= 100;        break;

      // ゲート操作
      // tキーが押された時の処理
      case 't':        sv10 += 100;        break;
      // gキーが押された時の処理
      case 'g':        sv10 = 1500;        break;
      // bキーが押された時の処理
      case 'b':        sv10 -= 100;        break;

      // 上記以外の場合の処理(何もしない)
      default:
        break;
    } //switch文の末尾

    // 上限・下限のソフトウェアリミットはswitch文の外に出してプログラムを読みやすくした
    if ( sv0 > SERVO_MAX ) sv0 = SERVO_MAX;
    if ( sv0 < SERVO_MIN ) sv0 = SERVO_MIN;

    if ( sv1 > SERVO_MAX ) sv1 = SERVO_MAX;
    if ( sv1 < SERVO_MIN ) sv1 = SERVO_MIN;

    if ( sv2 > SERVO_MAX ) sv2 = SERVO_MAX;
    if ( sv2 < SERVO_MIN ) sv2 = SERVO_MIN;

    if ( sv3 > SERVO_MAX ) sv3 = SERVO_MAX;
    if ( sv3 < SERVO_MIN ) sv3 = SERVO_MIN;

    if ( sv10 > SERVO_MAX ) sv10 = SERVO_MAX;
    if ( sv10 < SERVO_MIN ) sv10 = SERVO_MIN;

    // サーボに出力
    Servo.writeMicroseconds(SERVO0_YAW,   sv0);
    Servo.writeMicroseconds(SERVO1_LINK1, sv1);
    Servo.writeMicroseconds(SERVO2_LINK2, sv2);
    Servo.writeMicroseconds(SERVO3_HAND,  sv3);
    Servo.writeMicroseconds(SERVO10_GATE, sv10);

    // シリアルモニタに現在の出力値を表示
    Serial.print( sv0 );
    Serial.print("\t");
    Serial.print( sv1 );
    Serial.print("\t");
    Serial.print( sv2 );
    Serial.print("\t");
    Serial.print( sv3 );
    Serial.print("\t");
    Serial.println( sv10 );
  } //if文の末尾
}
```

# 動作テスト

Arduinoに書き込みが終わると，ロボットアームに力が入って動きます．
最初は全てのサーボがニュートラル位置(1500マイクロ秒)です．

もし，ロボットアームが変な姿勢になっていたら，サーボがズレているかもしれないので，担当の先生に申し出てください．

シリアルターミナルで，`t`キーを入力して[送信]してください．
ゲートが少し動いたでしょうか.

`b`キーを送信すると，逆方向に回ります．

`g`キーを送信すると，ニュートラル位置に戻ります．

以下の図のようにプログラムしてあるわけです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/a27cef2f-41fb-f0a4-4acb-5abe8ba91918.png)

# プログラム解説

それではプログラムを解説していきます．

## 準備

前回のプログラム`serial2servo.ino`と異なる部分だけ抜き出して解説します．

```cpp:#defineとグローバル変数の追加
#define SERVO10_GATE  10

int sv10 = 1500;
```

ゲートのサーボを10番ピンに挿したため，
#defineとグローバル変数を追加しました．

## 初期化関数

次は初期化関数です．
こちらも，前回のプログラムと異なる部分だけ解説します．

```cpp:setup関数から抜粋
  // 初期姿勢をセット
  Servo.writeMicroseconds(SERVO10_GATE,  sv10);
```

10番ピンのゲートの出力だけ書き加えました．


## ループ関数

次はループ関数です．

```cpp:loop関数
// ループ関数
void loop() {
  char key;     // 受信データを格納するchar型の変数

  // 受信データがあった時だけ、サーボの変数の値を変える
  if ( Serial.available() ) {       // 受信データがあるか？
    key = Serial.read();            // 1文字だけ読み込む

    // keyの文字に応じて、行う処理を切り替える
    switch ( key ) {
      // 土台の操作
      // qキーが押された時の処理
      case 'q':        sv0 += 100;        break;
      // aキーが押された時の処理
      case 'a':        sv0 = 1500;        break;
      // zキーが押された時の処理
      case 'z':        sv0 -= 100;        break;

      // リンク1操作
      // wキーが押された時の処理
      case 'w':        sv1 += 100;        break;
      // sキーが押された時の処理
      case 's':        sv1 = 1500;        break;
      // xキーが押された時の処理
      case 'x':        sv1 -= 100;        break;

      // リンク2操作
      // eキーが押された時の処理
      case 'e':        sv2 += 100;        break;
      // dキーが押された時の処理
      case 'd':        sv2 = 1500;        break;
      // cキーが押された時の処理
      case 'c':        sv2 -= 100;        break;

      // ハンド操作
      // rキーが押された時の処理
      case 'r':        sv3 += 100;        break;
      // fキーが押された時の処理
      case 'f':        sv3 = 1500;        break;
      // vキーが押された時の処理
      case 'v':        sv3 -= 100;        break;

      // ゲート操作
      // tキーが押された時の処理
      case 't':        sv10 += 100;        break;
      // gキーが押された時の処理
      case 'g':        sv10 = 1500;        break;
      // bキーが押された時の処理
      case 'b':        sv10 -= 100;        break;

      // 上記以外の場合の処理(何もしない)
      default:
        break;
    } //switch文の末尾

    // 上限・下限のソフトウェアリミットはswitch文の外に出してプログラムを読みやすくした
    if ( sv0 > SERVO_MAX ) sv0 = SERVO_MAX;
    if ( sv0 < SERVO_MIN ) sv0 = SERVO_MIN;

    if ( sv1 > SERVO_MAX ) sv1 = SERVO_MAX;
    if ( sv1 < SERVO_MIN ) sv1 = SERVO_MIN;

    if ( sv2 > SERVO_MAX ) sv2 = SERVO_MAX;
    if ( sv2 < SERVO_MIN ) sv2 = SERVO_MIN;

    if ( sv3 > SERVO_MAX ) sv3 = SERVO_MAX;
    if ( sv3 < SERVO_MIN ) sv3 = SERVO_MIN;

    if ( sv10 > SERVO_MAX ) sv10 = SERVO_MAX;
    if ( sv10 < SERVO_MIN ) sv10 = SERVO_MIN;

    // サーボに出力
    Servo.writeMicroseconds(SERVO0_YAW,   sv0);
    Servo.writeMicroseconds(SERVO1_LINK1, sv1);
    Servo.writeMicroseconds(SERVO2_LINK2, sv2);
    Servo.writeMicroseconds(SERVO3_HAND,  sv3);
    Servo.writeMicroseconds(SERVO10_GATE, sv10);

    // シリアルモニタに現在の出力値を表示
    Serial.print( sv0 );
    Serial.print("\t");
    Serial.print( sv1 );
    Serial.print("\t");
    Serial.print( sv2 );
    Serial.print("\t");
    Serial.print( sv3 );
    Serial.print("\t");
    Serial.println( sv10 );
  } //if文の末尾
}
```

前回の記事のサンプルプログラムは，土台を動かす`q`,`a`,`z`キーのみで，リンクやハンドは自分で書き加える必要がありました.
今回のプログラムは，すべてのサーボを書いてあります.

# 課題

キーボード入力をして,

0. ゲートを閉じる
0. ボールをつまみに行く
0. ボールをつまむ
0. リリースポイントまで移動
0. ボールを離す
0. ゲートを開く

をやってみましょう．

# まとめ

今回は，シリアル通信を使ってEEZYbotARM mk1とスロープを動かせるようにプログラムしてみました．

# おわりに

これでシリアル通信での動作は完了です．

次回は予め与えた数値で動かすチャレンジをしてみます．

[目次](https://qiita.com/hsgucci/items/5e3772c6e93bbfee74f7#%E5%AE%9F%E9%A8%93%E6%89%8B%E9%A0%86) へ戻って次の作業を行ってください。