アフィリエイト広告
アフィリエイト広告

Arduino のマルチタスク (3) C++ クラス

Arduino のマルチタスクとクラスについて、勉強中です。

Multi-tasking the Arduino - Part 1
Once you have mastered the basic blinking leds, simple sensors and buzzing motors, it’s time to move on to bigger and better projects. That usually involves com...

前回は、複数のステートマシンを組み合わせて、同時に 2 つのことを実行する方法について学びました。

Arduino のマルチタスク (2) 複数のステートマシン
Arduino のマルチタスクとクラスについて、勉強中です。 前回は「ステートマシン」の概念について学びました。 今回は、複数のステートマシンを組み合わせて、複数の L チカを同時に実行してみましょう。 同時に 2 つを実行する Now i...

でもこの方法は、同じコードを何度も繰り返すことになり、無駄があります。俺はそれを関数にしてみましたが、C++ クラスという「より単純でより効率的なプログラミング手法」があるとのこと。

洗練された解決法

Using the OOP features of the language we can gather together all of the state variables and functionality for a blinking LED into a C++ class

https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

さっそくやってみましょう。

回路図です。

前回と同様に、LED 2 個をつないでいます。この 2 つの LED を、同時に、独立して、点滅させます。

クラスに関して俺は、いまのところまったくわかっていないので、とりあえずスケッチを写してみました。

  1. class Flasher
  2. {
  3.     // class Member Variables
  4.     // These are initialized at startup
  5.     int ledPin;
  6.     long OnTime;
  7.     long OffTime;
  8.     // These maintain the current state
  9.     int ledState;
  10.     unsigned long previousMillis;
  11.   // Constructor - creates a Flasher
  12.   // and initializes the member variables and state
  13.   public:
  14.   Flasher(int pin, long on, long off)
  15.   {
  16.     ledPin = pin;
  17.     pinMode(ledPin, OUTPUT);
  18.     OnTime = on;
  19.     OffTime = off;
  20.     ledState = LOW;
  21.     previousMillis = 0;
  22.   }
  23.   void Update()
  24.   {
  25.     // check to see if it's time to change the state of the LED
  26.     unsigned long currentMillis = millis();
  27.     if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
  28.     {
  29.       ledState = LOW;
  30.       previousMillis = currentMillis;
  31.       digitalWrite(ledPin, ledState);
  32.     }
  33.     else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
  34.     {
  35.       ledState = HIGH;
  36.       previousMillis = currentMillis;
  37.       digitalWrite(ledPin, ledState);
  38.     }
  39.   }
  40. };
  41. Flasher led1(12, 100, 400);
  42. Flasher led2(13, 350, 350);
  43. void setup()
  44. {
  45. }
  46. void loop()
  47. {
  48.   led1.Update();
  49.   led2.Update();
  50. }

さて、どうしたものやら (;´Д`)

まず感じたことは、これは、いままでやってきたプログラムの書き方をいったんご破算にして、新たな書き方として覚えたほうが良さそうだ、ということ。

でもその基本は、

This isn’t very difficult to do.  We already have written all the code for it.  We just need to re-package it as a class.

https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

ということでしょうか。難しく考えることはなさそうです。

クラスの定義

以下、順番に見ていくけど、いろいろ間違っていたりしてもまぁ、嗤ってやってください。

クラスの宣言

最初は「クラスの宣言」です。

  1. class Flasher
  2. {
  3. };

中身がないけど、こうして定義したクラス名は「変数の型」として使えるようになる。
ここに、すべての変数を追加。これらは「メンバ変数」と呼ばれる。んだそうな。

  1. class Flasher
  2. {
  3.     // class Member Variables
  4.     // These are initialized at startup
  5.     int ledPin;
  6.     long OnTime;
  7.     long OffTime;
  8.     // These maintain the current state
  9.     int ledState;
  10.     unsigned long previousMillis;

うーん、教科書では最初に「アクセス指定子」ってのが書かれているけど、なぜないの?
省略できるらしい。省略したら private なんだそうだ。つまり、これらの変数はクラスの外から変更できない private メンバってこと。

よくわからんけど、次。

コンストラクタ

次に追加するのは「コンストラクタ」。クラスと同じ名前で、すべての変数を初期化する。

  1.   // Constructor - creates a Flasher
  2.   // and initializes the member variables and state
  3.   public:
  4.   Flasher(int pin, long on, long off)
  5.   {
  6.     ledPin = pin;
  7.     pinMode(ledPin, OUTPUT);
  8.     OnTime = on;
  9.     OffTime = off;
  10.     ledState = LOW;
  11.     previousMillis = 0;
  12.   }

クラスからオブジェクトが生成されるときに呼び出される特殊なメンバ関数、です。

… わ、わけわからん (;´Д`)

  1. Flasher led1(12, 100, 400);

こういうのが「オブジェクトの生成」。Flasher という型の変数 led1 を定義する、みたいな形。

つまり、オブジェクト led1 を生成するときに、メンバ変数を初期化するための関数、ってことか。
ちなみにここでは、引数を持ったコンストラクタを定義している。引数を持たないコンストラクタも作れる、ってことで。

ここに public: というアクセス指定子が出てきました。これ以降がクラスの外からアクセスできるようになるということ。
一般に、メンバ変数は private 、メンバ関数は public とするそうです。これらをひとまとめにして、変数にアクセスできなくすることを「カプセル化」と呼ぶ、のだとか。

メンバ関数

最後に、Blink without delay の void loop() に書かれていたコードを、void Update() という「メンバ関数」にする。

  1.   void Update()
  2.   {
  3.     // check to see if it's time to change the state of the LED
  4.     unsigned long currentMillis = millis();
  5.     if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
  6.     {
  7.       ledState = LOW;
  8.       previousMillis = currentMillis;
  9.       digitalWrite(ledPin, ledState);
  10.     }
  11.     else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
  12.     {
  13.       ledState = HIGH;
  14.       previousMillis = currentMillis;
  15.       digitalWrite(ledPin, ledState);
  16.     }
  17.   }
  18. };

メンバ関数とは、クラスオブジェクト固有の処理を行なう関数のこと。メソッドとも言う。
言葉が面倒くさい (;´Д`)

これで、LED を点滅させるためのすべての変数 (ステート) と機能 (マシン) がカプセル化されました。
うーん、でも、なんか、少し見えてきた (^_^;)

クラスを使う

定義したクラスを使ってみましょう。

Now, for every LED that we want to flash, we create an instance of the Flasher class by calling the constructor. And on every pass through the loop we just need to call Update() for each instance of Flasher.

https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

点滅させたい LED についてコンストラクタを呼び出し、オブジェクトを生成する。生成されたオブジェクトのことを「インスタンス」とも言う。
言葉が面倒くさい (;´Д`)

  1. Flasher led1(12, 100, 400);
  2. Flasher led2(13, 350, 350);

コンストラクタのところで出てきた、Flasher という型の変数 led1 を定義する、みたいな形。

引数のあるコンストラクタなので、引数を与える。引数は、ピン番号とオン時間、オフ時間の 3 つ。

  1. void setup()
  2. {
  3. }

変数の初期化とかはコンストラクタがやっているので、setup() では何もすることがない。ない。

  1. void loop()
  2. {
  3.   led1.Update();
  4.   led2.Update();
  5. }

loop() で、led1 や led2 といったインスタンスごとに Update() を呼び出す。

教科書とかみていると、ちょっと書式が違っていたりしてわけわからなくなるんだけど、基本としてこんな書き方していくんだと覚えておきましょ。使っているうちに、きっと、だんだんわかってくると思います。

This code shorter and easier to read. And, since there is no duplicated code, it also compiles smaller! That leaves you even more precious memory to do other things!

https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

今回は、クラスを定義して、それを使う方法を勉強しました。なんとかなりそうです。かな (^_^;)
次回は、サーボモータの制御をクラスで作ってみましょう。

タイトルとURLをコピーしました