Arduino Basics: Delay vs. Millis

Arduino Basics: Delay vs. Millis

Kontakt: MediaDock, TK

Wenn du mit einem Arduino erste Programme schreibst ist dir delay() wohl ein Begriff. Manchmal ist diese Pause für ein Programm aber nicht sehr förderlich. An dem Beispiel eines studentischen Projekts erklären wir dir kurz was der Unterschied des Pausierens in einem Programm durchlauf ist und weshalb diese Art des Pause machens hilfreich sein kann.

Kurz erklärt: delay()

Arduino Reference delay()

Wenn wir einen Arduino programmieren, verwenden wir oft delay() für kurze Pausen. Das delay() hält das Programm an einem bestimmten Punkt an und setzt es fort, wenn die Zeit abgelaufen ist. Dies kann sehr nützlich und gut genug für einfache Skizzen sein. Zum Beispiel in dem bekannten Blink Sketch:

void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}

Kurz erklärt: millis()

Arduino Reference millis()

Arduino Example Blink without delay()

Manchmal möchte man das Programm aktiv halten und kann nicht warten, bis ein delay() seine Pause beendet hat. Zum Beispiel kann man eine LED blinken lassen, während man einen Tastendruck ausliest. In diesem Fall kann man delay() nicht verwenden, da Arduino das Programm während delay() pausiert. Wenn der Knopf gedrückt wird, während Arduino pausiert und auf das Ende von delay() wartet, verpasst das Programm den Knopfdruck.

Derselbe Blinksketch sieht mit millis() etwas komplizierter aus, führt aber von aussen betrachtet zum selben Verhalten wie der delay() Blinksketch: Er lässt die LED 1x pro Sekunde blinken. Aber der Sketch pausiert nicht und der Loop bietet die Möglichkeit, weitere Befehle auszuführen. Im folgenden Beispiel haben wir einen Loopcounter eingefügt, der die Loops zählt und alle 100 Loops einen Serialprint ausgibt:

// constants won't change. Used here to set a pin number:
const int ledPin = LED_BUILTIN; // the number of the LED pin

// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long loopCounter = 0; // counter for loop iterations

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0; // will store last time LED was updated

// constants won't change:
const long interval = 1000; // interval at which to blink (milliseconds)

void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
// Initialize serial communication at 9600 baud:
Serial.begin(9600);
}

void loop() {
// Increment the loop counter
loopCounter++;

// Print the counter every 100 iterations to avoid flooding the serial monitor
if (loopCounter % 100 == 0) {
Serial.print("Loop count: ");
Serial.println(loopCounter);
}

// here is where you'd put code that needs to be running all the time.
unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;

// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}

// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}

Videotutorial

Es gibt viele Video-Tutorials, die das ganze Verfahren im Detail erklären, ein guter Einstieg ist diese Serie hier:

Ein praktisches Beispiel

Github Code Download

In einer studentischen Arbeit haben wir 3 Ultraschall-Distanzsensoren verwendet, um 3 Events auszulösen. Wenn ein Sensor ausgelöst wird, leuchtet eine LED für eine bestimmte Zeit und über die keyboard() Funktion wird ein Befehl an Touchdesigner gesendet um eine Sounddatei auszulösen. Im beigefügten Beispiel wollten wir die Sensoren aktiv halten, um eventuell andere Events parallel auslösen zu können.

Beispiel delay()

In Codebeispiel Arduino_Ultrasonic_Distance_Trigger_MultiSensor_Delay.ino wird ein Sensor nach dem anderen ausgelesen. Wenn ein Sensor den Abstand eines Sensors unterschreitet, wird der entsprechende Prozess ausgelöst:

  • Messzyklus beginnt
  • Ein Sensorwert wird unterschritten:
    • ein Tastaturbefehl wird ausgelöst und das LED aktiviert
    • eine Pause wird eingeleitet und das Programm stoppt für 10 Sekunden
  • Ein neuer Messzyklus beginnt.

Beispiel millis()

Im Codebeispiel Arduino_Ultrasonic_Distance_Trigger_MultiSensor_Millis.ino werden die Sensoren auch kurz aufeinander folgend ausgelesen. Wir haben aber mehr Steuermöglichkeiten:

  • Sensor 1 wird nur ausgelesen wenn kein Sensor-Event aktiv ist:
  • Sensor 2 kann auch ausgelesen werden wenn Sensor 1 bereits ausgelöst hat aber nicht wenn Sensor 3 aktiv ist.
  • Sensor 3 kann immer aktiviert werden ausser der eigene Sensor-Event 3 aktiv ist.

Fazit

Delay() ist eine «quick and dirty» Variante um ein Programm zu schreiben und Pausen in Loops einzufügen. Wenn du einen etwas komplexeren Controller benötigst, der mehrere Dinge gleichzeitig tun kann, solltest du millis() verwenden. Für grössere und komplexere Arduino Projekte ist diese Variante ideal um mehrere zeitgesteuerte Events hintereinander und parallel laufen zu lassen, ohne dass dein ganzes Programm ins Stocken gerät.