Arduinoの初心者:ステートマシンを別のクラスとライブラリにパックする

有限状態マシンの記述に関する最後の記事では、便利な繰り返し使用のために、独創的なコードをC ++のクラスの形式でパックすることを約束しました。 として、以前のSmartButton開発でも同じことを行います。 したがって、arduinoライブラリとOOPの不可解な世界に入ります。







ライブラリフォルダー







なぜこれがすべて必要なのですか?



Arduino IDEを使用すると、C ++ 11構文を使用できます。 つまり、非常に開発されたオブジェクト指向言語があります。 私たちは独創的なコードに集中したいのですが、プログラムによって拡散された余分なロジックにより、集中できないことがよくあります。 たとえば、あらゆる種類のディスプレイ、ボタン、センサー、リレーを考えてみましょう。それぞれに独自のロジックがあり、プログラムの一般的なロジックと組み合わせてください。 同じ、たとえば表示。 静的および可変の多くのフィールドがあります。 ああ、フィールドは同じクラスです。 このフィールドは、メニューに入る(メニュークラス)かどうか、または物理ディスプレイ(ディスプレイ:操作、設定、診断など)にある仮想ディスプレイ(クラス)の一部になることができます。 メニューは、ボタン(ボタンのクラスは異なる場合があります)またはジョイスティック(クラス)によって制御されます。 これらはすべて表示クラスであり、プログラムで次のように宣言できます。







#include "Display.h" Display disp(   );
      
      





何かのためにひざまずいてプロジェクトをしていて、後で何かを変更したり、ベストプラクティスの一部を再利用したりする場合は、Arduinoライブラリとして行われたものを設計する方が良いでしょう。 理想的には、もちろん、誰かがあなたのコードを修正または補足することを気にせず、気にしないなら、他の人のためにGithubに置いてください。







前回の記事でボタンを作成したので、クラスとライブラリとしてデザインしましょうか?







そのため、私たちのタスクは、スケッチを作成できるようにすることです。







 #include "myButton.h" myButton b1(4),b2(5),b3(12); //     4, 5  12. loop() { b1.run(); b2.run(); b3.run(); // ... if (b1.clicked()) doSomething(); //     ,  . // ... }
      
      





Arduinoライブラリを作成する方法は?



簡単です!







最初に、ライブラリの名前を決定する必要があります。 たとえば、 MyLibになります。







コンピューター上のスケッチの場所を見つけます。 それらはそれぞれ独自のパパにあり、それらの隣にはライブラリフォルダ(ライブラリ)があります。 たとえば、ポピー/ユーザー/ユーザー/ドキュメント/ Arduino /ライブラリおよびWindows c:\ユーザー\ユーザー\ドキュメント\ Arduino \ライブラリ。 私自身はケシの上に座っており、Windowsでの方法を知りません。 あなたが見つけます。







このライブラリフォルダで、新しいMyLibフォルダを作成します。つまり、ライブラリの名前を使用します。 そこに行きます。







この新しいフォルダーに、プロジェクトに含める1つ以上のMyLib.hファイルを作成する必要があります。 最小限のコンテンツは次のようになります。







 #ifndef MYLIB_H #define MYLIB_H #if ARDUINO >= 100 #include <Arduino.h> #else #include <WProgram.h> #endif //    #endif
      
      





ここで理由を説明します。 以下の設計により、エラーなしでライブラリをコードに数回含めることができます。 ライブラリの名前は大文字で使用することをお勧めします。 これは厳密には必要ではありませんが、誰もがそれを行い、あなたは目立ちません。 タスクは、このヘッダーファイルの識別子であるMYLIB_Hの一意の単語を作成することです。







 #ifndef MYLIB_H #define MYLIB_H //   #endif
      
      





つまり、スケッチには次のような複数の行がある場合があります。







 #include "MyLib.h"
      
      





「さようなら、はい私、はい私は続きます、はい私は...」と言うと、あなたは間違っているでしょう。 突然別のものに埋め込む場合やライブラリが別のものに含まれる場合など、既製のスケッチを修正するよりも、このデザインを1つのファイルに1回記述する方が適切です。 このコードは、MYLIB_Hという単語が定義されているかどうかを確認し、定義されていない場合はそれを判別し、さらにコードを含めます。 単語が既に定義されている場合、2回目はコードをコンパイルする必要はありません。







以下の重要なコード:







 #if ARDUINO >= 100 #include <Arduino.h> #else #include <WProgram.h> #endif
      
      





Arduino UDEランタイムからの定義が含まれます。 これがないと、ライブラリをコンパイルできません。







それだけです Arduino IDEを閉じて、再度開きます。 新しいスケッチを作成し、そこに#include "MyLib.h"と書いて歓声を上げてください。あなたのライブラリは接続されています!







私は見ました、ライブラリで、それはいくつのファイルがあるべきであるように思われますか?



はい、もちろんです。 ライブラリを作成するための最小限の手順を実行しました。 今こそ計画の時です。







コピーペーストでコードのチャンクをここに配置できるように、SmartButtonライブラリに名前を付けます。 MyLibブランクは不必要に打ち負かされます。







前の段落と同様に、その中にSmartButtonフォルダーを作成します。









ライブラリフォルダー内のファイルの場所







SmartButton.h







 #ifndef SMART_BUTTON_H #define SMART_BUTTON_H #if ARDUINO >= 100 #include <Arduino.h> #else #include <WProgram.h> #endif //    include    #ifndef SmartButton_debounce #define SmartButton_debounce 10 #endif #ifndef SmartButton_hold #define SmartButton_hold 1000 #endif #ifndef SmartButton_long #define SmartButton_long 5000 #endif #ifndef SmartButton_idle #define SmartButton_idle 10000 #endif class SmartButton { //    . //        //     . //        . private: byte btPin; // ,     [   ](https://habrahabr.ru/post/345960/) enum state {Idle, PreClick, Click, Hold, LongHold, ForcedIdle}; enum input {Press, Release, WaitDebounce, WaitHold, WaitLongHold, WaitIdle}; enum state btState = Idle; enum input btInput = Release; unsigned long pressTimeStamp; //   ,    . private: void DoAction(enum input in); //  ,   . public: //   . //   ,     . SmartButton(); SmartButton(int pin); SmartButton(int pin, int mode) {btPin=pin; pinMode(pin,mode);} ~SmartButton(); //   Arduino IDE   begin void begin(int p, int m) {btPin=p; pinMode(p,m);} //      loop(). void run(); //    . public: inline virtual void onClick() {}; // On click. inline virtual void onHold() {}; // On hold. inline virtual void onLongHold() {}; // On long hold. inline virtual void onIdle() {}; // On timeout with too long key pressing. inline virtual void offClick() {}; // On depress after click. inline virtual void offHold() {}; // On depress after hold. inline virtual void offLongHold() {}; // On depress after long hold. inline virtual void offIdle() {}; // On depress after too long key pressing. }; #endif
      
      





ベンチャーの本質を説明しましょう。 ボタンから何が必要かはわかりません。 MCAは、クリック、プレス、ホールド、長すぎるホールドの状態になり、これらの状態からオフ状態に移行できます。 ライブラリを汎用化しているため、別のプログラマーがコードを状態ハンドラーに挿入する機会を提供する必要があります。 OOPには、このためのすばらしいツール、継承があります。







いくつかのメソッド(関数)を持つクラスを作成しますが、それらは空です。 つまり、正しいタイミングで呼び出されますが、コードはありません。 これはなぜですか? 次に、スケッチでは、私たちのクラスに基づいて独自のクラスを作成し、そこに必要なメソッドのみを定義して、それらをコードで埋めることができます。







たとえば、トグルボタンを作成します。つまり、1回押すとオンになり、もう1回押すとオフになります。 LEDのオンとオフを切り替え、isOn()関数を提供して、loop()関数の古典的な形式で使用します。







 #include "SmartButton.h" #define LED_PIN (13) //      SmartButton class Toggle : public SmartButton { private: byte sw = 0; //   byte led; //    public: Toggle(byte bt_pin, byte led_pin) : SmartButton(bt_pin) { // . led=led_pin; }; //   //    . byte isOn() { return sw; } //    . virtual void onClick() { if (sw) { //  . . digitalWrite(led,LOW); //         . } else { //  . . digitalWrite(led,HIGH); //         . } sw=!sw; //  . } }; //   bt   .   . Toggle bt(4,LED_PIN); //  4,  . Toggle drill(12,8) //  12,    8. void loop() { bt.run(); drill.run(); if (bt.isOn()) { // -  } else { // -   } if (drill.isOn()) { // -  } else { // -   } }
      
      





ご覧のとおり、前の記事のICAボタンはここではまったく興味がありません。このボタンにはコードがなく、非表示になっています。 基本クラスに機能を追加し、クリックスルースイッチを作成しました。 また、新しいToggleクラスはライブラリとして設計することもできます。または、スケッチの隣に別のToggle.hファイルを配置し、#includeディレクティブで接続するだけです。 また、ボタンを点灯するようにLEDで足を設定します。 新しいToggleクラスの2つのオブジェクト(btとdrill)を作成したばかりであり、MCAを処理するボタンは非表示であり、気にしないことに注意してください。







SmartButtonクラスに基づいて、メニューを介してカーソルを移動したり、マシンガンの砲塔をゆっくりと回すなど、ダブルクリックすることで、ボタンが保持されている時間に応じて、理解できる独自のクラスを作成できます。 これを行うには、SmartButton.hに記述されているメソッドを仮想として定義するだけで十分です。 すべてを決定する必要はなく、必要なものだけを決定する必要があります。







ターゲットオーディエンスの要求に応じて、メソッドを提供するPressButtonクラスの例を次に示します。









 #include "SmartButton.h" #define LED_PIN (13) //      SmartButton class PressButton : public SmartButton { private: byte sw = 0; //   public: PressButton(byte bt_pin) : SmartButton(bt_pin) {}; // . //   //     . byte pressed() { return sw; }; //   ,   . void ok() { sw=0; }; //    . virtual void onClick() { sw=1; }; }; //   bt   .   . PressButton bt(4); //  4. PressButton drill(12) //  12. void loop() { bt.run(); drill.run(); if (bt.pressed()) { // -  bt.ok(); } else { // -   } if (drill.pressed()) { // -  if (__) drill.ok(); } else { // -   } }
      
      





したがって、2つの独立して機能する「スティッキー」ボタンを取得します。これらのボタンは、押すとok()メソッドでリセットされるまで押された状態になります。







メニューがある場合は、上下のボタンのonClick()メソッドを定義して、メニューカーソルを対応する方向にディスプレイ内で移動させることができます。 それらに対してonHold()を定義すると、たとえばカーソルがメニューの最初と最後に移動する可能性があります。 Enterボタンで、メニュー選択としてonClick()、保存を伴う終了としてonHold()、保存を伴わない終了としてonLongHold()を定義できます。







ダブルクリックが必要な場合は、クリックカウンターと前回のクリックからの時間を取得できるようにonClickを定義します。 その後、シングルクリックとダブルクリックを区別できます。







SmartButtonは単なるMCAであり、ボタンの動作を実装するためのツールです。







すべての魔法はどこに隠されていますか? 魔法はSmartButton.cppファイルにあります







 #include "SmartButton.h" //    . SmartButton::SmartButton() {} SmartButton::~SmartButton() {} //   . //    . SmartButton::SmartButton(int pin) { btPin = pin; pinMode(pin, INPUT_PULLUP); } //     : //   -     , //     [ ](https://habrahabr.ru/post/345960/). //       on*  off*. void SmartButton::DoAction(enum input in) { enum state st=btState; switch (in) { case Release: btState=Idle; switch (st) { case Click: offClick(); break; case Hold: offHold(); break; case LongHold: offLongHold(); break; case ForcedIdle: onIdle(); break; } break; case WaitDebounce: switch (st) { case PreClick: btState=Click; onClick(); break; } break; case WaitHold: switch (st) { case Click: btState=Hold; onHold(); break; } break; case WaitLongHold: switch (st) { case Hold: btState=LongHold; onLongHold(); break; } break; case WaitIdle: switch (st) { case LongHold: btState=ForcedIdle; break; } break; case Press: switch (st) { case Idle: pressTimeStamp=millis(); btState=PreClick; break; } break; } } //     . //     loop() void SmartButton::run() { unsigned long mls = millis(); if (!digitalRead(btPin)) DoAction(Press); else DoAction(Release); if (mls - pressTimeStamp > SmartButton_debounce) DoAction(WaitDebounce); if (mls - pressTimeStamp > SmartButton_hold) DoAction(WaitHold); if (mls - pressTimeStamp > SmartButton_long) DoAction(WaitLongHold); if (mls - pressTimeStamp > SmartButton_idle) DoAction(WaitIdle); }
      
      





ロジックはいくつかの場所で議論の余地がある、私は知っている:)しかし、それは動作します。







ライブラリの説明をREADMEファイルに記入し、同様に、library.jsonファイルに記入します。このフィールドは非常に明白です。







 { "name": "SmartButton", "keywords": "button, abstract class, oop", "description": "The SmartButton abstract class for using custom buttons in Arduino sketches.", "repository": { "type": "git", "url": "https://github.com/nw-wind/SmartButton" }, "version": "1.0.0", "authors": { "name": "Sergei Keler", "url": "https://github.com/nw-wind" }, "frameworks": "arduino", "platforms": "*" }
      
      





リポジトリがない場合は、このセクションを省略できます。







やった! ライブラリの準備ができました。 フォルダーを圧縮して友人に配布したり、他のコンピューターにコピーしたりできます。







類推により、MCAのクラスを作成できます。 原則は一般的です。クラスを作成し、仮想メソッドを定義します。そして、普遍性が必要ない場合は、コードまたは既製のメソッドを挿入するために再定義する必要があります。







Githubとは何ですか、なぜ必要なのですか?



Githubはプログラマーの巨大なコミュニティです。 はい、あなたのコードはインターネット全体に公開されますが、誰でもあなたのコードに編集を提供できます。 たとえば、2人がSmartDelayで私を大いに助けてくれました。1人は自分で同様のライブラリを作成し、お互いに少しのコードを見ました。 2つの優れたライブラリは、2つのバグのあるライブラリよりも優れています。







ライブラリをGithubに配置するには、そこにアカウントを作成し、キーを生成して、ライブラリ(フォルダー)と同じ名前のリポジトリを作成する必要があります。 ファイルはWebインターフェースからダウンロードできます。







ライブラリをGithubからArduino IDEにインストールするには、URLをコピーしてgitユーティリティを使用するだけです:













または、ZIPをダウンロードします。これは、他のすべてのライブラリと同様、Arduinoライブラリになります。







一般的にgitを、特にGithubを使用する方法については、確かに多くの記事があります。 検索してみてください。 見つからない場合は、使用方法を書きます。








All Articles