Posts モータの制御プログラムをライブラリ化しよう
Post
Cancel

モータの制御プログラムをライブラリ化しよう

この記事で使うソースコード

ここではモータの制御プログラムを関数化しようで書いたプログラムをファイル分割してライブラリ化します。 モータの制御プログラムはAのファイル、センサのプログラムはBのファイルというように分割して作成することで、役割ごとにプログラムを分けることができます。 分割したプログラムはライブラリとして読み込むことで使用できます。 ライブラリ化しておくと、様々なプログラムで再利用することが可能となり、大規模なプログラムや似た処理のプログラムを作るときに楽ができます。

ヘッダファイルとcppファイルの作成


まずはArudino IDE上部メニューの「ファイル」から「新規ファイル」をクリックして新しいスケッチ(inoファイル)を作成します。 100kinsat_motor_library.inoというファイル名にしました。 次にヘッダファイルとcppファイルを追加します。 ファイルの追加はIDEの右上部にある三角アイコンの「新規タブ」からできます。 ファイル名にmotor.hppmotor.cppを入力して作成します。

new_file 新規ファイルの作成

ヘッダファイルのプログラムを実装する


まずはヘッダファイルのプログラムを書いていきます。 ヘッダファイルの上部と下部に#ifndef#endifを書くことでインクルードガードしています。 #ifndef __MOTOR_H__と書くことで、「もし__MOTOR_H__が定義されていなかったら、#endifまでのプログラムを実行する」というような意味になります。 これはヘッダファイルがプログラム中で二重に読み込まれることを防ぐ処置です。 先に述べたように、分割したプログラムは他のプログラムでライブラリとして読み込みます。 ファイルを分割する理由は、人間がプログラムするうえで扱いやすくするためで、分割したプログラムは最終的に一つのプログラムとしてコンパイルされます。 そのとき、複数箇所で同じライブラリを読み込んでいると二重に読み込んでしまい、コンパイルエラーとなってしまいます。 インクルードガードを書いておくことで、既にライブラリが読み込まれていた場合、二重に読み込まないようにしてくれます。

1
2
3
4
5
6
7
8
9
10
#ifndef __MOTOR_H__ // インクルードガード
#define __MOTOR_H__

#include "Arduino.h"

class Motor {
  // ここにクラスの中身を書く
};

#endif // __MOTOR_H__

ヘッダファイルの中身には、Motorクラスを定義しています。 Motorクラスはコンストラクタ(クラスの初期化をする処理)とforward()back()stop()メソッド持っています。 c言語において、クラスに定義した関数のことをメソッドと呼びます。

forward()back()stop()メソッドにはcppファイルでそれぞれ前進、後退、停止の処理を実装していきます。 これらのメソッドはpublicで定義しています。 publicにすることでこのライブラリを読み込んだプログラム上で、これらのメソッドを呼び出すことができます。

privateにはモータに接続しているマイコンのピンなどの変数を定義しています。 privateにすることで、ライブラリを読み込んだプログラム上からは参照できなくすることができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Motor {
  public:
    Motor();

    void forward(int pwm); // 前進
    void back(int pwm); // 後退
    void stop(); // 停止

  private:
    const int motorA[3] = {4, 13, 25}; // AIN1, AIN2, PWMA
    const int motorB[3] = {14, 27, 26}; // BIN1, BIN2, PWMB

    const int CHANNEL_A = 0;
    const int CHANNEL_B = 1;

    const int LEDC_TIMER_BIT = 8;
    const int LEDC_BASE_FREQ = 490;
};

cppファイルのプログラムを実装する


次に、cppファイルを実装していきます。 最初の行は、上で作成したヘッダファイルを読み込んでいます。

1
#include "motor.hpp"

そして、ヘッダファイルのMotorクラスに定義した各メソッドを定義していきます。 Motor::Motor()Motorクラスのコンストラクタになります。

1
2
3
4
5
6
7
8
9
10
11
12
Motor::Motor() {
  for (int i = 0; i < 3; i++) {
    pinMode(motorA[i], OUTPUT);
    pinMode(motorB[i], OUTPUT);
  }

  ledcSetup(CHANNEL_A, LEDC_BASE_FREQ, LEDC_TIMER_BIT);
  ledcSetup(CHANNEL_B, LEDC_BASE_FREQ, LEDC_TIMER_BIT);

  ledcAttachPin(motorA[2], CHANNEL_A);
  ledcAttachPin(motorB[2], CHANNEL_B);
}

以下は前進制御のメソッドです。 Motor::forward()と書くことで、forwardメソッドはMotorクラスに紐づいたメソッドとなります。 backstopメソッドも同様です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * 前進
 */
void Motor::forward(int pwm) {
  // 左モータ(CCW、反時計回り)
  digitalWrite(motorA[1], LOW);
  digitalWrite(motorA[0], HIGH);
  ledcWrite(CHANNEL_A, pwm);

  // 右モータ(CW、時計回り)
  digitalWrite(motorB[1], LOW);
  digitalWrite(motorB[0], HIGH);
  ledcWrite(CHANNEL_B, pwm);
}

これで、モータ制御のヘッダファイルとcppファイルが作成できました。

メインのプログラムから分割したファイルを読み込んで使う


最後に、メインのプログラムからモータ制御のヘッダファイルとcppファイルをライブラリとして読み込みます。 100kinsat_motor_library.inoの最初の行で作成したライブラリをincludeしています。 そして、Motorクラスのmotorという変数を定義して、コンストラクタを呼び出しています。 これで、motor.forward()のように「.(ドット)」でメソッドを繋いで、メソッドを使うことができます。

モータの制御プログラムを関数化しようでは、一つのファイルにすべてのプログラムを記述していましたが、ファイル分割することで役割ごとにファイルを分けることができました。 そして、メインのプログラムとなる100kinsat_motor_library.inoはすっきりと書くことができています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "motor.hpp"

Motor motor = Motor();

void setup() {
}

void loop() {
  motor.forward(100);
  delay(1000);

  motor.stop();
  delay(1000);

  motor.back(100);
  delay(1000);

  motor.stop();
  delay(1000);
}

ここまででモータ制御のプログラムをloop関数に書く方法、関数化する方法、ファイル分割する方法の3つのやり方で書いてきました。 CanSatの制御プログラムはモータ制御だけでなく、各センサを読み込んだり、取得したデータを記録したりとさまざまなプログラムを書く必要があります。 そうすると、プログラムは大規模なものとなり、一つのファイルに書くだけでは、大変見通しの悪いソースコードとなってしまいます。 見通しの悪いソースコードはバグが発生しやすく、バク修正等のメンテナンスも煩雑になりがちです。 機能ごとに適切にファイル分割したプログラムを作成していきましょう。

This post is licensed under CC BY 4.0 by the author.

モータの制御プログラムを関数化しよう

9軸センサの値を取得しよう