Arduino: millis() als delay() in If-Klause benutzen?

4 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Erstmal sollte Dir ganz genau klar sein, was passieren soll: Soll die Pumpe 10 Sekunden nach dem ersten oder dem letzten Meßwert >= 600 ausschalten? Oder soll sie nach dem ersten Triggern 10 Sekunden laufen, dann ausgehen und eventuell gleich wieder angehen?

Wenn Dir das klar ist, gibt es verschiedene (letztlich äquivalente) Möglichkeiten, das zu implementieren. Zum Beispiel mit Flags oder als Zustandsmaschine (mit switch ... case), um Dir zu merken, ob die Pumpe gerade läuft oder nicht.

Knifflig ist immer die Tatsache, daß millis() ja überlaufen kann. Sicher ist es wohl, auf "millis() - startZeit > sollLaufzeit" zu testen, bei allen anderen Methoden (z.B. Stopzeit vorher berechnen und dann in der Schleife testen) mußt Du selbst auf Überläufe prüfen.

(Und nebenbei: Übersichtlicher ist es immer, Werte wie Schellwert und Laufzeit in Konstanten zu packen und nicht direkt in den Code zu schreiben.)

jan106 
Fragesteller
 19.05.2019, 20:57

Sie soll nach dem Triggern 10 Sekunden laufen und dann woeder ausgehen, das nächste mal über 600 wieder das selbe. Nett wäre, wenn du mir ein Beispiel zeigen könntest. Code noch nicht lange, bin noch Anfänger.

So müsste man die Zeit ja nur noch starten, wenn der If-Satz triggert, nicht?

event= 10000

currentMillis = millis()

if(currentMillis - previousMillis >= event) {

digitalWrite(..., LOW);

currentMillis = previousMillis

}

0
gwf79  20.05.2019, 20:57
@jan106

Reicht Dir die Lösung von Over9000IQ, obwohl die ja doch retriggerbar ist?

Meine nicht-retriggerbare Lösung sähe (Trivilalitäten entfernt) so aus:

enum { PumpOff, PumpOn };
int pumpState = PumpOff;

void loop() {

 switch (pumpState) {
 case PumpOff:
   if (analogRead(SensorPin) >= threshold) {
     pumpStartTime = millis();
     digitalWrite(PumpPin, HIGH);
     pumpState = PumpOn;
   } // if
   break;
 case PumpOn:
   if (millis() - pumpStartTime > PumpRunTime) {
     digitalWrite(PumpPin, LOW);
     pumpState = PumpOff;
   } // if
   break;
 } // switch pumpState

}// loop

oder kürzer, aber möglicherweise etwas hackish:

 switch (digitalRead(pumpPin)) {
 case LOW: // Pumpe läuft nicht
   if (analogRead(SensorPin) >= threshold) {
     pumpStartTime = millis();
     digitalWrite(PumpPin, HIGH);
   } // if
   break;
 case HIGH: // Pumpe läuft gerade
   if (millis() - pumpStartTime > PumpRunTime)
     digitalWrite(PumpPin, LOW);
   break;
 } // switch pumpState

An Deinem Codeschnipsel verstehe ich nicht, wieso Du currentMillis zurücksetzt (oder wozu Du die Variable überhaupt brauchst).

1
jan106 
Fragesteller
 26.05.2019, 14:25
@gwf79

  if (millis() - pumpStartTime > PumpRunTime) {

Bei dem Satz steht immer "pumpStartTime was not decared in this scope" oben steht aber pumpStartTime = millis() (genau gleich geschrieben)

0
gwf79  26.05.2019, 15:49
@jan106

Deshalb hatte ich extra "Trivilalitäten entfernt" dazugeschrieben. Variablen und Konstanten deklarieren, Pins auf In- oder Output stellen und sowas kannst Du doch auch alleine.

1
jan106 
Fragesteller
 27.05.2019, 17:11
@gwf79

Ja die hab ich ja eingestellt, nur eigentlich muss man für "pumpStartTime = millis()" nichts festlegen, trotzdem kommt die Fehlermeldung

0
gwf79  27.05.2019, 17:37
@jan106

Sorry, verstehe ich gerade nicht. Hast Du irgendwo vorher

unsigned long pumpStartTime;

stehen?

pumpStartTime = millis();

ist zwar eine Zuweisung, aber keine Deklaration. Der Compiler (für C(++), in anderen Sprachen sieht das anders aus) weiß ja ohne Deklaration gar nicht, welchen Datentyp Du gern hättest und hat auch keinen Speicher für die Variable reserviert.

(Theoretisch könntest Du die Variable erst deklarieren, wenn Du sie auch benutzt, so:

unsigned long pumpStartTime = millis();

Aber aus Gründen der Übersichtlichkeit hat es sich eingebürgert, Variablen und Konstanten so früh wie möglich --am Beginn ihres Scopes-- zu deklarieren, bei Arduino-Sketchen üblicherweise gleich nach den #includes und noch vor der setup()-Prozedur. So, wie Du es in Deinem Code mit "const int pumpPin = 7;" ja auch machst.)

1
jan106 
Fragesteller
 27.05.2019, 21:22
@gwf79

Ah sorry, wusste nicht, das man das so machen muss. Dann ist es wohl klar, warum's nicht funktioniert hat.

Danke dir!

0
jan106 
Fragesteller
 27.05.2019, 22:53
@jan106

*Meine damit explizit das "unsigned long pumpStartTime;"

0
jan106 
Fragesteller
 27.05.2019, 21:28

Da du mir so gut weiterhelfen konntest, frage ich dich mal, (hat mit dem gleichen Sketch zu tun) warum das hier nicht funktioniert:

(bin mir ziemlich sicher, das richtig eingebaut zu haben, hat mir ein guter Freund gemacht)

soll dazu dienen, Heizung über 22°C auszuschalten und unter 17°C bis 22°C einzuschalten, hier auch wieder, sobald es über 22° ist, erst wieder einschalten, wenns unter 17° ist.

long timestamp_delay = 6500L;
bool schedule_off = false;
 
// 17-22°C heatPin, HIGH
// >22°C heatPin, LOW
 
 
void setup() {
 
}
 
void loop() {
  if (t > 22 && !schedule_off) {
    digitalWrite(heatPin, LOW);
    schedule_off = true;
  }
 
  if (t <= 17 && schedule_off) {
    digitalWrite(heatPin, HIGH);
    schedule_off = false;
  }
0
gwf79  28.05.2019, 15:55
@jan106
warum das hier nicht funktioniert

Was bedeutet "nicht funktioniert"? Kompiliert es nicht (Welche Fehlermeldung?), tut es gar nichts, oder tut es nicht das, was es soll, sondern...?

soll dazu dienen, Heizung über 22°C auszuschalten und unter 17°C bis 22°C einzuschalten, hier auch wieder, sobald es über 22° ist, erst wieder einschalten, wenns unter 17° ist.

Sorry, den Satz verstehe ich wieder nicht ganz. Du unterscheidest drei Fälle: T>22°C, 17°C<T<=22°C und T<=17°C. Aber was soll wann genau passieren?

Logisch wäre eine Hysterese:

  • T > 22°C: Heizung aus
  • T <= 17°C: Heizung an
  • dazwischen: Heizung bleibt, wie sie ist

Wenn es das ist, was Du willst, dann verstehe ich nicht, wozu Du die Variable schedule_off brauchst (und was timestamp_delay damit zu tun haben soll) , sehe aber auch keinen Fehler in Deinem Codeschnipsel.

Wenn Du aber das willst, was im Kommentar steht, nämlich:

  • T > 22°C: Heizung aus
  • 17°C < T <= 22°C: Heizung an
  • T <= 17°C: ???

dann solltest Du Dir erstmal klar werden, was statt ??? bei weniger als 17°C stehen soll. Danach würde es vermutlich auch ganz einfach.

0

Nun, du musst ständig prüfen, ob von einem gewissen Startpunkt aus eine bestimmte Zeit vergangen ist.

Pseudocode:

starttime = millis()
delay = 5

while true:
  if (millis() - starttime > delay):
    break

Die Schleife sorgt dafür, dass der Programmfluss gebremst wird. Wenn die Differenz aus der aktuellen Zeit und der Startzeit über die Verzögerungszeit hinausgeht, kann die Sperre aufgehoben werden.

jan106 
Fragesteller
 19.05.2019, 21:26

Wo muss ich das denn einbauen? while true (während was true?)

Vielen Dank

0
regex9  19.05.2019, 21:34
@jan106

Du kannst eine konkrete Implementation dieses Pseudocodes tatsächlich anstelle des delay-Aufrufs einsetzen.

Für die Bedingung habe ich true angegeben, da es sich um eine Endlosschleife handeln soll. Sie läuft so lange, wie ihre Bedingung den Wert wahr ergibt. Das erfüllt der Wert true. Du könntest auch andere Ausdrücke schreiben, wie:

while (1 == 1) {
  // ...
}
0
regex9  19.05.2019, 21:46
@jan106

Oder du machst dir die loop-Funktion zunutze, die ja bereits endlos wiederholt wird. Die beiden Variablen für Dauer und Startzeit werden global definiert. Wenn das Warten starten soll, setzt du beide.

Das sollte (in Pseudocode formuliert) ungefähr so aussehen:

delay = 0
starttime = 0

loop:
  if delay == 1000:
    # before delay
    
    # when to start:
    if delay == 0:
      delay = 10000
      starttime = millis()
  
  if (delay != 0 and millis() - starttime > delay):
    # after delay
    delay = 0
0
jan106 
Fragesteller
 19.05.2019, 22:11
@regex9

Danke dir! Ich verstehe nicht ganz wie und wo ich deinen Vorschlag einbauen soll. Kannst du mir sagen, ob die Variante von Over9000IQ funktioniert? Ansonsten müsstest bzw. könntest du mir deine Variante in meinen Code einpflanzen? Das wäre echt toll.

0

Du hast die Frage jetzt neu gestellt... Aber keine Hinweise oder Erklärungen warum. Zudem schon eine "Hilfreiche Antwort vergeben, so das augenscheinlich dein Problem bereits gelöst ist. Es gibt keine Ergänzung, auch keinen aktuellen Code mit neuer Problemstellung... Soll man jetzt die Glaskugel auspacken, warum Du die Frage neu stellst? War es ein Versehen? Oder erhoffst Du Dir neue Erkenntnisse, vor allem, wo man nicht mal weiß, was aktueller Sachstand ist???

jan106 
Fragesteller
 20.06.2019, 21:17

Nein, leider hat kein Vorschlag geklappt, ich war bei vollstem Bewusstsein, als ich diese Frage gestellt hatte. Den Code hab ich heute Nachmittag nochmals überarbeitet, sodass jetzt nur noch die eine letzte Frage übrig bleibt. Ich erhoffe mir neue Erkenntnis, ja, und am besten einen Lösungsvorschlag, der möglichst simpel gehalten ist. Es kann doch nicht sein, das man für "delay()" keine Alternativen hat. Ich bin froh um alle Antworten, nur nicht un die, die mich dauernd kritisieren, besser antwortet ihr dann gar nicht. Vielen lieben Dank, für eure Hilfe.

0
RareDevil  20.06.2019, 21:22
@jan106

Okay.. Das ist ein Ansatzpunkt... :)

Delayalternative gibt es. Der Beispielsketch "Blinken ohne Delay" ist z.B. so gelöst...

Im Prinzip brauchst Du eine "unsigned Long"-Variable (z.B. timespeicher), die zu einem bestimmten Punkt den Wert von millis() speichert...

In einer If-Abfrage schaust Du dann, ob der Wert millis()-timespeicher größer ist, wie ein gewollter Wert... Dann führst Du in der If eben etwas aus....

Wie man das richtig in deinen Code einbindet, muss man dann schauen... Ich habe mir diesen noch nicht durchgeschaut...

Dein vollständiger aktueller Code ist in der Frage?

1
jan106 
Fragesteller
 21.06.2019, 12:07
@RareDevil

Ja genau der Code, der in der Frage ist, um diesen geht's. Das habe ich bereits versucht, so wie du oben erklärst. Irgendwie hat es den Wert nicht gespeichert, wenn ich das in die If-Klause geschrieben habe. Danke für deine Hilfe.

0
RareDevil  21.06.2019, 17:26
@jan106

Hab mal was geschrieben... Ist ein Entwurf, fehlerfrei kompiliert.. Testen kann ich es nicht, da mir die Sensoren fehlen... Theoretisch sollte es so klappen...

Pin 12 war 2x deklariert, einmal oben DHTPIN und einmal als Pumpenpin, den hab ich jetzt mal 13 genannt, Bemerkung ist aber dran geblieben... Und hab was aufgeräumt und Ordnung geschaffen, sowie ein paar Kommentare als Gedankenstütze eingefügt...

Deine Bewässerung hat 5 Minuten Sperrzeit bekommen, damit der Feuchtesensor Zeit hat, ohne das direkt wieder gepumpt wird...

#include "DHT.h"

//Sensor
#define DHTPIN 12 // !12 ist auch für Pumpe verwendet
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
int moist_value = analogRead(A0);

//VENT DELAY//
long schedule_freq = 3200000; // 3200s
long schedule_duration = 300000; //300s
long schedule_timestamp = 0;
long schedule_timestamp_off = 0;
bool schedule = false;

//PUMP
bool pumpen = false;
bool pumpsperre = false;
unsigned long pumptime = 0;

//Pinbelegung
const int heatPin = 7; //Heizung
const int ventPin = 4; //Ventilator
const int pumpPin = 13; //Wasserpumpe !!!Bereits verwendet!!!

void setup() {
  dht.begin();
  pinMode(heatPin, OUTPUT);
  pinMode(ventPin, OUTPUT);
  pinMode(pumpPin, OUTPUT);
}

void loop() {
// Einlesen der Werte
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  moist_value = analogRead(A0);
 
// Steuerung Heizung
  if (t <= 16.0) {
    digitalWrite(heatPin, HIGH);
  }

  if (t >= 25.0) {
    digitalWrite(heatPin, LOW);
  }

// ---- Steuerung Pumpe ----
// Prüfung Bodenfeuchte & Pumpsperre nicht aktiv
  if (moist_value >= 600 && pumpsperre == false) {
    pumpen = true;        // pumpen einschalten
    pumpsperre = true;    // pumpsperre setzen
    pumptime = millis();  //Zeitspeicher setzen
  }

//prüfen ob pumpen aktiv und 10sek abgelaufen  
  if (pumpen == true && (millis()-pumptime) >= 10000){
    pumpen = false; // Pumpe aus
  }

//prüfen ob Pumpsperre aktiv und seit dem letzten Pumpen mehr wie 300s vergangen sind
  if (pumpsperre == true && (millis()-pumptime) >= 300000){
    pumpsperre = false; //Pumpsperre deaktivieren
  }

  digitalWrite(pumpPin, pumpen); // setzt den Ausgang wie die Variable für pumpen

// ---- Ventilatorsteuerung ---

  if (schedule_timestamp < millis()) {
    schedule_timestamp = millis() + schedule_freq;
    schedule_timestamp_off = millis() + schedule_duration;
    schedule = true;
    //Vent On
    digitalWrite(ventPin, HIGH);
  }

  if (schedule_timestamp_off < millis() && schedule ) {
    schedule = false;
    //Vent Off
    digitalWrite(ventPin, LOW);
  }

}
1
RareDevil  21.06.2019, 17:37
@RareDevil
long schedule_timestamp = 0;
long schedule_timestamp_off = 0;

Diese beiden Zeilen müssen noch "unsigned long" werden

unsigned long schedule_timestamp = 0;
unsigned long schedule_timestamp_off = 0;

Grund: millis() ist ein unsigned long-Wert... Wenn die Variable zum speichern nur als long definiert wird, gibt es irgendwann ein Problem mit dem Zeitspeicher, weil millis() nicht vollständig abgebildet und gespeichert werden kann...

1
jan106 
Fragesteller
 22.06.2019, 16:13
@RareDevil

Cool! Danke für deine Bemühungen, der Code funktioniert perfekt!

1

Dein Sketch so wie er steht ist nichtmal ausführbar beheb erstmal grundlegende Fehler wie "analog.Read" oder "digialWrite"

jan106 
Fragesteller
 19.05.2019, 18:49

Sorry, hab nur den Schnipsel nachgebildet, hier korrigiert

Willst du den ganzen Code auch sehen? Ist recht komplex.

const int pumpPin = 7

void setup() {

pinMode(pumpPin, OUTPUT);

void loop() {

moistureValue = analogRead(pumpPin);

if (moistureValue >= 600) {

digitalWrite(pumpPin, HIGH);

delay(10000);

digitalWrite(pumpPin, LOW);

}

}

0
Over9000IQ  19.05.2019, 20:19
@jan106
const int pumpPin = 7;
const long wait = 10000;
unsigned long pumpOnTime = 0;
int value = 0;

void setup()
{
  pinMode(pumpPin, OUTPUT);
  pinMode(A0, INPUT);
}

void loop()
{
  unsigned long currentMillis = millis();
  value = analogRead(A0);
  if(value >= 600) {
    digitalWrite(pumpPin, HIGH);
    pumpOnTime = currentMillis;
    }
  if (currentMillis - pumpOnTime >= wait) {
    digitalWrite(pumpPin, LOW);
    }
}
1