EEZYbotARMをキー入力で動かす

# はじめに
ここでは，シリアル通信でEEZYbotARM mk1の各関節のサーボを動かしてみます．

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

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

# 概要

前回はAdafruit PWM Servo Driver Library を使ってEEZYbotARM mk1を動かしてみました．
`9685_servo_test.ino`は，全ての関節がニュートラル角度になるだけの面白くないプログラムでした．

今回は，各関節をシリアル通信を使って動かしてみます．
「キーを入力すると少しづつ角度が変わる」というやり方です．

ですが，プログラム解説を読めば`9685_servo_test.ino`がベースになっていることがよく分かると思います．

# スケッチを書き込む

メニューバーから[ファイル]-[新規ファイル]をクリックし、新しいプログラムを書く準備をしてください。

以下のプログラムを打ち込み、コンパイル/実行してみましょう。
ファイル名は `serial2servo.ino` としました。
"2"は"to"を省略する文字です．すなわち「シリアルからサーボへ」という意味になります．

```cpp:serial2servo.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

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

// サーボに出力するマイクロ秒の値を格納する変数
int sv0 = 1500; // 初期値は1500us
int sv1 = 1500;
int sv2 = 1500;
int sv3 = 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);

  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;   // パルス幅を+100us
        if ( sv0 > SERVO_MAX ) sv0 = SERVO_MAX;     // 2200を超えないようにソフトウェアで制限する
        break;

      // aキーが押された時の処理
      case 'a':
        sv0 = 1500;   // ニュートラルの1500us
        break;

      // qキーが押された時の処理
      case 'z':
        sv0 -= 100;   // パルス幅を-100us
        if ( sv0 < SERVO_MIN ) sv0 = SERVO_MIN;     // 0以下にならないようにソフトウェアで制限する
        break;

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

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

    // シリアルモニタに現在の出力値を表示
    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();
  } //if文の末尾
}
```

# 動作テスト

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

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

ここで，シリアルターミナルを開いて，`q`キーを入力して[送信]してください．
EEZYbotARMの土台が少し動いたことが分かるでしょうか．

更に`q`キーを送信すると，もっと動きます．

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/b22c835b-d84b-b294-b811-cf06c19bdaf7.png)


# プログラム解説

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

## 準備

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

```cpp:グローバル変数の宣言
// サーボに出力するマイクロ秒の値を格納する変数
int sv0 = 1500; // 初期値は1500us
int sv1 = 1500;
int sv2 = 1500;
int sv3 = 1500;
```

キー入力で動かしたいので，各サーボに出力する値をグローバル変数で宣言しています．
初期値として1500マイクロ秒を入れてあります．

## 初期化関数

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

```cpp:setup関数から抜粋
  // 初期姿勢をセット
  Servo.writeMicroseconds(SERVO0_YAW,   sv0);
  Servo.writeMicroseconds(SERVO1_LINK1, sv1);
  Servo.writeMicroseconds(SERVO2_LINK2, sv2);
  Servo.writeMicroseconds(SERVO3_HAND,  sv3);

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

最初に出力する数値を，固定値ではなくグローバル変数に入っている値に変えました．
最初は1500が出ることになります．

また，シリアル通信を使うので，初期メッセージを投げるようにしました．

## ループ関数

次はループ関数です．
実験１　[シリアル通信でデジタル出力](https://qiita.com/hsgucci/items/26d399ac0cd68779bec5)　でやったことと同様な記述をしています．

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

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

    // keyの文字に応じて、行う処理を切り替える
    switch ( key ) {
      // qキーが押された時の処理
      case 'q':
        sv0 += 100;   // パルス幅を+100us
        if ( sv0 > SERVO_MAX ) sv0 = SERVO_MAX;     // 2200を超えないようにソフトウェアで制限する
        break;

      // aキーが押された時の処理
      case 'a':
        sv0 = 1500;   // ニュートラルの1500us
        break;

      // qキーが押された時の処理
      case 'z':
        sv0 -= 100;   // パルス幅を-100us
        if ( sv0 < SERVO_MIN ) sv0 = SERVO_MIN;     // 0以下にならないようにソフトウェアで制限する
        break;

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

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

    // シリアルモニタに現在の出力値を表示
    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();
  } //if文の末尾
}
```

`q`キーを押すと，グローバル変数`sv0`の値を+100，
`z`キーを押すと，グローバル変数`sv0`の値を-100しています．

ただし，キーを何回も押しすぎてしまうと，パルス幅が800～2200マイクロ秒からはみ出してしまう危険があるので，
最小値，最大値を超えないようにソフトウェアリミットを設けてあります．

'a'キーで初期のニュートラル角度に戻します．

# プログラムの改良

今のままでは，土台のサーボしか動かないので，
他のサーボも動くようにプログラムを改良してみましょう．

具体的には，以下の図のようにしましょう．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/61fdfd6b-8eac-ea4f-5221-407ca6943a50.png)

`w`,`s`,`x`でリンク１を動かすために`sv1`を，
`e`,`d`,`c`でリンク２を動かすために`sv2`を，
`r`,`f`,`v`でハンドを動かすために`sv3`を
変えるようにswitch内のcase文を追加しましょう．

ここでも，キー押しすぎて値が最小値，最大値を超えないようにソフトウェアリミットを設ける必要があります．

# まとめ

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

複数のモータやサーボを持っているロボットでは，最初に作る動作テストのプログラムは，
キーボードをコントローラーに見立てて，操縦できるように作ることが多いです．
実験１　[シリアル通信でデジタル出力](https://qiita.com/hsgucci/items/26d399ac0cd68779bec5#%E5%8B%95%E4%BD%9C%E3%83%86%E3%82%B9%E3%83%88)　でも同様な紹介を少しました．
今回やったことがまさにそれです．

アナログジョイスティックで滑らかに動かせると格好いいのですが，
いきなり高いハードルでチャレンジするのは無謀です．
「最初は」こんな感じのデジタル操作で良いのです．


# おわりに

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

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

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