Wie zeitliche Abläufe ohne delay() einfach gestalten?

Hallo,
ich bin grad am programmieren meines Arduino Uno für eine Kameraschiene.

Jedoch bin ich grad auf das Problem gestoßen, dass ich die zeitlichen Abläufe nicht über die delay()-Funktion machen kann, da beispielsweise so keine Tasteneingaben mehr möglich sind.

Mein Ablauf schaut wie folgt aus:

  • Taster schaltet die Funktion ein
  • Ausgang A (Motor) wird für eine bestimmte Zeit 1 eingeschaltet
  • Nach Ablauf dieser Zeit 1, wird eine Verzögerung Zeit 2 aktiviert
  • Nach Ablauf von Zeit 2 wird Ausgang B (DSLR) für eine bestimmte Zeit 3 aktiviert
  • Nach Ablauf von Zeit 3 folgt eine Verzögerung von Zeit 4 und das Programm wiederholt sich

Ich bin Arduino-Anfänger und habe zunächst die delay()-Funktion als Verzögerung eingesetzt. Da jedoch immer ein delay aktiviert ist, kann ich keine Eingaben über meine Taster machen.

Wie kann ich am einfachsten die o.g. Funktion realisieren? Ich hatte mir schon die BlinkWithoutDelay und ein paar timer-Libraries angeschaut. Jedoch zwar nichts dabei, was bei mehreren Timern einigermaßen übersichtig bzw. einfach aussau.

Am besten vergegenwärtigt man sich das Verhalten des Controllers mit einem Endlichen Zustandsautomaten (Endlicher Automat – Wikipedia). Bedingungen für Zustandswechsel sind dann nicht nur externe Ereignisse, sondern könnten dann auch abgelaufene Zeitperioden sein. Beispiel: Ausgangszustand sei A. Nach 5 Sekunden soll Zustand B eingenommen werden: Bei betreten von Zustand A berechnet man die neue Zielzeit aus der aktuellen Zeit plus 5 Sekunden. Wenn die Zielzeit erreicht ist, wird der Zustand auf B gewechselt.

Grüße,
Oliver

Schau Dir mal das Beispiel http://arduino.cc/en/Tutorial/BlinkWithoutDelay an
Grüße Uwe

Das BlinkWithoutDelay-Beispiel hatte ich mir schon angeschaut.

Ich als absoluter Programmier-Anfänger, weiß aber nicht, wie ich z.B. unterschiedliche Zeiten für das Ein/Aus einstellen kann. Das Beispiel schaltet ja die LED 1 Sekunde aus, und 1 Sekunde ein. Ich bräuchte aber beispielsweise 1 Sekunden ein und 5 Sekunden aus.

Außerdem verstehe ich nicht, wie ich mehrere dieser Timer aktiviere und benutze.

Für eine normales Ein/Aus mag das Beispiel zwar geeignet sein. Sobald aber mehrere Timer genutzt werden sollen, finde ich, dass das Beispiel viel zu wenig beschrieben ist.

Mach dir eine Time Mashine. Siehe hier, zweites Post von Oben von meiner wenigkeit.
http://arduino.cc/forum/index.php/topic,123929.0.html

Das BlinkWithoutDelay-Beispiel funktioniert folgendermaßen.
Die Funktion millis() gibt die Millisekunden zurück, die vergangen sind seitdem Arduino eingeschaltet oder resetiert wurde.

Du speicherst die Startzeit ab un kontrolliest durch subtrahieren der Startzeit von der jetztzeit ob die Wartezeit schon vorbei ist.
Wenn Du mehrere solche Abläufe kontrollieren willst ( 2 LED unterschiedlich blinken) dann verwende mehrere variablen für die Startzeiten.

Zu Deinem Problem: Ich bräuchte aber beispielsweise 1 Sekunden ein und 5 Sekunden aus. Ändere das http://arduino.cc/en/Tutorial/BlinkWithoutDelay BEispiel folgendermaßen:

intervallon = 1000;
intervalloff = 5000;

...

void loop()
{
    unsigned long currentMillis = millis();
 
  if(ledState == HIGH && currentMillis - previousMillis > intervalon ) {
     previousMillis = currentMillis;  
     ledState = LOW;
     digitalWrite(ledPin, ledState);
  }
   if(ledState == LOW && currentMillis - previousMillis > intervaloff) {
     previousMillis = currentMillis;  
     ledState = HIGH;
     digitalWrite(ledPin, ledState);
  }
}

Grüße Uwe

... und außerdem brauchst du wohl eine Variable, die dir sagt in welchem Schritt du grade bist.

int Schritt;
unsigned long Dauer; 
unsigned long previousMillis;
...

void loop () 
{
    unsigned long currentMillis = millis();

   ...
   if (  currentMillis  - previousMillis > Dauer )  
   {
      Schritt++;  // nächster Schritt
      switch (Schritt) 
           {
                 case 1:
                   // Motor Ein
                    Dauer = 5000; // sec warten
                    break;
      
                case 2:
                   // Motor aus
                    Dauer = 1000; // Pause zeit 2
                    break;

              // ....  Beliebig weitere Schritte definieren

              default:
                   Schritt = 0;
                   Dauer = 1;     // Schrittfolge wieder von vorn
           }
          previousMillis = currentMillis; // "Delay" neu starten
   }

   // hier evtl. Tasterabfragen, um die Schrittfolge zu ändern oder Wartezeiten abzubrechen o.ä.

}

Edit: hoppla, Code Tag vergessen ; )

michael_x:
... und außerdem brauchst du wohl eine Variable, die dir sagt in welchem Schritt du grade bist.

Die hatte ich ja:
ledState

Grüße Uwe

Die hatte ich ja:
ledState

Schon richtig, aber das Problem mit "Blink without Delay" ist, dass die Leute sagen: prima, aber an/aus Blinken reicht mir nicht...
Und eine "State-Machine" ist auch richtig, aber abschreckend ...

@Michael: Vielen Dank, dein Code ist genau das was ich gesucht habe, funktioniert genauso wie ich es wollte.

Jedoch habe ich da noch eine Frage:
Die Funktion soll nur dann ablaufen, wenn der Einschalter auf 1 steht und der Endlageschalter auf 0. Sobald sich einer dieser Werte ändert, soll der Ablauf gestoppt werden.

Mein Ansatz mit der if-Bedingung:

 if (endlageValue == 0 && einaus == 1)
{
...
}

Wenn ich den Endschalter betätigt (=1) und Einschalter(einaus=1), passiert nichts. Wenn ich den Endschalter loslasse, startet die Funktion. Also genau so wie ich es will. Wenn jedoch die Funktion abläuft und ich den Endschalter oder Einschalter betätige, dann läuft die Funktion immer noch weiter bzw. wird am momentanen Schritt gestoppt und der Schritt läuft im Dauermodus.

Was muss ich ändern, damit die Funktion beim Ändern der Bedingungen sich resettet bzw. stoppt?

Hofdiener, bringe er mir die Kristallkugel. :wink: :wink:
BlueGene bitte den gesamten modifizierten Sketch.
Grüße Uwe

Das Posten der Definition der Variablen und des setup kann ich verzichten, oder ist das auch von Bedeutung?

void loop()
{
  unsigned long currentMillis = millis();

 if (modus == 1 && einaus == 1)
  {
    if (endlageValue==0)
    {
      if (currentMillis  - previousMillis > Dauer )  
      {
        Schritt++;  // nächster Schritt
        switch (Schritt) 
        {
        case 1:
          analogWrite(motoreinaus, 255);// Motor - Leistung 100%
          digitalWrite(rechtslauf, LOW);// Motor - Rechtslauf aus
          digitalWrite(linkslauf, HIGH);// Motor - Linkslauf ein
          digitalWrite(led4, LOW);
          Dauer = 1000; // sec warten
          break;

        case 2:
          analogWrite(motoreinaus, 127);// Motor - Leistung 50%
          digitalWrite(led2, HIGH); 
          Dauer =1000; // Pause zeit 2
          break;


        case 3:
          digitalWrite(led2, LOW);
          digitalWrite(led3, HIGH);
          analogWrite(ein, 255); // Motor - Leistung 100%
          Dauer = 1000;
          break;

        case 4:
          digitalWrite(led3, LOW);
          digitalWrite(linkslauf, LOW);// Motor - Linkslauf aus
          digitalWrite(motoreinaus, LOW);// Motor Aus
          digitalWrite(led4, HIGH);
          Dauer=1000;
          break;


        default:
          Schritt = 0;
          Dauer = 1;     // Schrittfolge wieder von vorn
        }
        previousMillis = currentMillis; // "Delay" neu starten
      }  //  if (currentMillis  - previousMillis > Dauer )  
    }   // if (endlageValue==0)
  } //   if (modus == 1)
} // loop-Ende

Wenn ... ich den Endschalter ... betätige, dann läuft die Funktion immer noch weiter

ja klar, das hast du so programmiert:

...
   if (endlageValue==0)
    {
         ....
    }   // if (endlageValue==0)
  } //   if (modus == 1)
} // loop-Ende

-- Was soll er machen, wenn der Endlage-Schalter erreicht ist ?
-- Und wo würdest du das einbauen ?

2 Fragen, die du selbst beantworten musst, bzw. können solltest .

Falls es dir zur 2. Frage hilft:
if (bedingung) { ... } gibt es auch als if (bedingng) { ... } else { ... }

Danke nochmal, jetzt funktioniert endgültig wie es soll (hoff ich mal^^)