eine Funktion mehrfach verwenden - Wie?

Hallo,

ich möchte eine Funktion mehrfach nutzen, ist ja Sinn einer Funktion, um verschiedene LEDs mit unterschiedlichen an/aus Zeiten blinken zu lassen.

die Funktion sieht so aus

void LED_Flash()        // LED blinken lassen 
{
  static boolean state_LED = LOW;
  static unsigned long millis_LED = 0;
                 
     if (state_LED == LOW && millis() > millis_LED )  {
       digitalWrite(Flash_LED, HIGH);        // LED einschalten für
       millis_LED = millis() + 50;           // 50ms
       state_LED = HIGH;
     }
     if (state_LED == HIGH && millis() > millis_LED )  {
       digitalWrite(Flash_LED, LOW);         // LED ausschalten für
       millis_LED = millis() + 2000;         // 2 sec. 
       state_LED = LOW;
     }   
}

Mir ist klar, dass ich für die allgemeine Nutzung die beiden Zeiten, derzeit 50ms und 2000ms, und die LED Pin Nummer durch Variablen ersetzen und dafür Werte beim Aufruf übergeben muß.
Nur was passiert mit den beiden lokal definierten Variablen?
Erstellt der Compiler für jeden unterschiedlichen Funktionsaufruf neue lokale Variablen?
Sonst bringen mehrere Aufrufe alles durcheinander, habe ich die Befürchtung.

ich glaube nicht, dass das so einfach ist, das sind ja keine losgelösten, selbständigen Threads in einem Betriebssystem, das parallele Threads zulässt.

Die static Variable gibt es immer nur einmal.

Sowas ist eine Aufgabe für einen Functor, bzw. ein Function Object, d.h. den () Operator einer Klasse überladen. Dann kann man verschiedene Objekte erzeugen und diese wie Funktionen verwenden, aber jedes Objekt hat seinen eigenen Zustand:

Hallo,

upps, dass liest sich ja wie eine Library programmieren. Mist. Ich merk mir das mal vor, hab dafür nicht den Kopf frei.
Aber Danke für den Link.

Eine Library ist meistens auch nichts anderes eine Klasse.

Ein Functor ist eine Mini-Klasse. Der Sinn einer Klasse ist ja dass jedes Objekt sein eigenes Set an Variablen hat (es sei denn man will Klassen-Variablen, d.h. statische Variablen, dann teilen sich alle Objekte eine Variable). Dann wird nur der () Operator überladen, damit man das Objekt von der Syntax her wie eine Funktion verwenden kann. Ist eigentlich ganz simpel.

Da ich nicht weiß, was Du machen willst, wofür Dein Kopf frei ist, hier die funktionierende Anfängervariante:

void loop() {
  LED_Flash(links, 50);
  LED_Flash(rechts, 2000);
}

void LED_Flash(byte Flash_LED, unsigned int verzoegerung)        // LED blinken lassen
{
  static boolean state_LED;
  static unsigned long millis_LED;
  static boolean state_LEDlinks = LOW;
  static unsigned long millis_LEDlinks = 0;
  static boolean state_LEDrechts = LOW;
  static unsigned long millis_LEDrechts = 0;
  if (Flash_LED == links) {
    state_LED = state_LEDlinks;
    millis_LED = millis_LEDlinks;
  }
  if (Flash_LED == rechts) {
    state_LED = state_LEDrechts;
    millis_LED = millis_LEDrechts;
  }
  if (state_LED == LOW && millis() > millis_LED )  {
    digitalWrite(Flash_LED, HIGH);        // LED einschalten für
    millis_LED = millis() + verzoegerung;
    state_LED = HIGH;
  }
  if (state_LED == HIGH && millis() > millis_LED )  {
    digitalWrite(Flash_LED, LOW);         // LED ausschalten für
    millis_LED = millis() + verzoegerung;
    state_LED = LOW;
  }
  if (Flash_LED == links) {
    state_LEDlinks = state_LED;
    millis_LEDlinks = millis_LED;
  }
  if (Flash_LED == rechts) {
    state_LEDrechts = state_LED;
    millis_LEDrechts = millis_LED;
  }
}

ich möchte eine Funktion mehrfach nutzen, ist ja Sinn einer Funktion, um verschiedene LEDs mit unterschiedlichen an/aus Zeiten blinken zu lassen.

Dann packe alle nötigen Daten in eine Struktur. Nein, in 2 gleichartige Strukturen.
Und rufe die Funktion mit jeweils einer der beiden, also 2 mal, auf.

Und dann wirst du merken, dass du auch die Funktionen mit in die Struktur packen kannst.

struct Blinker
{
  unsigned long lastHit;
  unsigned long interval;
  int outPin;
  bool zustand;
  Blinker(int outPin,unsigned long interval) : outPin(outPin),interval(interval)
  {
    pinMode(outPin,OUTPUT);
    lastHit = millis();
    zustand = true;
    digitalWrite(outPin,zustand);
  }
  void update()
  {
    if(millis()-lastHit >interval)
    {
      zustand = !zustand;
      digitalWrite(outPin,zustand);
      lastHit = millis();
    }
  }
};

Blinker A(12,1000);
Blinker B(13,100);


void setup()
{
}

void loop() 
{
    A.update();
    B.update();
}

ungetestet

Oh...
Das ist ja schon Objekt orientiertes Programmieren....
Also fix das Wort struct durch class ersetzen, und fertig ist eine Lib im Adruino Stil.

1 Like

combie:
Dann packe alle nötigen Daten in eine Struktur. Nein, in 2 gleichartige Strukturen.
Und rufe die Funktion mit jeweils einer der beiden, also 2 mal, auf.

Und wenn man dann die Funktion durch das überladen des () Operators ersetzt hat man einen Functor :slight_smile: Was anderes ist das nicht. Operatoren überladen ist schließlich nur auch nur eine Funktionsdeklaration.

Dann kann man das machen:

LedFunctor led1(50);
LedFunctor led2(100);

void loop()
{
  led1();
  led2();
}

Wie ein ganz normales Objekt, nur dass es aussieht wie ein Funktions-Aufruf.

Statt das:

LedClass led1(50);
LedClass led2(100);

void loop()
{
  led1.flash();
  led2.flash();
}
1 Like

Hallo,

@ agmue:
nett gemeint, nüchtern betrachtet hat man damit nur beide Einzelfunktionen für jede LED in eine Funktion gepackt. Oder?

Librarys sind nachwievor ein rotes Tuch für mich. Mit dem Wirrwar von Operatoren und (.) "Verbindern" ähnlich dem struct.

Da es nicht viel Code ist und sich glaube ich nicht viel nimmt, habe ich erstmal eine weitere Einzelfunktion geschrieben.
Ich bin zur Zeit dabei mein Projekt zu Ende zu bringen. Sprich Leiterplatte erstellen, Preise einholen usw.
Mein Mega2560 verkraftet noch etwas Code und Variablen im RAM. Zum Glück.
Sind die letzten Feinheiten im Code, ehrlich gesagt Spielerei, jetzt ist damit dafür erstmal Schluss, sonst werde ich nie fertig.

Wenn's fertig ist, komme ich auf das hier zurück. Hoffe ich. :wink:

Edit: die letzten Antworten, verstehe wirklich derzeit nur Bahnhof

Doc_Arduino:
Edit: die letzten Antworten, verstehe wirklich derzeit nur Bahnhof

Zu meiner Antwort habe ich noch Quellcode hinzugefügt, probiere den mal aus,
ist auch recht leicht zu verstehen (hoffe ich).
Ansonsten FRAGE, ich will dir das gerne erklären.

Hallo,

okay, ja, überredet, guck ich mir an, aber nicht mehr heute ... sorry

Mit 2 LEDs grob getestet:

class Led
{
private:
  byte pin_LED;
  unsigned int on_time;
  unsigned int off_time;
  bool state_LED;
  unsigned long millis_LED;

public:
  Led(byte pin, unsigned int on, unsigned int off) : pin_LED(pin), on_time(on), off_time(off), state_LED(0), millis_LED(0)
  { 
    pinMode(pin_LED, OUTPUT);
  }

  void operator()(void)
  { 
    if (state_LED == LOW && millis() > millis_LED + off_time)  
    {
      digitalWrite(pin_LED, HIGH);
      millis_LED = millis();
      state_LED = HIGH;
    }
    if (state_LED == HIGH && millis() > millis_LED + on_time) 
    {
      digitalWrite(pin_LED, LOW);
      millis_LED = millis();
      state_LED = LOW;
    }
  }
};


Led led1(13, 300, 1000);
Led led2(8, 1000, 500);

void setup()
{
}

void loop()
{
  led1();
  led2();
}
1 Like

Doc_Arduino:
@ agmue:
nett gemeint, nüchtern betrachtet hat man damit nur beide Einzelfunktionen für jede LED in eine Funktion gepackt. Oder?

Ein "schönes" Programm sieht natürlich anders aus, aber ich bin Pragmatiker und meine LEDs blinken schon seit geraumer Zeit :smiley:
Danke für die Frage, die Antworten sehen interessant aus.

Hallo,

@ agmue: wir wollen sicherlich beide etwas lernen, los gehts ... :wink:

@ Serenifly: sei mir nicht böse wenn ich mit combies Code Gerüst anfagen, erscheint mir (noch) einfacher, wenn ich das gerafft habe kommt Dein Code dran :wink:

Ich denke mal laut zum mitmeißeln. Habs umgebaut mit 2 Zeiten für an/aus damit ich auch verstehen lerne was zusammengehören muß. Funktionieren tut es, sieht mit den Zeiten aus wie ein kurzes Schieberegister.

ganz oben im struct werden lokale Variablen definiert.
Danach wird in Zeile 9 eine Funktion oder Objekt ??? erzeugt mit diversen Parametern ?
Was macht das ":" Zeichen? Trennung was definiert wird?
Das erscheint mir aber noch doppelt gemoppelt. Müssen die Datentypen gleich sein? Eigentlich ja, sonst paßt das nicht eineinander, nur dann könnte man sich eine Datentypsangabe sparen?

Das grün eingerahmte sind Startwerte wie sonst im setup? Werden einmalig abgearbeitet, wenn das Objekt in Zeile 34 erzeugt wird?

In loop wird letztlich das Objekt mit der Update Funktion verbunden und ständig aufgerufen. Hierbei muß der Compiler selbstständig neue unsichtbare Variablen erzeugen, sonst kommen die LED Zeiten durcheinander.

Sind die Gedankengänge so richtig? Oder muß das korrigiert werden?

struct Blinker
{
  unsigned long millis_LED;
  unsigned int on_time;
  unsigned int off_time;
  byte pin_LED;
  boolean state_LED;
  
  Blinker(byte pin, unsigned int on, unsigned int off) : pin_LED(pin), on_time(on), off_time(off)  
  {
    pinMode(pin_LED, OUTPUT);
    millis_LED = millis();
    state_LED = false;
    digitalWrite(pin_LED, state_LED);
  }
  
  void update()
  {
    if (state_LED == LOW && millis() > millis_LED )  
    {
      digitalWrite(pin_LED, HIGH);       // LED einschalten für
      millis_LED = millis() + on_time;   // ms
      state_LED = HIGH;
    }
    if (state_LED == HIGH && millis() > millis_LED ) 
    {
      digitalWrite(pin_LED, LOW);         // LED ausschalten für
      millis_LED = millis() + off_time;   // ms
      state_LED = LOW;
    }
  }
};

Blinker A(23,2000,2000);  // Pin, onTime, offTime
Blinker B(25,1000,1000);
Blinker C(27, 500, 500);


void setup()
{
}

void loop() 
{
    A.update();
    B.update();
    C.update();
}

sei mir nicht böse wenn ich mit combies Code Gerüst anfagen, erscheint mir (noch) einfacher, wenn ich das gerafft habe kommt Dein Code dran

Das ist im Prinzip genau das gleiche nur mit etwas syntaktischem Zucker

Was macht das ":" Zeichen? Trennung was definiert wird?

Das ist eine Initialisierungsliste:

https://cpp.nope.bz/initliste.html

Der Sinn der Sache ist, dass die Zuweisungen ausgeführt werden noch bevor das eigentliche Objekt erzeugt wurde. Wenn man in den Klammern des Konstruktors ist (wo man auch sowas machen kann), wurde das Objekt schon erzeugt.

Das ist hier egal. Man könnte die Zuweisungen auch im Konstruktor-Körper selbst machen, aber besser man gewöhnt es sich gleich so an. In anderen Fällen geht das nämlich nicht.

Und doppelt ist da nichts. Der Konstruktor Blinker(...) wird ausgeführt wenn das Objekt erstellt wird. Dann werden wie bei einer Funktion Parameter übergeben und diese den Variablen des Objekts zugewiesen.

Hallo,
okay, soweit erstmal klar, im Moment :wink: mal sehen wie lange.
wegen doppelt gemoppelt.
ich habe die Zeilen rausgenommen worauf ich mich beziehe.

pin, on, off wird definiert im Code. Mit sowas hier. Blinker A (23, 2000, 2000)

jetzt wird doch nichts weiter gemacht, als die 23 in die pin_LED Variable und die 2000 in die on_time und off_time Variable "geschrieben".
pin ist byte und pin_LED ist byte. Gehört doch aber sowieso zusammen.
Wenn jetzt on_time long wäre und on wäre nur byte, paßt das doch nicht rein. Verstehst Du mein Gedankenproblem besser?

  unsigned int on_time;
  unsigned int off_time;
  byte pin_LED;
    
  Blinker(byte pin, unsigned int on, unsigned int off) : pin_LED(pin), on_time(on), off_time(off)

und hier hast Du demnach syntaktisches Salz geschrieben, oder? :slight_smile:

state_LED(0), millis_LED(0)

Wenn jetzt on_time long wäre und on wäre nur byte, paßt das doch nicht rein. Verstehst Du mein Gedankenproblem besser?

Das ist doch nichts neues. Integer-Datentypen sind zuweisungskompatibel. Wenn du einem long ein Byte zuweist geht das natürlich. In ein Byte passen halt nur kleine Zahlen rein. Du kannst auch einem Byte einen long zuweisen. Das wird halt zur Laufzeit schief gehen, da dann Bits verloren gehen.

und hier hast Du demnach syntaktisches Salz geschrieben, oder?

Nein. Der Begriff ist für bestimmte Syntax-Konstrukte gedacht. Nicht so sehr dafür wie man legitime Schreibweisen verwendet.

Das explizit auf 0 zu initialisieren wäre nicht unbedingt nötig, aber es schadet auch nichts und macht das deutlicher.

Hallo,

naja, ich dachte eben, wenn die Datentypen sowieso gleich sein müssen, damit nichts verloren geht, warum muß man die dann 2x angeben. Ich merke mir darauf zu achten die zusammengehörenden Datentypen gleich sein müssen, sonst gehts schief.

Ganz schön viel Stoff. Bei deinem Code mit public und private ist das quasi das gleiche nur sieht anders aus. Ist aber einer Library vom Schreibstil her viel näher. ?

Die werden nicht zweimal angeben. Das sind alles unterschiedliche Dinge

Einmal wird die Variable deklariert. Dann übergibt man einen Parameter um Konstruktor. Und in der Liste weist man diesen Wert der deklarierten Variable zu.

Bei deinem Code mit public und private ist das quasi das gleiche nur sieht anders aus. Ist aber einer Library vom Schreibstil her viel näher. ?

Hätte man auch so mit einem struct machen können. Combie hat ein struct verwendet. Ich habe class verwendet. In C++ ist da der einzige Unterschied, dass die Member eines structs standardmäßig public sind und die einer Klasse private.

Private bedeutet, dass die Member nur innerhalb der Klasse sichtbar sind, aber nicht nach außen. public Member sind auch nach außen sichtbar. Das will man bei internen Variablen eigentlich nicht. In diesem Fall will man nur die Funktion und den Konstruktor verwenden, also sind diese public.

Wenn man einen Konstruktor private macht kann man kein Objekt der Klasse erstellen. Dafür gibt es auch Anwendungen, z.B. Factory Klassen bei bestimmten Singleton Implementierungen

Hallo,

aha, ich glaube ich hab das erstmal soweit begriffen, hoffe ich.

Jetzt bin ich gerade mutig und baue das gelernte noch in mein Projekt ein. Dazu muß ich aber noch wissen, was der aktuelle Status der LED ist. Ob an oder aus. Von außen darauf zugegriffen wird dann über mittels

if ( A.state_LED == HIGH ) .... ?

struct Blinker
{
  unsigned long millis_LED;
  unsigned int on_time;
  unsigned int off_time;
  byte pin_LED;
  boolean state_LED;
  
  Blinker(byte pin, unsigned int on, unsigned int off) : pin_LED(pin), on_time(on), off_time(off)  
  {
    pinMode(pin_LED, OUTPUT);
    millis_LED = millis();
    state_LED = false;
    digitalWrite(pin_LED, state_LED);
  }
  
  void update()
  {
    if (state_LED == LOW && millis() > millis_LED )  
    {
      digitalWrite(pin_LED, HIGH);       // LED einschalten für
      millis_LED = millis() + on_time;   // ms
      state_LED = HIGH;
    }
    if (state_LED == HIGH && millis() > millis_LED ) 
    {
      digitalWrite(pin_LED, LOW);         // LED ausschalten für
      millis_LED = millis() + off_time;   // ms
      state_LED = LOW;
    }
  }
};

Blinker A(23,2000,2000);  // Pin, onTime, offTime

void setup()
{
}

void loop() 
{
    A.update();
}