Problem mit Millis als Ersatz für Delay und/oder while-Schleife

Hi zusammen,

Vorweg: versucht es bitte auf dem Sanften Wege :wink:

Ich bin seit Dienstag Stolzer Besitzer (und sogar Eigentümer) eines Arduino Starter Kits.
Jetzt sitze ich zur Freude meiner Frau fast 24/7 an meinem Schreibtisch und frickel vor mich hin.

Erfahrung in C? nope!
Ich hab 1983 auf der Volkshochschule mal nen BASIC-Kurs belegt... ist aber schon was her...

Mein Ziel: Motorisierung/Automatisierung
Für den Anfang hab ich mir da was leichtes vorgenommen: Kameraslider mit Pan/Tilt und Timelaps-Steuerung. hüstel :cold_sweat:

OK, vorerst sollte erst mal die Fernauslösung funktionieren :smiley:

Jetzt mein Problem:
Nach viel lesen und Stöbern im Forum bin ich imho schon ganz gut voran gekommen, habe aber jetzt das typische while-Problem: Ausstieg aus der Schleife bei Knopfdruck.

Bitte verlinkt jetzt nicht auf die Millis-Funktion; soweit bin ich schon, aber irgendwie will er nicht...
Während der "Millis-Schleife" hält er die eingestellte Pause (int pause) nicht ein und er unterbricht auch nicht sofort bei Knopfdruck (digitalRead (2)).

Also, was soll passieren?

  • Zuerst über Poti einen Wert zwischen 1-30 Sekunden eingeben (int pause)
  • dann button drücken
  • nach Ablauf des 5-sekündigen Countdowns
  • erste Aufnahme, dann (int pause) - zweite Aufnahme ... usw

währen dieser Pause möchte ich jetzt gerne durch Knopfdruck aus der while-Schleife raus... aber alles was ich bisher probiert habe funktioniert nicht:
while, delay, millis, if/else... jetzt bin ich mit meinem Latein am Ende. Wo liegt mein Denkfehler?

Vorab schon mal vielen Dank für die Hilfe!

hier mal mein sketch: (ja, geht bestimmt einfacher/besser/intelligenter, aber bedenkt, ich arbeite mich erst seit 3 Tagen in das Thema ein :wink: )

/*
*************************************************************
*************************************************************
                TIMELAPS FOR CANON EOS 600D           
                       V0.5 2017-08-17                          
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

 int poti = A2;
 int pause;
 int LED = 3;
 int ausloeser = 4;
 int cdown;
 int Anz;
 int xit;
 unsigned long Mil;
 unsigned long DelayTime;
 
// Belegung für 16x2 LCD-Modul
 LiquidCrystal_I2C  lcd(0x3F,2,1,0,4,5,6,7,3,POSITIVE);

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
  pinMode(poti, INPUT);
  pinMode (ausloeser, OUTPUT);
  
  lcd.begin(16,2);          // ### Begruessungsbildschirm ###
  lcd.setCursor(2,0);
  lcd.print("Timelaps 0.5");
  lcd.setCursor(1,1);
  lcd.print("2017 by HAGGIE");
  
  digitalWrite(ausloeser, LOW);
}

void loop()
{
  delay(2000);                         //2 Sekunden Pause
 
  lcd.clear();                         //Display löschen und Cursor nach oben links
  lcd.home();

// *** Intervall Einstellen bis Start gedrueckt wird *** 
  while(digitalRead(2)==LOW)  
{
      pause = analogRead(poti);          // Wert vom Poti einlesen
      pause = map(pause, 0, 1023, 0, 30);  // Poti den Wert 0-30 zuweisen
      
  if (pause < 1)
    {
      lcd.setCursor(0,0);               // ### DISPLAY ###
      lcd.print("off        ");         // Anzeige "off" bei 0 Sekunden-Timer
     }
  else 
  {
    lcd.setCursor(0,0);                 // ### DISPLAY ###
    lcd.print("alle ");
    lcd.print(pause);                     // Anzeige der eingestellten Sekunden
    lcd.print(" Sek.      ");
  }
 
 }
    
      lcd.clear();                      //Display loeschen und Cursor nach oben links
      lcd.home();

// *** 5 Sekunden Countdown bis zum Start der Aufnahme ***
   cdown = 5;
   while(cdown>0)
    {
      lcd.setCursor(0,0);                  // ### DISPLAY ###
      lcd.print("Aufnahme startet");
      lcd.setCursor(0,1);
      lcd.print("   in ");
      lcd.print(cdown);
      lcd.print(" Sek.      ");
      digitalWrite(LED, HIGH);
      delay(150);   
      digitalWrite(LED, LOW);
      delay(850);

      cdown = cdown - 1;
    }

    lcd.clear();                      // Display loeschen und Cursor nach oben links
    lcd.home();

// Anzeige während Aufnahme laeuft und Kontroll-LED blinkt
     Anz = 0;                     // Zähler Anzahl Bilder auf 0
     xit = 0;
     while(xit == 0)       // Serienaufnahme bis Stop gedrueckt wird
  { 
    Anz = Anz + 1;                    // Zähler Anzahl Bilder +1
   
    lcd.setCursor(0,0);               // ### DISPLAY ###
    lcd.print("Aufn. ");
    lcd.print(pause);                   // Anzeige Intervall
    lcd.print(" Sek.      ");
    lcd.setCursor(0,1);
    lcd.print("Anz.Bilder: ");
    lcd.print(Anz);                   // Anzeige Anzahl Bilder
    
    digitalWrite(LED, HIGH);          // Kontroll-LED an
    digitalWrite(ausloeser, HIGH);    // Fernausloeser fuer 1,5 sek. triggern
    delay(1500);                      
    digitalWrite(ausloeser, LOW);     // Fernausloeser auf 0
    digitalWrite(LED, LOW);           // Kontroll-LED aus

      Mil = millis();                 // #####################################################
      DelayTime = Mil + (pause*1000); // AB HIER: nicht funktionierender while-Ersatz
                                      // mit Ausstiegsmöglichkeit während der "delay-zeit"
        while (Mil < DelayTime)       //                       |
        {                             //                       |
          if (digitalRead(2)==LOW)    //                       |
          {                           //                       |
            Mil = Mil + 1;            //                       |
          }                           //                       |
          if (digitalRead(2)==HIGH)   //                       |
          {                           //                       |
          Mil = DelayTime;            //                       |
          xit = 1;                    //                       | 
          }                           //                       V
        }                             // bis hierhin läuft's nicht
  }                                   // ######################################################
    
    lcd.clear();                      // Display loeschen und Cursor nach oben links
    lcd.home();

// Anzeige nach Aufnahme 
    lcd.setCursor(0,0);               // ### DISPLAY ###
    lcd.print("Aufnahme beendet");
    lcd.setCursor(0,1);
    lcd.print(Anz);
    lcd.print(" Bilder");             // Anzeige Summe Bilder
    delay(5000);
    while (digitalRead(2)==LOW)      // Anzeige bis erneut Stop gedrueckt wird <<-----
   { 
    lcd.setCursor(0,0);               // ### DISPLAY ###
    lcd.print("Aufnahme beendet");
    lcd.setCursor(0,1);
    lcd.print(Anz);
    lcd.print(" Bilder");             // Anzeige Summe Bilder
    }
    lcd.clear();                      // Display loeschen und Cursor nach oben links
    lcd.home();
}

Ich hoffe Du hast Pullupwiderstände an den Taste.

digitalWrite(ausloeser, LOW);

mach das gleich nach dem pinMode und nicht nach dem Begrüßungstext auf dem Display.

void loop()
{
  delay(2000);

Die loop() Schleife wird nur alle 2 sekunden durchgeführt. Dadurch kannst Du Einstellungen wie die Zeit nur alle 2 Sekunden machen.
delay muß weg.

  pinMode(poti, INPUT);

braucht es nicht.

Da müssen alle delays und whiles weg und die Steuerung auf einen endlichen Automaten umgestellt werden.
Du mußt einen Modi-variable einführen die die verschiedenen Möglichkeiten ermöglicht.
Du unterschedest mit if anfragen oder Switch-case.
Modi = 1; Einstellung Delaytime
Modi = 2; Pause vor auslösen
Modi = 3; Auslösen
Modi = 4; Delayzeit zwischen 2 auslösen
ecc

Die Zeiten jedes Modi werden mittels millis() gesteuert.
https://forum.arduino.cc/index.php?topic=423688.0
So kann ein Abbruch jederzeit erfolgen.

Grüße Uwe

Grrrr... jetzt hatte ich Dir eine ausführliche Antwort geschrieben, und der Server hats weggeschmissen >:( >:(
Nochmal in Kurzform:
Der Kommentar von Uwe ist der richtige Weg für die Zukunft und ein ausbaubares Programmkonzept. Dein jetziges Programm ist für die einfachen Serienaufnahmen machbar, aber eine nicht ausbaufähige Sackgasse.
Zur whileschleife: die macht letztendlich gar keine Pause, sondern ist in wenigen millisekunden durchgelaufen, da Du in der Schleife keine Zeitfunktionen nutzt, sondern einfach

        Mil = Mil + 1;            //                       |

setzt. Dazu braucht der Arduino kaum ein µSec.
Du musst also innerhalb der Schleife mit den millis() arbeiten:Mil = millis();Dann wird Mil im Millisekundenraster hochgezählt.

Hi ihr zwei beiden, vielen Dank für die Antworten!
Das gibt mir jetzt erst mal wieder Futter für 1-2 Tage ;D
Zum endlichen Automaten hatte ich gestern schon was gefunden, wusste aber nicht, dass ich es gebrauchen könnte...
Also nochmal ein wenig wühlen und lesen. Auch noch mal den Wachmann (auch den hatte ich schon :confused: )

Ja, Uwe, ich glaube, dass ich ausreichend Widerstand leiste :wink:
Ist aber ein pulldown-Widerstand...?

Hier mal der Aufbau, am Ausgang zur Kamera sitzt ein Optokoppler (LTV 827):

pinMode Poti braucht es nicht, weil's ein analoger Eingang ist?

OK, ich fürchte es war doch ein wenig viel Input für mein altes Hirn, ich dachte, ich hätte das mit dem millis richtig verstanden.
Puha... dann mache ich mich nochmal an die Arbeit, aber genau das wollte ich ja.
Diese kleine Platine ist schon ein gar lustig Teilchen... wenn ich da mal so an meinen C64 zurückdenke :slight_smile:

Also nochmal Danke!! (ja, Ausrufezeichen sind Rudeltiere)
Ich werde bei gegebener Zeit erneut berichten (d.h. fragen :grin: )

Grüße
Marcus

Tante Edit sagt:

Ihr Genies! Es läuft :smiley:
Ich habe jetzt unten nur

Mil = millis();                 
      DelayTime = Mil + (pause*1000); 
                                      
        while (Mil < DelayTime)       
        {                             
          if (digitalRead(2)==LOW)    
          {                           
            Mil = millis();           
          }                           
          if (digitalRead(2)==HIGH)   
          {                           
          Mil = DelayTime;            
          xit = 1;                    
          }                           
        }

Mil=Mil+1 in Mil=millis() geändert.

Danke für den Denkanstoß!
Dann werde ich mich jetzt mal um den Nachtwächter und die Modi kümmern .

DelayTime = Mil + (pause*1000);

while (Mil < DelayTime)

Diese Konstruktion fällt dir pünkltlich nach 49,xx Tagen Dauerlauf auf die Füße.

Der Nachtwächter und BlinkWithoutDelay zeigen dir, wie man es besser macht.

Ich glaube kaum, dass diese Version jemals 49 Tage am Stück laufen wird :wink: .

Und für sein zukünftiges Gesamtprojekt muss er eh nochmal von vorne anfangen. Aber auch das wird dann von der Anwendung her eher kein Dauerläufer-Sketch.

Man muss sich der Problematik allerdings schon bewusst sein, ja. Insofern ist der Hinweis schon richtig.
Aber wenn ich genau weis, dass mein Sketch nie mehr als ein paar Stunden am Stück läuft ( z.B. eben nur während der Arbeit damit ) interessiert mich dieses Problem auch nicht. Wenn ich nur mal ein paar Stunden spazieren gehe, nehm ich ja auch nicht Verpflegung für 14 Tage mit :wink: .... Ok, unverhofft kommt oft, und so soll es schon vorgekommen sein, dass das im Einzelfall besser gewesen wäre :smiley: :smiley:

Da muss ich euch beiden zustimmen :slight_smile: (ziemlich diplomatisch)

Danke für den Hinweis mit dem Überlauf, ich war mir dessen, bis ich gestern im Forum drüber gestolpert bin, tatsächlich nicht bewusst.

Aber wie MicroBahner sagt, das Ding wird niemals 49 Tage durchlaufen.
Zumindest nicht, bis jemand nen bezahlbaren Kamera-Akku erfunden hat, der so lange hält - nein, keine LKW-Batterie :smiley:

Und ja, das Ding ist jetzt erst einmal, um mich überhaupt mit dem Arduino und C++ vertraut zu machen.
Wenn es jetzt an die Motorsteuerung geht, fange ich eh von vorne an.

Außerdem macht es keinen Sinn die Pausen von 0-30 in 1-Sek.-Schritten einzustellen.

  1. brauche ich längere Pausenzeiten (muss ich auch mal meinem Chef sagen), bis zu 15 Minuten
  2. müssen die dann nicht in Sekundenschritten einstellbar sein.

Also nächster Schritt: die einzelnen Belichtungszeiten in einen "Container" packen und mit jedem Schritt auslesen lassen.
Aber ich meine, auch dazu schon was im Forum/YouTube gesehen zu haben :smiley:

Danke nochmal für die Hilfe!

Grüße
Marcus

Haggie:
Ja, Uwe, ich glaube, dass ich ausreichend Widerstand leiste :wink:
Ist aber ein pulldown-Widerstand...?

Du kannst soviel Widerstand leisten wie Du willst udn Du verträgst. Ansonsten darfst Du fragen. :wink: :wink: :wink:
Taster brauchen einen Widerstand damit sie im unbetätigten zustand einen klaren Spannungspegel an den Eingang liefern. Dieser Pegel wird durch einen Widerstand erreicht der zwischen Eingang und Masse (pulldown - runterziehen) oder zwischen Eingang und + Versorgungsspannung (pullup - hinaufziehen) geschaltet ist. Im ersten Fall ist der Taster zwischen Eingang und + Versorgungsspannung geschaltet. Wird der Taster betätigt liest der Eingang HIGH. Im zweiten Fall ist der Taster zwischen Eingang und Masse und liest LOW wenn betätigt.
Der Arduino hat interne Pullupwiderstände die per Funktion eingeschaltet bzw ausgeschaltet werden können.

Grüße Uwe