Go Down

Topic: BlinkwithoutDelay - Die Nachtwächtererklärung (Read 12490 times) previous topic - next topic

guntherb

Sep 13, 2016, 08:48 am Last Edit: Nov 18, 2016, 06:07 pm by guntherb
Ich wurden gebeten, die Erklärung des BlinkWithoutDelay hier nochmal reinzustellen.

Am Beispiel eines Wachmanns, der seine Runden dreht, wird in einfacher und eingängiger Weise erklärt, wie man ohne delay() zeitliche Abfolgen programmieren kann.

(an die Profis: es geht auch einfacher, es sind syntaktische Unsauberkeiten drin. Mein Augenmerk lag auf einer möglichst einfachen Darstellung für Menschen, die noch niemals im Leben was mit Programmiersprachen zu tun hatten)

Also: Wie funktioniert Blink Without Delay?
So kompliziert ist das gar nicht.

Gehen wir vom Blinken mal einen Schritt weg  zu was ganz praktischem: Wir haben einen Nachtwächter / Wachmann, der immer seine Runden um ein Haus geht (das entspricht unserer Loop).
Zusätzlich erhält er die Aufgabe, jede Stunde für 15min eine Lampe einzuschalten. Die Lampe selbst sieht er nicht, aber es gibt einen Schalter am Haus.
Was macht er also:
Er schaltet das Licht ein, merkt sich die Zeit und dass die Lampe ein ist und macht weiter seine Runden. Bei jeder Runde schaut er auf die Uhr, ob die 15min schon um sind. Sind sie noch nicht um, geht er einfach weiter, wenn die um sind, schaltet er die Lampe aus und merkt sich, dass sie jetzt aus ist.
Nun schaut er bei jeder Runde auf die Uhr, um zu prüfen, ob die Stunde seit Einschalten schon um ist.
Ist sie um, schaltet er wieder ein, notiert sich Status der Lampe und Uhrzeit, und nimmt seine Runden wieder auf.

Genau so macht es der Arduino.
wenn wir die Schritte des Wachmanns in Arduinosprache übersetzen, schaut das in etwas so aus:



wenn sich der Wachmann die Zeiten auf dem Zettel notiert, schreibt der Arduino einen Wert in eine Variable. millis() ist die Systemuhr des Arduino. Sie zählt einfach die vergangenen millisekunden seit Start des Arduino.

der zugehörige Code schaut dann so aus:
Code: [Select]
unsigned long LED_timestore;  // Variable Speicher für Systemzeit.
int LedStatus = LOW;
int LedPin = 13;



void setup() {
  pinMode(LedPin, OUTPUT);  // teilt dem Arduino mit, dass das ein Ausgang ist.
}

void loop() {
  if (LedStatus == LOW) {
    if (millis() - LED_timestore> 1000 ) {
      digitalWrite(LedPin, HIGH);
      LED_timestore = millis();
      LedStatus = HIGH;
    }
  } else {
    if (millis() - LED_timestore> 300) {
      digitalWrite(LedPin, LOW);
      LedStatus = LOW;
    }
  }
}


Wenn der Wachmann / Arduino mehrere Lampen schalten soll, braucht er einfach noch mehr Zettel / Variable und Schalter / Pins.

Geben wir Ihm doch noch eine Aufgabe dazu:
Erweiterung:
Ausserdem soll er alle 3h die Bewässerung für 10min einschalten.


Dann könnte das so aussehen:



In dieser Weise kann man (fast) beliebig viele Aktionen unabhängig voneinander ausführen lassen.
Man kann es übersichtlicher gestalten, in dem man die einzelnen Aktionen in Funktionen packt, oder flexibler, in dem man die Zeiten in Arrays steckt. Und, und, und...



Im Anhang Tabellen als Word-Datei (Excel geht leider nicht)
Grüße
Gunther

Schuppeste

#1
Sep 13, 2016, 10:31 am Last Edit: Apr 24, 2018, 01:33 pm by Schuppeste
Das kann man aber auch allgemein besser schreiben, ich konnte es jetzt nicht testen..

Code: [Select]

unsigned long Zeit_Eben;  // Variable Speicher für Systemzeit.
int meineEinZeit=700;
int meineAusZeit=300;
int ledStatus = LOW;
int LedPin = 13;

void setup() {

  pinMode(LedPin, OUTPUT);  // teilt dem Arduino mit, dass das ein Ausgang ist.

}

void loop() {

  int Zeitfaktor=0;

  if (ledStatus == LOW)
    Zeitfaktor=meineAusZeit;
  else
    Zeitfaktor=meineEinZeit;


  if (Jetzt()-Zeitfaktor > Zeit_Eben ) {

    Zeit_Eben = Jetzt();
    ledStatus= !ledStatus; // Umkehrung Status
    digitalWrite(LedPin, ledStatus);

  }

}

unsigned long Jetzt()
{
  return millis();
}


oder so

Code: [Select]

unsigned long Zeit_Eben;  // Variable Speicher für Systemzeit.
int meineEinZeit=700;
int meineAusZeit=300;
int LedStatus = LOW;
int LedPin = 13;

void setup() {
  pinMode(LedPin, OUTPUT);  // teilt dem Arduino mit, dass das ein Ausgang ist.
  }

void loop() {
  if (LedStatus == LOW) {
 
    if (Jetzt()-meineAusZeit > Zeit_Eben ) {
      digitalWrite(LedPin, HIGH);
      Zeit_Eben = Jetzt();
      LedStatus = HIGH;
    }
 
  } else {
   
 if (Jetzt()-meineEinZeit > Zeit_Eben) {
      digitalWrite(LedPin, LOW);
      Zeit_Eben = Jetzt();
      LedStatus = LOW;
   
 }
}
}

unsigned long Jetzt()
{
return millis();
}


Schuppeste

Für das Verständnis ist es allerdings besser zu verstehen.. weil man eine ohnehin schwierige Numemr nicht noch im Kopf umkehren muss.

Magst wohl recht haben.. aber da muss ein Programm erstmal hinkommen.


Schuppeste

#3
Sep 13, 2016, 10:49 am Last Edit: Apr 24, 2018, 08:33 pm by Schuppeste
Dann erkläre es doch gleich richtig!

Womöglich wird die Millis() Variable nach 50Tagen oder wann auch immer überlaufen und auf Null resettet, wenn man kurz vorm Überlauf die 300 oder 700 draufrechnet gibts einen Rechenfehler.

Also macht man das mit Minus.

combie

#4
Sep 13, 2016, 11:32 am Last Edit: Sep 13, 2016, 03:29 pm by combie
Code: [Select]
#include <TaskMacro.h>
// Die  TaskMacros sind hier zu finden:
// https://forum.arduino.cc/index.php?topic=415229.0


const bool AUS = false;
const bool EIN = true;

// Pin def
const byte licht  = 13;
const byte wasser = 12;


void tueWasser()
{
  taskBegin();
  taskPause(3L*60L*60L*1000L); // 3H warten
  while(1)
  {
   digitalWrite(wasser,EIN);
   taskPause(10L*60L*1000L); // 10 Minuten warten
   digitalWrite(wasser,AUS);
   taskPause((3L*60L*60L-10L*60L)*1000L); // Warten bis 3H voll
  }
  taskEnd();
}

void tueLicht()
{
  taskBegin();
  taskPause(60L*60L*1000L); // Stunde warten
  while(1)
  {
   digitalWrite(licht,EIN);
   taskPause(15L*60L*1000L); // 1/4 Stunde warten
   digitalWrite(licht,AUS);
   taskPause(45L*60L*1000L); // 3/4 Stunde warten
  }
  taskEnd();
}

void nachtwaechter()
{
  tueLicht();
  tueWasser();
}

void setup()
{
  pinMode(licht,OUTPUT); 
  digitalWrite(licht,AUS);
 
  pinMode(wasser,OUTPUT);
  digitalWrite(wasser,AUS);
}

void loop()
{
  nachtwaechter();
}


Das ist eine Umsetzung des Nachtwächters mit Hilfe der TaskMacros.
 


PS:
Schuppeste, meine Kritik an deinem Code habe ich gelöscht, da sie jetzt hinfällig ist.
Der Pessimist sieht die Wolke vor der Sonne.
Der Optimist sieht die Sonne hinter der Wolke.

Mantra: Die Sonne scheint immer!

guntherb

Das kann man aber auch allgemein besser schreiben, ich konnte es jetzt nicht testen..
ja, ich weiß.

Deshalb hatte ich auch das geschrieben:
(an die Profis: es geht auch einfacher, es sind syntaktische Unsauberkeiten drin. Mein Augenmerk lag auf einer möglichst einfachen Darstellung für Menschen, die noch niemals im Leben was mit Programmiersprachen zu tun hatten)
Aber tob dich ruhig weiter aus wenns deinem Wohlbefinden dient.

Grüße
Gunther

HotSystems

ja, ich weiß.

Deshalb hatte ich auch das geschrieben: Aber tob dich ruhig weiter aus wenns deinem Wohlbefinden dient.

Hallo Gunther,

deine Erklärung (auch mit dem DocToGo) ist sehr ausführlich und verständlich.
Das es nicht um den Sketch geht, sondern um eine verständliche Beschreibung hat wohl nicht jeder mitbekommen.

Alle Kritiker müssen das erst mal besser machen. Also mach weiter so.

Vielen Dank.

P.S.
Die Lösung von combie finde ich allerdings gut gelungen und hoffe das es für Anfänger verständlich ist.
Gruß Dieter

I2C = weniger ist mehr: weniger Kabel, mehr Probleme. 8)

agmue

Ich finde Deine Anleitung sehr anschaulich, weshalb ich sie bei meiner Anleitung Ein Endlicher Automat entsteht mit verlinkt habe.

Deine Anmerkung habe ich gelesen, dennoch fände ich diese Zeilen nicht gar so verwirrend:
Code: [Select]
bool LedStatus = LOW;
const int LedPin = 13;
Wahnsinn und Verstand trennt nur eine dünne Wand. (Daniel Düsentrieb)

Tommy56

Ich wurden gebeten, die Erklärung des BlinkWithoutDelay hier nochmal reinzustellen.

Am Beispiel eines Wachmanns, der seine Runden dreht, wird in einfacher und eingängiger Weise erklärt, wie man ohne delay() zeitliche Abfolgen programmieren kann.
Im Anhang Tabellen als Word-Datei (Excel geht leider nicht)
Vielen Dank für dieses geniale Beispiel.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

Moko

@guntherb
Quote
1h Wachmann entspricht 1sek Arduino
Erweiterung:
Ausserdem soll er alle 3h die Bewässerung für 10min einschalten.

Auf die Uhr sehen, ob die 3h schon rum ist. Wenn ja:    if (millis() -  Water_timestore > 1000){
müsste es nicht heissen:

Code: [Select]
  if (millis() -  Water_timestore > 3000){

guntherb

Grüße
Gunther

Doc_Arduino

Hallo,

ich weiß auch nicht warum man sich über eine gute Erklärung künstlich aufregt.
Zudem der Variablenüberlauf hier gar keine Rolle spielt.



Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

guntherb

Es ging ja gar nicht um den Überlauf (der ist korrekt), sondern dass ich 1000 anstelle 3000 geschrieben hatte. Ich verstehe es auch nicht als aufregen,  sondern als Hinweis auf einen Fehler.

Ich werde es bei Gelegenheit korrigieren.
Grüße
Gunther

combie

#13
Nov 18, 2016, 05:22 pm Last Edit: Nov 18, 2016, 05:23 pm by combie
Quote
Ich verstehe es auch nicht als aufregen,  sondern als Hinweis auf einen Fehler.
dito/ebenso/konform

Dieses Tutorial soll Anfängern auf die Sprünge helfen, und nicht verwirren.
Der Pessimist sieht die Wolke vor der Sonne.
Der Optimist sieht die Sonne hinter der Wolke.

Mantra: Die Sonne scheint immer!

Moko

dito/ebenso/konform

Dieses Tutorial soll Anfängern auf die Sprünge helfen, und nicht verwirren.
Deshalb hatte ich auch die Frage gestellt. Nicht dass sich Anfänger wundern wieso die "1000" einmal 1 Stunde, und einmal 3 Stunden bedeuted.

Es sollte keinesfalls eine Kritik an der genialen Erklärung sein.

Go Up