Stap 5: Het toevoegen van knoppen, en gaan van OO
Zoals we in de vorige stap zagen, wordt het steeds moeilijk omhoog aan schaal. Er zijn wereldwijde status variabelen, die door sommige functies worden genoemd. Stel je een 2de knop toevoegen: de nachtmerrie begint; niet spreken over een 3e knop...
Programmering: Gaan OO (Object-georiënteerd)
Arduino maakt gebruik van de Processing language gebouwd op de top van C++, dus waarom niet gebruiken sommige OO-functies?
We moeten voor de verpakking van deze globale variabelen in structuren die we naar believen kunt maken. Deze structuren, met bijbehorende handling functies heten klassen en methoden in de OO-wereld. Een klasse is slechts de beschrijving van het object, terwijl de effectief geheugen toegewezen voor elk object het exemplaar heet.
De eerste foto van deze stap toont dat we een 2de knop hebt toegevoegd. De tweede foto toont de inmiddels traditionele lang/kort persen, werken onafhankelijk van elkaar zeer mooi. De 2e knop heeft, door de manier, een langere long-pers-drempel.
Pro/Cons
Deze code wordt steeds complexer, dat is normaal, omdat tijdens het schalen, de complexiteit door een belangrijke stap verhoogt. Het goede nieuws is dat het dan een willekeurig aantal extra knoppen op geen verhoging van de complexiteit kan ondersteunen.
Verwerking ingesteld sommige beperkingen (er is bijvoorbeeld geen dynamische creatie: de nieuwe en verwijderen actoren zijn niet beschikbaar). Die is niet slecht, omdat wij ook niet wilt verdwalen in sommige van de met name bloederige zijden van C++, die in kleine ingesloten systemen zoals Arduino (aantoonbaar) overbodig zijn.
Zeker hetzelfde kan worden gedaan in zuivere C, dus indien nodig / een C-implementatie gewenst, gewoon het mij vraagt.
Code
––––––––––8<––––––––––
#define BUTTON1_PIN 2 // Button 1 #define BUTTON2_PIN 3 // Button 2 #define DEFAULT_LONGPRESS_LEN 25 // Min nr of loops for a long press #define DELAY 20 // Delay per loop in ms ////////////////////////////////////////////////////////////////////////////// enum { EV_NONE=0, EV_SHORTPRESS, EV_LONGPRESS }; ////////////////////////////////////////////////////////////////////////////// // Class definition class ButtonHandler { public: // Constructor ButtonHandler(int pin, int longpress_len=DEFAULT_LONGPRESS_LEN); // Initialization done after construction, to permit static instances void init(); // Handler, to be called in the loop() int handle(); protected: boolean was_pressed; // previous state int pressed_counter; // press running duration const int pin; // pin to which button is connected const int longpress_len; // longpress duration }; ButtonHandler::ButtonHandler(int p, int lp) : pin(p), longpress_len(lp) { } void ButtonHandler::init() { pinMode(pin, INPUT); digitalWrite(pin, HIGH); // pull-up was_pressed = false; pressed_counter = 0; } int ButtonHandler::handle() { int event; int now_pressed = !digitalRead(pin); if (!now_pressed && was_pressed) { // handle release event if (pressed_counter < longpress_len) event = EV_SHORTPRESS; else event = EV_LONGPRESS; } else event = EV_NONE; // update press running duration if (now_pressed) ++pressed_counter; else pressed_counter = 0; // remember state, and we're done was_pressed = now_pressed; return event; } ////////////////////////////////////////////////////////////////////////////// // Instantiate button objects ButtonHandler button1(BUTTON1_PIN); ButtonHandler button2(BUTTON2_PIN, DEFAULT_LONGPRESS_LEN*2); void setup() { Serial.begin(9600); // init buttons pins; I suppose it's best to do here button1.init(); button2.init(); } void print_event(const char* button_name, int event) { if (event) Serial.print(button_name); Serial.print(".SL"[event]); } void loop() { // handle button int event1 = button1.handle(); int event2 = button2.handle(); // do other things print_event("1", event1); print_event("2", event2); // add newline sometimes static int counter = 0; if ((++counter & 0x1f) == 0) Serial.println(); delay(DELAY); }
––––––––––>8––––––––––