Schleife blockiert, trotz millis

Hallo liebe Arduino-Gemeinde,

ich bastele schon lange mit dem Arduino und gerade durch die vielen Anregungen hier aus dem Forum konnte ich schon viele nette Projekte umsetzen.

Einige dieser Projekte laufen mit Verzögerungen basierend auf der "millis"-Abfrage.

Beim aktuellen Projekt fällt mir jedoch auf, dass trotz Nutzung von "millis" der Ablauf verzögert wird.

Ich hoffe ihr könnt mir helfen.

Bei dem Projekt soll der Arduino jeweils eine LED ansteuern und die über die Analogeingänge angeschlossenen Fototransistoren auswerten.
Wird der eingestellte Schwellwert unterschritten soll der dazu gehörige Ausgang für eine definierte Zeit schalten.
Ziel ist es jeweils einen Gas- und einen Wasserzähler abzufragen, und eine S0-Signal zu erzeugen.

Danke für eure Hilfe.

/* Der Lesekopf soll in einem festgelegtem Intervall IR-Impulse
 *  senden und den entsprechenden Eingang auslesen.
 *  Sinkt der Eingangswert unter den Schwellwert, soll der zugehörige
 *  Ausgang für eine festgelegte Zeit auf 0 schalten.
 */

const int gasPin    = A5;
const int gasIRPin  = 4;
const int gasOUTPin = 6;
const int gasLEDPin = 2;

const int waterPin    = A3;
const int waterIRPin  = 5;
const int waterOUTPin = 7;
const int waterLEDPin = 3;

unsigned long letzteMillis  = 0;
unsigned long gasIRPuls     = 20;
unsigned long gasOUTPuls    = 1000;
unsigned long gasOUTPause   = 500;
unsigned long gasLED        = 1000;

unsigned long waterIRPuls   = 20;
unsigned long waterOUTPuls  = 1000;
unsigned long waterOUTPause = 500;
unsigned long waterLED      = 1000;

int gasValueOff   = 1023;
int gasValueOn    = 1023;
int gasTrigger    = 200;

int waterValueOff = 1023;
int waterValueOn  = 1023;
int waterTrigger  = 200;

void setup() {
  // put your setup code here, to run once:

  digitalWrite(gasPin, INPUT_PULLUP);
  pinMode(gasIRPin, OUTPUT);
  digitalWrite(gasIRPin, LOW);
  pinMode(gasOUTPin, OUTPUT);
  digitalWrite(gasOUTPin, HIGH);
  pinMode(gasLED, OUTPUT);
  digitalWrite(gasLED, LOW);

  digitalWrite(waterPin, INPUT_PULLUP);
  pinMode(waterIRPin, OUTPUT);
  digitalWrite(waterIRPin, LOW);
  pinMode(waterOUTPin, OUTPUT);
  digitalWrite(waterOUTPin, HIGH);
  pinMode(waterLED, OUTPUT);
  digitalWrite(waterLED, LOW);

  Serial.begin (9600);   
}

void timer_gasIR ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < gasIRPuls);
       {}    
       } 

void timer_gasPuls ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < gasOUTPuls);
       {}    
       } 

void timer_gasPause ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < gasOUTPause);
       {}    
       } 

void timer_gasLED ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < gasLED);
       {}    
       } 

       
void timer_waterIR ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < waterIRPuls);
       {}    
       } 

void timer_waterPuls ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < waterOUTPuls);
       {}    
       } 

void timer_waterPause ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < waterOUTPause);
       {}    
       } 

void timer_waterLED ()
{
  
unsigned long  letzteMillis = millis();

     while((millis() - letzteMillis) < waterLED);
       {}    
       } 

void GasLED ()
{
  digitalWrite(gasLEDPin, HIGH);

  timer_gasLED ();

  digitalWrite(gasLEDPin, LOW);
}

void WaterLED ()
{
  digitalWrite(waterLEDPin, HIGH);

  timer_waterLED ();

  digitalWrite(waterLEDPin, LOW);
}


void Gaszaehler ()
{

  digitalWrite(gasIRPin, HIGH);
  gasValueOff = analogRead(gasPin);

  timer_gasIR ();

  digitalWrite(gasIRPin, LOW);
  gasValueOn = analogRead(gasPin);

  if (gasValueOn < 300)        //  if (gasValueOn < (gasValueOff - gasTrigger))
  {
    digitalWrite(gasOUTPin, LOW);

    GasLED ();

    timer_gasPuls ();

    digitalWrite(gasOUTPin, HIGH);

    timer_gasPause ();

    Serial.print("gasIROn");
    Serial.println(gasValueOn); 
    Serial.print("gasIROff");
    Serial.println(gasValueOff); 
  } else {

    digitalWrite(gasOUTPin, HIGH);

    Serial.print("gasIROn");
    Serial.println(gasValueOn); 
    Serial.print("gasIROff");
    Serial.println(gasValueOff); 
  }

    timer_gasIR ();
    
}

void Wasserzaehler ()
{

  digitalWrite(waterIRPin, HIGH);
  waterValueOff = analogRead(waterPin);

  timer_waterIR ();

  digitalWrite(waterIRPin, LOW);
  waterValueOn = analogRead(waterPin);

  if (waterValueOn < 300)      //  if (waterValueOn < (waterValueOff - waterTrigger))
  {
    digitalWrite(waterOUTPin, LOW);

    WaterLED ();

    timer_waterPuls ();

    digitalWrite(waterOUTPin, HIGH);

    timer_waterPause ();

    Serial.print("waterIROn");
    Serial.println(waterValueOn); 
    Serial.print("waterIROff");
    Serial.println(waterValueOff); 
  } else {

    digitalWrite(waterOUTPin, HIGH);

    Serial.print("waterIROn");
    Serial.println(waterValueOn); 
    Serial.print("waterIROff");
    Serial.println(waterValueOff); 
  }

    timer_waterIR ();
    
}

void loop() {
  // put your main code here, to run repeatedly:

Gaszaehler ();

Wasserzaehler ();

}

Natürlich blockiert das!

Du wartest, bis eine Zeit abgelaufen ist.
Das macht delay() auch.
So einfach ersetzen kann man das nicht.

Die Änderung beginnt im Kopf.
Du musst lernen in Nebenläufigkeiten zu denken.
Suche mal nach der "Nachtwächter Erklärung"


digitalWrite(waterPin, INPUT_PULLUP);
pinMode(waterIRPin, OUTPUT);

Das findet man aber auch in keinem Handbuch!
Was beabsichtigst du denn damit?

Hallo Combie,

Danke für Deine schnelle Antwort.
Die "Nachtwächter-Erklärung" kenne ich und dachte ich habe Sie verstanden (war wohl ein Irrglaube).

Ich vermute ich muß wohl den Programmablauf grundlegend ändern.
Kannst Du mir vielleicht einen zusätzlichen kurzen Denkanstoß geben, was bei meinem Programm falsch läuft.

Mit "digitalWrite(waterPin, INPUT_PULLUP);" soll der Eingang mit Pullup-Widerstand definiert werden.
Und mit "pinMode(waterPin, OUTPUT);" soll der Pin als Ausgang definiert werden.

Gruß Oscar

Der Trick mit den Millis ist ja im Gegensatz zu delay (das ähnlich wie deine Schleifen
programmiert ist), das man den Inhalt der Schleife selber unter Kontrolle hat.
Du tust in deinen While-Schleifen nichts, also machst du das Gleiche wie delay:

Gruß
Ulli

Hallo Ulli,

Danke für den Tipp.
Ich werde den Code mal umprogrammieren.

Gruß Oscar

Mit "digitalWrite(waterPin, INPUT_PULLUP);" soll der Eingang mit Pullup-Widerstand definiert werden.

Möchtest du da nicht lieber pinMode() verwenden?

Wieso reagierst du nicht mit lesen der Doku, wenn ich sage: "Das findet man aber auch in keinem Handbuch!"
? ?


Ich vermute ich muß wohl den Programmablauf grundlegend ändern.

Genau!

Kannst Du mir vielleicht einen zusätzlichen kurzen Denkanstoß geben, was bei meinem Programm falsch läuft.

Deine Schleifen blockieren den restlichen Programmablauf.
Und das willst du offensichtlich nicht.

Ansonsten kann ich deinem Programm keine Logik entnehmen, ich sehe nicht wie es was tun soll.
Es ist mir zu verworren.

Delay ist wenn Du vorm Geschäft darauf wartest das es aufsperrt.
Mit millis schaust Du immer wieder mal vorbei ob das Geschäft offen ist; in der Zwischenzeit gehst Du Eis essen, Zeitung kaufen, mit dem Nachbarn reden, aufs Klo, schaust Dir die Auslagen an ecc.

zu

Mit "digitalWrite(waterPin, INPUT_PULLUP);" soll der Eingang mit Pullup-Widerstand definiert werden.
Und mit "pinMode(waterPin, OUTPUT);" soll der Pin als Ausgang definiert werden.

Du benutzt digitalWrite falsch.

Grüße Uwe

uwefed:
Delay ist wenn Du vorm Geschäft darauf wartest das es aufsperrt.
Mit millis schaust Du immer wieder mal vorbei ob das Geschäft offen ist; in der Zwischenzeit gehst Du Eis essen, Zeitung kaufen, mit dem Nachbarn reden, aufs Klo, schaust Dir die Auslagen an ecc.

Hey Uwe, diese Erklärung finde ich viel "abgefahrener" als die Nachtwächtererklärung.

Mit Millis richtig angewendet sorge ich dafür, dass der Teil, der für eine bestimmte Zeit nicht ausgeführt werden soll, übersprungen wird. Alles andere kann trotzdem ausgeführt werden. Ich stoppe also nichts, sondern ich überspringe nur einen Teil, weil der für eine bestimmte Zeit nicht ausgeführt werden soll.

Also:

void loop()
{

If (millis() - pausebeginn > Pausezeit)
{
Einmal Ausführung dieses Programmes;
Einmal Ausführung dieses Programmes;

pausebeginn = millis(); // Hier wird der neue Pausbeginn wieder aktuallisiert
}
Hier laufen alle anderen Aktivitäten des Programmes ohne Pause weiter;
Kloo, Eis essen, Zeitung kaufen, um das Beispiel oben zu benutzen. :slight_smile:
}
Hier ist loop Ende. Das Programm springt wieder nach oben auf void loop

Franz54:
Kloo, Eis essen, Zeitung kaufen, um das Beispiel oben zu benutzen. :slight_smile:

Zur besseren Übersicht solltest du hier besser Funktionen einsetzen.
Sonst sieht so es aus, als ob alles an einem Platz/Ort passiert. :wink:

void loop()
{
Kloo();
Eis essen();
Zeitung kaufen();
}

Ihr müßt da schon einen endlichen Automaten einbauen; ich kann nicht jedesmal bevor ich beim Geschäft vorbeischauen ein Eis essen, Zeitung kaufen usw. :wink: :wink: :wink: Da werde ich noch dick und habe jede Menge Zeitungen vom selben Tag zuhause.

Grüße Uwe

Ja, das Beispiel war so schön anschaulich, das wollte ich beibehalten :slight_smile:

uwefed:
Ihr müßt da schon einen endlichen Automaten einbauen; ich kann nicht jedesmal bevor ich beim Geschäft vorbeischauen ein Eis essen, Zeitung kaufen usw. :wink: :wink: :wink: Da werde ich noch dick und habe jede Menge Zeitungen vom selben Tag zuhause.

Wir bauen einfach ein "sportstudio();" ein, das hilft sicher. :wink: :wink:

Hallo an Alle,

ich komme gerade von der Arbeit/vom Training.
Ihr seid verdammt schnell mit euren Tipps, Klasse.

Einen Teil habe ich bereits geändert, insbesondere die Deklaration der Eingänge.
Keine Ahnung warum ich das nicht schon vorher gesehen habe, aber ich hatte bisher nur diese falsche Möglichkeit gefunden den Pullup-Widerstand zu deklarieren.

Der Fehler ist schon einmal behoben.

Das Programm habe ich weitestgehend verworfen und bin dabei es neu mit if-Abfragen zu programmieren.
Ein Teilbereich ist schon fast fertig, aber noch ungetestet.
Ich hoffe ich kann euch morgen eine funktionierende Version präsentieren.

Danke für eure Hilfe.

Gruß Oscar

Wenn man eine 1, HIGH, true, oder vermutlich eben auch INPUT_PULLUP mit digitalWrite auf einen Eingangspin schreibt, wird der interne Pullup aktiviert. So kenne ich das zumindest.

mit digitalWrite auf einen Eingangspin schreibt, wird der interne Pullup aktiviert.

Und dennoch ist und bleibt es ein Fehler.
Der Nächste, von dieser Sorte, ist nicht so gnädig.

Ich finde:
Eine solche semantische Verwirrung, sollte man an der Wurzel greifen und ausmerzen.
Nicht, dass sich da eine Gewöhnung einstellt.

Hallo an Alle,

ich habe mein Programm für's erste in den Müll gehauen und mich noch einmal mit den grundlegenden Dingen beschäftigt.

Mit "digitalWrite" den Pullup-Widerstand zu aktivieren ist natürlich Käse, keine Ahnung warum ich vorher immer nur auf die Methode gestossen bin.

Richtig wäre es mit pinMode(Pin, INPUT_PULLUP).

Da mein eigentliches Problem aber Blockaden im Programmablauf waren, habe ich noch einmal den Nachtwächter bei seiner Arbeit begleitet und das Programm dahingehend geändert.

Es sind noch keine Auswertungen von irgendwelchen Eingängen enthalten, da ich zunächst mein Verständnis prüfen wollte.

Basis ist der BlinkwithoutDelay-Sketch.

// Konstanten
const int ledPin1 =  2;
const int ledPin2 =  4;
const int ledPin3 =  3;
const int ledPin4 =  5;                                     // Nummer des LED-Pin

// Variablen
int ledState1 = LOW;                                        // ledState wird benutzt um die LED zu steuern
int ledState2 = LOW;
int ledState3 = LOW;
int ledState4 = LOW;

// Immer unsigned long-Variablen für Zeitoperationen nutzen
unsigned long previousMillis1 = 0;                          // speichert den Zeitpunkt der letzten Statusänderung --> Millis() zählen die Zeit vom Einschalten des Controllers
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
unsigned long previousMillis4 = 0;

// Konstanten
const long interval1 = 1000;                                // Blinkintervall in Millisekunden
const long interval2 = 400;
const long interval3 = 270;
const long interval4 = 100;

void setup() {
  
  pinMode(ledPin1, OUTPUT);                                 // definiert den ledPin als Ausgang
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
}

void loop()
{

  unsigned long currentMillis = millis();                  // übergibt den Wert von Millis() an die Variable currentMillis
                                                           // Da dies fortlaufend geschehen soll, wird die Variable im "Loop" definiert.
 
  if(currentMillis - previousMillis1 >= interval1) {       // überprüft, ob die Differenz zwischen der aktuellen Zeit und der Zeit der letzten Ausgangsänderung
                                                           // größer ist als das eingestellte Intervall
    
    previousMillis1 = currentMillis;                        // speichert den Umschaltzeitpunkt

    if (ledState1 == LOW)                                   // ist die LED aus, schalte sie ein, und anders herum
      ledState1 = HIGH;
    else
      ledState1 = LOW;

    digitalWrite(ledPin1, ledState1);                        // setzt den Ausgang mit der Variable ledState
  }

  if(currentMillis - previousMillis2 >= interval2) {       
                                                           
    
    previousMillis2 = currentMillis;                      

    if (ledState2 == LOW)                                  
      ledState2 = HIGH;
    else
      ledState2 = LOW;

    digitalWrite(ledPin2, ledState2);                      
  }


  if(currentMillis - previousMillis3 >= interval3) {       
                                                           
    
    previousMillis3 = currentMillis;                      

    if (ledState3 == LOW)                                  
      ledState3 = HIGH;
    else
      ledState3 = LOW;

    digitalWrite(ledPin3, ledState3);                      
  }

  if(currentMillis - previousMillis4 >= interval4) {       
                                                           
    
    previousMillis4 = currentMillis;                      

    if (ledState4 == LOW)                                  
      ledState4 = HIGH;
    else
      ledState4 = LOW;

    digitalWrite(ledPin4, ledState4);                      
  }

}

Das Programm tut jetzt was es soll.

Danke für eure Hilfe.

Noch ein paar Änderungsvorschläge:

// Variablen
bool ledState1 = LOW;                                        // ledState wird benutzt um die LED zu steuern
bool ledState2 = LOW;
bool ledState3 = LOW;
bool ledState4 = LOW;

Damit Du vorzeichenlose mit vorzeichenlosen Werten vergleichst:

// Konstanten
const unsigned long interval1 = 1000;                                // Blinkintervall in Millisekunden
const unsigned long interval2 = 400;
const unsigned long interval3 = 270;
const unsigned long interval4 = 100;
    if (ledState1 == LOW)                                   // ist die LED aus, schalte sie ein, und anders herum
      ledState1 = HIGH;
    else
      ledState1 = LOW;

kann ersetzt werden durch

ledState1 = !ledState1;

auch ginge

digitalWrite(ledPin1, !digitalRead(ledPin1));

ich habe mein Programm für's erste in den Müll gehauen und mich noch einmal mit den grundlegenden Dingen beschäftigt.

Richtig gemacht!

Dein Programm ist jetzt aufgeräumt.
Sauber strukturiert.

An der Funktion kann ich nichts kritisieren.
Ganz klar: Du bist auf dem richtigen Weg!

Aber es gibt ein paar Dinge, die mich persönlich stören würden, wenn das mein Programm wäre.
Das sind die durchnummerierten Variablen, und die Codeduplikate.

Ich habe deinen Code mal verwurstet:

struct Blink
{
  const byte pin;
  const unsigned long interval;
  unsigned long timeStamp;
  bool state;
  
  Blink(const byte pin,const unsigned long interval): pin(pin),interval(interval),timeStamp(0),state(false)
  {
  }

  void init()
  {
    pinMode(pin,OUTPUT);
  }

  void run()
  {
    if(millis() - timeStamp >= interval) 
    {  
      timeStamp = millis();
      state = !state;
      digitalWrite(pin,state);  
    }
  }
};

Blink leds[] = {{2,1000},{3,400},{4,270},{5,100}};

void setup() 
{
  for(Blink &led:leds) led.init();
}

void loop() 
{
  for(Blink &led:leds) led.run();
}

Es ist im Grunde der gleiche Code, dein Code.
Nur etwas aufgemischt.

Vielleicht kannste dir da ja was von abschauen.

@agmue

Danke für Deine Änderungsvorschläge. Ich habe Sie gleich eingepflegt.

combie:
Aber es gibt ein paar Dinge, die mich persönlich stören würden, wenn das mein Programm wäre.
Das sind die durchnummerierten Variablen, und die Codeduplikate.

Ich nenne es den Versuch ein wenig den Überblick zu behalten.
Aber ich gebe Dir Recht "Schön ist anders".

combie:
Vielleicht kannste dir da ja was von abschauen.

Abschauen ganz sicher, aber bevor die Grundlagen nicht sitzen, versuche ich mich lieber noch nicht an der Umsetzung.

Trotzdem Danke für die Anregung.

LG Oscar