Ersatz von delay(x) durch millis()

Hallo zusammen,
ich habe Schwierigkeiten in einen im Internet gefundenen Code-Schnipsel die delay-Funktion mit millis() zu ersetzen, da nebenher noch andere Dinge laufen.
Allgemein handelt es sich dabei um eine Art "bewegenden" Regenbogen in einem WS2812B LED-Strip. Über delay(x) wird die Geschwindigkeit festgelegt:

unsigned long previousMillis = 0;

void regenbogenmodus_laufend(int speeddelay)
{
  byte *c;
  uint16_t i, j;
  for(j=0; j<256; j++) {
    for(i=0; i< NUM_LEDS; i++) {
      c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
      leds[i].setRGB(*c,*(c+1), *(c+2));
    }
    FastLED.show();
    delay(speeddelay);
    }
}

Folgendermaßen habe ich versucht delay mit millis() zu ersetzen, aber es kommt einfach nicht das gleiche Ergebnis bei gleichem Wert von speeddelay (liegt im Bereich von 20ms) raus:

unsigned long previousMillis = 0;

void regenbogenmodus_laufend(int speeddelay)
{
  byte *c;
  uint16_t i, j;
  for(j=0; j<256; j++) {
   if (millis()-previousMillis>speeddelay){
    previousMillis = millis();
    for(i=0; i< NUM_LEDS; i++) {
      c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
      leds[i].setRGB(*c,*(c+1), *(c+2));
    }
    FastLED.show();
    }
  }
}

Der Farbübergang ist dann nicht mehr smooth sondern springt eher.
Ich glaube es liegt daran, dass in der ersten for-Schleife zum nächsten Index gesprungen wird, falls die if-Bedingung nicht erfüllt ist. Stimmt das und falls ja, wie kann ich das verhindern bzw. wie kann ich dort der Funktion sagen, dass gewartet werden soll bis die if-Bedingung eintritt?
Hoffe mir kann jemand diesbezüglich helfen! Danke schon mal!

Gruß,
crossfirex

Ein einfacher Ersatz von delay() durch millis() ist nicht möglich.

Du wirst die For Schleife ausrollen müssen.

Leg mal previousMillis = millis(); nach FastLED.show();.

Du machst da ja einiges in der Verarbeitungsroutine, das frisst ja auch Zeit, also würde ich die neue Vergleichszeit auch erst hinterher puffern. delay() läuft ja auch erst hinterher los.

Hallo,

das Grundschema lautet immer so, oder so in der Art, kann man noch mit anderen Variablen abändern.
Wenn die "Verzögerungszeit" intervall erreicht ist, dann mache dies und jenes und aktualisiere den Zeitmerker.
Ansonsten ignoriere all das da drin

unsigned long lastTime;
unsigned long interval = 2000;  // 2sec "delay"

if (millis() - lastTime > interval )  {
  digitalWrite( ... );    // mach sinnvolle Dinge
  ...
  lastTime = millis();    // Merker aktualisieren          
}

Jetzt kannste das auf deinen Fall anwenden.

Edit:
wegen deiner for Schleife, kannst ja mal hier schauen, da zeigte ich das an einem Bsp.
http://forum.arduino.cc/index.php?topic=378551

1.) In der Funktion erst überprüfen ob die Zeit abgelaufen ist und wenn nicht gleich mit return verlassen
2.) Wenn was zu tun ist nur einen Schritt machen

So ist man nicht ständig in der Funktion, sondern kann zwischendurch andere Dinge tun.

if (millis()-previousMillis>speeddelay ) {
   previousMillis += speeddelay;
   ...
}

so kannst du beliebig lange rumtrödeln bis zum nächsten Mal

Danke euch allen für die Antworten! Das ging ja super schnell!
Habe jetzt mal den Lösungsvorschlag von Doc_Arduino ausprobiert und es funktioniert tadellos!

So sieht der Code jetzt aus:

void regenbogenmodus_laufend(int speeddelay)
{
  byte *c;
  uint16_t i, j;
  for(j=0; j<256;) { 
   if (millis()-previousMillis>speeddelay){
    for(i=0; i< NUM_LEDS; i++) {
      c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
      leds[i].setRGB(*c,*(c+1), *(c+2));
    }
    FastLED.show();
    j++;
    previousMillis = millis();
    }
  }
}

Unterschied ist jetzt der, dass der Index j der ersten Schleife erst dann hochgezählt wird, wenn die if-Bedingung eingetreten ist. GLeichzeitig hab ich previousMillis = millis(); weiter nach unten gesetzt.
Vielen Dank!

Gruß,
crossfirex

Hallo,

du bist jedoch noch nicht fertig mit der Aufgabe delay durch millis zu ersetzen.
Im Grunde hat sich an der Funktion "regenbogenmodus" nichts geändert.
Sie ist bei Aufruf für den Rest von Sketch weiterhin blockierend.
Deine Änderung zeigt noch keine Wirkung für den gesamten Sketch.
Außer das die innere for Schleife jetzt mehrfach "ohne Ende" aufgerufen ohne etwas zu tun bis das millis intervall erreicht ist. Weil erst wenn die Bedingung j erfüllt ist, wird die for Schleife beendet. Solange befindet sich der Sketch in der Funktion und kann nichts anderes abarbeiten.

Deswegen habe ich die for Schleife im verlinkten Bsp. ganz am Ende (#21) durch if ersetzt und rufe die Funktion immer wieder auf. Ob diese dann was tut oder nicht, weis sie selbst an Hand der millis Zeit.

Ja, du hast Recht. Meine Eingaben werden zwar aufgezeichnet, aber erst nach Schleifendurchlauf verarbeitet. Ich werd es mal, so wie du es gemacht hast, ausprobieren.

Schöne Grüße

Hallo,

die Codekomplexität nimmt leider etwas zu. Damit muß man leben lernen. Wenn man das Grundprinzip verinnerlicht hat, erkennt man auch das und kann es leichter umsetzen. Wird mit der Zeit immer leichter. :slight_smile:

Wenn in deinem Programm noch mehr dazu kommen soll, wäre es vielleicht auch nicht falsch sich mit der Codestruktur eines Zustandsautomaten zu befassen. Der Begriff klingt nur etwas blöd. Da wird der Code im Grunde ohne Ende durchlaufen und reagiert auf Zustandsänderungen, falls was eintritt, wird darauf reagiert. Sonst eben nicht.

Namen sind dafür wie gesagt, Zustandsautomat oder state maschine oder weis der Geier. Mit der Suche, auch über Google, sollte man auf Threads stoßen von jurs oder agmue und vielleicht andere.

Ob das passen könnte kannst nur du abschätzen. Mal anschauen kann nicht schaden. Der Code bekommt damit auch mehr Struktur bzw. eine Linie rein, falls es mehr wird. Kommt immer darauf was alles gemacht werden soll.

Hallo,

danke für die Erklärung! Hab mich mal ein wenig über Zustandsautomaten schlau gemacht.
So ähnlich hatte ich es auch schon implementiert. Bei mir wird eine Variable über Bluetooth übergeben, die der Arduino ausliest. Diese Variable wird über if-else verglichen (switch-case wäre hier wahrscheinlich übersichtlicher) und je nach Inhalt der Variable wird eine andere Beleuchtungsfunktion aufgerufen.
So hab ich im loop() nur den Aufruf der Funktion, um die Variable zu empfangen, und den Vergleich dieser.

Gruß,
crossfirex