Mein erster OOP versuch

Moin liebe gemeinde,
jetzt springe ich auch mal auf den Zug des Ardoino auf.
Nachdem ich einige kleine Sketche erfolgreich umgesetzt habe, wollte ich mal an OOP versuchen.
Die einzelnen Klassen, die ich baue möchte ich später in eine Libery schreiben.
jetzt erst mal zu "meiner" Timer-Klasse.


```cpp
 class TTimer
{		unsigned long period;
    unsigned long currentTime;
    unsigned long startTime;
    bool t_activ = false;

	public:
    typedef void (*t_Action)();

    void activ(bool val){                                // timer aktivieren / deaktivieren
      t_activ = val;
      if (t_activ){
        startTime = millis();
      }
    }

    void OnTimer(t_Action func){                         // Funktion übergeben die aufgerufen werden soll
      this->func = func;
    }

    void SetTimer(unsigned long t_msec){                // Intervall des Timer's
      period = t_msec;
    }

    void tic(){
      currentTime = millis();
      if (t_activ){
        if  (currentTime - startTime >= period) {
          t_Action();                                   // hier Function aufrufen
        }
      }
    }

  private:
    t_Action func;

};


bool toggle;
TTimer myTimer;


void setup() {
  pinMode(7, OUTPUT); // put your setup code here, to run once:
  myTimer.SetTimer(400);
  myTimer.OnTimer(Blinken);
  myTimer.activ(true); 
}

void loop() {
  myTimer.tic();
}


void Blinken(){
  toggle = !toggle;
  digitalWrite(7, toggle);
}

1. Kann mir jemand sagen, wo mein denkfehler liegt? 
2. gibt es eine möglichkeit, das sich eine Klasse nach dem Konstrukt immer wieder aufruft?

Liebe Grüße Jörg
und danke schon mal im vorraus...

was denkst du denn?
oder besser:
Was macht dein Sketch, was soll er statt dessen machen?

jein. Grundsätzlich musst du deinen tick aufrufen. Es gäbe aber ein paar Tricks dass das jemand anderer für dich macht.
Würde ich an deiner Stelle aber vorerst nicht umsetzen.
Das aufrufen einer .tick, .run, .update Member Function im loop ist durchaus üblich.

sorry, ich dachte das wäre offensichtlich...
die Klasse TTimer soll nach jedem ablauf von 400ms (SetTimer)
die Funktion Blinken (OnTimer) aufrufen wenn TTimer (activ=true) ist

Gruß Jörg

jetzt hast du das soll beschrieben was du dir wünscht.
Was macht es statt dessen?

nichts

zum zweiten, dann muss ich für jeden Konstrukt auch den jeweiligen tic aufrufen. Das ist aber nicht der Sinn in einer OOP...

Schalte mal die Warnungen ein.

läuft, muss aber noch zusammenräumen.

edit ... soweit mal

/*
   erste Gehversuche mit OOP
   https://forum.arduino.cc/t/mein-erster-oop-versuch/1235082
   https://wokwi.com/projects/392258781793396737

   2024-03-13 läuffähig gemacht von noiasca
   code im Thread
*/

class TTimer {  
  private:
    using CallBack = void (*)(void);           // nimm using statt den typdef
    CallBack func;
    unsigned long period;
    unsigned long currentTime;
    unsigned long startTime;
    bool t_activ = false;

  public:

    void activ(bool val) {                               // timer aktivieren / deaktivieren
      t_activ = val;
      if (t_activ) {
        startTime = millis();
      }
    }

    void onTimer(CallBack func) {                        // Funktion übergeben die aufgerufen werden soll
      this->func = func;
    }

    void setTimer(unsigned long t_msec) {               // Intervall des Timer's
      period = t_msec;
    }

    void tic() {
      currentTime = millis();
      if (t_activ) {
        if  (currentTime - startTime >= period) {
          startTime = currentTime;                      // das hat gefehlt funktional
          if (func) func();                             // das würde ich absichern, falls du den callback nicht übergeben hättest
        }
      }
    }
};


bool toggle;
TTimer myTimer;

constexpr uint8_t outputPin = 13;

void setup() {
  Serial.begin(115200);
  pinMode(outputPin, OUTPUT); // put your setup code here, to run once:
  myTimer.setTimer(400);
  myTimer.onTimer(blinken);
  myTimer.activ(true);
}

void loop() {
  myTimer.tic();
}

void blinken() {
  Serial.print(".");  // nur debug check obs aufgerufen wird
  toggle = !toggle;
  toggle ? digitalWrite(outputPin, LOW) : digitalWrite(outputPin, HIGH) ;
}

Es werden keine Warnungen angezeigt

Danke, das hätte ich auch mal testen können.
habe das hier nur auf einem UNO R3 live getestet

super, besten dank!
jetzt kann ich die Klasse optimieren und in eine zukünftige Libery packen.

Schreibt wer?

Du gibst jedem Objekt (Instanz) einen exakten Zeitpunkt zur Durchführung seiner Tätigkeiten.
In der Arduino Welt ist der Loop deine zentrale Ablaufsteuerung.
Also rufst du darin auch die member functions so auf wie du sie benötigst.
Und die tick/run/update brauchst halt einmal pro Durchlauf.

Du kannst dich auch einlesen zu Factory - bedenke aber, du bist da teilweise auf kleinen 8bittern unterwegs.
Oder bei @MicroBahner s Moba Tools Library reinschauen - da werden auch Objekte angetriggert.

Aber ich denke es gibt vorher noch genug andere Themen beim Erlernen von OOP.

Übrigens: Groß Kleinschreibung: member functions durchgängig mit Kleinbuchstaben beginnen lassen. So wie globale Funktionen auch.

Dann hast Du nicht alle eingeschaltet:

... \sketchbook\_ArduinoCC\ducky_42-sketch_mar13b\ducky_42-sketch_mar13b.ino:29:21: warning: statement has no effect [-Wunused-value]
           t_Action();                                   // hier Function aufrufen

Dann hättest Du gesehen, dass das mit deinem Funktionsaufruf so nicht geht - Du hast den Typ angegeben statt des Funktionspointers.

Ich finde es auch sehr unübersichtlich eine interne Variable genauso zu benennen wie einen Methodenparameter. Und die verbreitete 'Sitte', die Namen von private Variable mit einem '_' zu beginnen finde ich auch sehr übersichtlich.

Danke!
Dann werde ich mal weiter lesen.
Ich komme von Pascal (Delphi) da gibt es doch große Unterschiede!
Zumal Arduino "nur" einen Task zur verfügung hat.
Nochmals danke

Übrigens: Mein Versuch eines einfachen Timers findest du in dieser Lib:

https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm

Okay, da muß ich nochmal schauen!
Danke für die Info

noch eine letzte Anmerkung:

wenns dann soweit läuft halte dich an dieses Paper:

und folge auch den Deep-Links darin.

@noiasca war schneller...

Aber da ich jetzt auch schon aufgeräumt habe, poste ich meine kleinen Änderungen hier auch mal:

class TTimer {
  unsigned long period;
  unsigned long startTime;
  bool t_activ = false;

public:
  using t_Action = void (*)();

  void activ(bool val) {   // timer aktivieren / deaktivieren
    t_activ = val;
    if (t_activ) { startTime = millis(); }
  }

  void OnTimer(t_Action func_) {   // Funktion übergeben die aufgerufen werden soll
    func = func_;
  }

  void SetTimer(unsigned long t_msec) {   // Intervall des Timer's
    period = t_msec;
  }

  void tic() {
    if (t_activ) {
      if (millis() - startTime >= period) {
        startTime = millis();
        func();   // hier Function aufrufen
      }
    }
  }

private:
  t_Action func;
};

bool toggle;
TTimer myTimer;

void setup() {
  pinMode(7, OUTPUT);   // put your setup code here, to run once:
  myTimer.SetTimer(400);
  myTimer.OnTimer(Blinken);
  myTimer.activ(true);
}

void loop() { myTimer.tic(); }

void Blinken() {
  digitalWrite(7, !digitalRead(7));
}

Zumindest blinkt es jetzt ...

"typedef" würde ich durch "using" ersetzen (ist mehr c++ style :wink: ), im ursprünglichen Code war der Funktionsaufruf verkehrt, currentTime war überflüssig, in tic() hat "startTime = millis();" gefehlt.

Im Prinzip war das der einzige Fehler, der das Aufrufen der Funktion verhindert hat.
Die von @noiasca ergänzte Zeile, um den Timer immer wieder neu 'aufzuziehen':

und seine weiteren Tips und Ergänzungen solltest Du dir auf jeden Fall auch zu Herzen nehmen :sunglasses:

P.S. die Warnungen einzuschalten ist immer eine gute Idee. Viele Warnungen sind eigentlich Fehler. Leider ist das bei der IDE kein Standard ( Vielleicht weil die internen Programme leider auch oft Warnungen erzeugen :roll_eyes: ).

Auch hier nochmal vielen Dank!
startTime wird bei activ=true gesetzt.
Alles andere korrigiere ich grade.