Arduino: millis() als delay() in If-Klause benutzen?
Hi! Ich suche schon lange nach einer Lösung wie ich millis() als delay() benutzen kann. Ich habe einen Bodenfeuchtesensor und ein Relais, das an eine Pumpe angeschlossen wird. Ich möchte, dass das Relais für 10 Sekunden angeht, wenn der Bodenfeuchtesensor einen Wert über 600 ausspuckt. Bisher lautet die If-Klause so:
(siehe unten)
Möchte das aber gerne mit millis() machen. Habe schon viel studiert und probiert, aber keine Lösung gefunden. Nachgelesen habe ich auch. Das Prinzip von millis habe ich verstanden, nur nicht, wie das in einem If-Satz funktioniert.
Vielen Dank für Eure Hilfe!
#include "DHT.h"
#define DHTPIN 12
#define DHTTYPE DHT22
//VENT DELAY//
long schedule_freq = 3200000L;
long schedule_duration = 300000L;
long schedule_timestamp = 0L;
long schedule_timestamp_off = 0L;
bool schedule = false;
//VENT DELAY//
//PUMP//
//PUMP//
const int heatPin = 7;
const int ventPin = 4;
const int pumpPin = 12;
int moist_value = analogRead(A0);
DHT dht(DHTPIN, DHTTYPE);
void setup() {
dht.begin();
pinMode(heatPin, OUTPUT);
pinMode(ventPin, OUTPUT);
}
void loop() {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (t <= 16.0) {
digitalWrite(heatPin, HIGH);
}
if (t >= 25.0) {
digitalWrite(heatPin, LOW);
}
if(moist_value >= 600) {
digitalWrite(pumpPin, HIGH);
delay(10000);)
digialWrite(pumpPin, LOW;
}
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);
}
}
4 Antworten
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.)
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.
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
}
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).
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.)
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.
Wo muss ich das denn einbauen? while true (während was true?)
Vielen Dank
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) {
// ...
}
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
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.
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???
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.
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?
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.
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);
}
}
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...
Cool! Danke für deine Bemühungen, der Code funktioniert perfekt!
Dein Sketch so wie er steht ist nichtmal ausführbar beheb erstmal grundlegende Fehler wie "analog.Read" oder "digialWrite"
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);
}
}
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);
}
}
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.