[erledigt][S] Programmier-Strategie(n) f. nichtblockierende Schleife(n)

Hallo zusammen!

Nachdem ich mich eine Zeit lang gedanklich mit den Vor- und Nachteilen nichtblockierender Schleifen auseinandergesetzt habe, würde ich gerne mal lesen, was sich andere dazu ausgedacht haben. Vielleicht hat ja schon jemand vor mir das Rad erfunden :slight_smile:

Was mir momentan als „beste“ Strategie dazu im Kopf schwebt: loop() wird sozusagen ungebremst durchlaufen. Ob eine Funktion ihre Arbeit tun soll, entscheidet ein Flag, das anzeigt, ob eine Funktion ihren Job machen soll (oder nicht, weil z. B. seit dem letzten Mal noch keine Sekunde vergangen ist).
So kann jede Funktion „selbst entscheiden“, ob (und wie oft) sie aktiv werden soll.

Wahrscheinlich gibt es noch ein halbes Dutzend anderer „Strategien“, die ihre besonderen Vor- und Nachteile haben. Daher: Links zu Lektüre und sonstige Kommentare sind hochwillkommen!

Gruß

Gregor

Hallo Gregor,

ähnliche Gedanken hatte ich auch schon gehabt. Tat ich mich anfangs schwer mit den „Millis“ & Co - habe ich heute ein Konzept, mittels dem ich ohne jegliche wilde Blockierung meine Programme erstelle. Exakt nach deiner Idee mit den Flags, die über „Gedeih und Verderb“ einer Funktion entscheiden, gehe ich ebenso vor.

Damit ich mich selbst brutal dazu zwinge, Programme absolut frei von Blockierungen zu schreiben, füge ich am Anfang einer loop() generell einen kleinen Code-Schnipsel ein, der eine verschüchterte kleine LED im Sekundentakt blinken läßt. So in etwa als „Herzschlag“ oder „Betriebsanzeige“. Auf diese Weise kontrolliere ich meine Programme (und mich selbst), ob irgendwo ein Hemmschuh im Wege liegt...

Passende Links oder Lektüre kann ich leider keine liefern. Alles dazu ist bei mir (fast) zu 100% auf meinem eigenem Mist gewachsen.

Dennoch beste Grüße
Rudi

gregorss:
Was mir momentan als „beste“ Strategie dazu im Kopf schwebt: loop() wird sozusagen ungebremst durchlaufen. Ob eine Funktion ihre Arbeit tun soll, entscheidet ein Flag, das anzeigt, ob eine Funktion ihren Job machen soll (oder nicht, weil z. B. seit dem letzten Mal noch keine Sekunde vergangen ist).
So kann jede Funktion „selbst entscheiden“, ob (und wie oft) sie aktiv werden soll.

Ja, genau so. Solange nichts zu tun ist, läuft die loop() Funktion weitgehend leer durch. Und wenn etwas zu tun ist, wird etwas getan.

Und ob etwas zu tun ist, hängt eben von verschiedenen Bedingungen ab.

Z.B. vom Vorhandensein eines Zeichens im seriellen Eingangspuffer

if (Serial.available()) ...

vom Betätigen eines Schalters:

if (Buttonpressed()) ...

bei zyklisch auszuführenden Aktionen vom Zeitablauf

if (millis()-lastCycleStartTime>= cycleDuration) ...

vom Verbinden eines clients mit einem Arduino-Webserver

if (client.connected())  ...

Der Trick daran ist nur, dass die Funktionsaufrufe, die zum Prüfen der Bedingung, ob etwas zu tun ist oder nicht, sehr schnell ablaufen müssen, also möglichst in wenigen Mikrosekunden oder noch weniger. Dann kann ein Arduino hunderte oder tausende von Dingen quasi "zur selben Zeit" am Laufen haben, und erlegigt wird jeweils das, was gerade zu erledigen ist.

Hallo Rudi+Jurs,

vielen Dank für Eure Kommentare! Jetzt weiß ich, dass meine Ideen zu dieser Sache in die richtige Richtung gehen.

Gruß

Gregor

Wahrscheinlich gibt es noch ein halbes Dutzend anderer „Strategien", die ihre besonderen Vor- und Nachteile haben.

Hier mal ein Beispiel, wie ich sowas "angehe".

Ziele sind:

  1. möglichst weit kapseln
  2. Setup() und Loop() schlank halten
  3. Wiederverwendung von Code, möglichst ohne "copy and past"

In diesem Beispiel sind an den 6 Analog Pins, jeweils 1 Taster gegen GND angeschlossen.
Welche 3 unabhängige PWM Dimmer steuern:

class Handler
{
  protected:
  static Handler* first;
  Handler* next = NULL;
  Handler()
  {
    if(first) next = first;
    first = this;
  }
  
  virtual void update() = 0;
  
  public:
  static void handle()
  {
      Handler* temp = first;
      while(temp)
      {
        temp->update();
        temp = temp->next;
      }
  }
};

Handler* Handler::first = NULL;

class Dim2Taster : public Handler
{
  protected:
  const byte upPin;
  const byte downPin;
  const byte pwmPin;
  int pwm;
  int dimDelay;
  unsigned long lastHit;
  void update()
  {
     if(millis() - lastHit > dimDelay)
     {
       int newPwm = pwm;
       if(!digitalRead(upPin))
       {
         newPwm++;
       }else if(!digitalRead(downPin))
       {
         newPwm--;
       }
       newPwm = constrain(newPwm,0,255);
       if(pwm != newPwm)
       {
         analogWrite(pwmPin,pwm=newPwm);
         lastHit = millis();
      //   Serial.print(pwmPin);
      //   Serial.print(" ");
      //   Serial.println(pwm);
       }
    }
  }

  
  public:
  Dim2Taster(int upPin, int downPin, int pwmPin, int dimDelay = 20) :upPin(upPin), downPin(downPin), pwmPin(pwmPin), pwm(0), dimDelay(dimDelay)
  {
    pinMode(upPin,INPUT_PULLUP);
    pinMode(downPin,INPUT_PULLUP);
    analogWrite(pwmPin,pwm=0);
    lastHit = millis();
  }
};



Dim2Taster dimmer[] = { Dim2Taster(A0,A1, 9),
                        Dim2Taster(A2,A3,10),
                        Dim2Taster(A4,A5,11),
                      };



void setup() 
{
  // Serial.begin(9600);
}

void loop() 
{
  Handler::handle();
}