Wie bekomme ich bei einer RGB-LED einen kurzen Farbwechsel von Rot auf Orange hin?

Moin zusammen,

ich bin absoluter Rookie, habe bisher null Erfahrung mit der Programmierung im Allgemeinen und dem Arduino im Besonderen.

Aufgrund der Möglichkeiten, die der Arduino bietet, habe ich allerdings Blut geleckt und möchte mein Projekt auf diesem Wege realisieren.

Ich möchte auf einer Grafik an der Spitze einer Zigarette eine rote LED permanent Glimmen haben und alle 30 Sekunden soll sie aufleuchten, als ob jemand daran zieht. Diesen Teil des Sketches habe ich mit Studium an der YouTube-Uni soweit hinbekommen. Dann hatte ich Lust auf mehr und es soll nach dem Herunterfaden der LED ein Rauchstoß aus dem Mund in der Grafikkommen. Den Smoker habe ich mit einer weißen LED simuliert, ob ich den dann direkt da anschließe oder mittels eines Relais‘ wird sich zeigen. Auch diese Implementierung habe ich hinbekommen. Zur Erläuterung: Ich habe noch keine Hardware und probiere aktuell noch alles am Arduino-Simulator von tinkercad aus. dies ist mein Sketch, der das richtige Verhalten der roten LED steuert und das kurze Auslösen des Smokers.

int RGBgreen=3;

int RGBred=5;

int Smoke=13;

int helligkeit= 30;

int fadeschritte= 5;

void setup()

{

pinMode(Smoke, OUTPUT);

pinMode(RGBred, OUTPUT);

pinMode(RGBgreen, OUTPUT);

}

void loop()

{

analogWrite(RGBred, helligkeit);

helligkeit = helligkeit + fadeschritte;

delay(30);

if (helligkeit == 250)

delay (1500);

if (helligkeit == 30 || helligkeit == 255){

fadeschritte = -fadeschritte;

}

if (helligkeit == 30)

{

delay (1500);

digitalWrite(Smoke, HIGH);

delay (3000);

digitalWrite (Smoke, LOW);

delay (23000);

}

}

Jetzt kommen wir zum Problem und meiner Frage: Zur weiteren Optimierung der Schaltung fände ich es toll, wenn sich gegen Ende des Aufblendens der LED die Farbe etwas in Richtung Orange verschiebt und das darauffolgende Glimmen wieder ein reines Rot ist.

Ich habe die rote LED gegen eine RGB ausgetauscht und viel im Internet nach Tipps gesucht, den gewünschten Effekt bekomme ich aber nicht hin. Ich habe Anleitungen gefunden, von einer Farbe zur anderen durchs gesamte Farbspektrum zu faden, da habe ich aufgrund der Komplexität des Programms aber nicht den kleinen Teil gefunden, der mir helfen würde. Auch hier im Forum habe ich mit der Suche nach „LED Farbe Faden“ nur Beiträge gefunden, die mein Problem (meiner Amateurmeinung nach) nicht behandeln.

Ich habe nach dem Aufblenden versucht, kurz hart auf Orange (250, 40, 0, korrekt?) zu wechseln, damit bekomme ich aber nur ein oranges Blinken hin und kein ruhiges Leuchten - außerdem fände ich es viel schöner, wenn der Farbwechsel auch gefadet würde.

Dies ist der Sketch, der den ‚falschen' Orangewechsel beinhaltet.

int RGBgreen=3;
int RGBred=5;
int Smoke=13;
int helligkeit= 30; 
int fadeschritte= 5;
int brightness1a = 250;
int brightness1b = 30;

void setup()

{
pinMode(Smoke, OUTPUT);
pinMode(RGBred, OUTPUT);
pinMode(RGBgreen, OUTPUT);
}

void loop()
{
analogWrite(RGBred, helligkeit);
helligkeit = helligkeit + fadeschritte;
delay(30);
if (helligkeit == 240)
{
analogWrite(RGBred, brightness1a);
analogWrite(RGBgreen, brightness1b);
delay (1500);
analogWrite (RGBgreen, 0);
}
if (helligkeit == 30 || helligkeit == 255){
fadeschritte = -fadeschritte;
}
if (helligkeit == 30)
{
delay (1500);
digitalWrite(Smoke, HIGH);
delay (3000);
digitalWrite (Smoke, LOW);
delay (23000);
}
}

Ich hoffe mir kann einer aus dem Schwarmwissen erläutern, wie ich die kurze Blende auf Orange hinbekomme.

Dies wird mein Setup

Es ist so eine Sache mit dem menschlichen Farbempfinden. Außer RGB gibt es weitere Farbschemata, die dem menschlichen Empfinden von Mischfarben besser angenähert sind (HSV...). Vielleicht findest Du eine Bibliothek die Deinen Wünschen besser entspricht. Dann läßt Du den Farbverlauf in RGB Anteile umrechnen und auf der RGB LED ausgeben, mit einem kurzen Delay zwischen den einzelnen Schritten.

Ansonsten mußt Du die einzelnen RGB Werte selbst herausfinden, so wie sie Dir am besten erscheinen, und sie der Reihe nach ausgeben.

Kannst Du mal das ausführen und mir sagen, ob es so ungefähr das ist, was Du willst?
[edit:] neue Zeile 49 eingefügt - Siehe #7[/edit]


const byte rotPin = 5;
const byte gruenPin = 3;
unsigned long lastmillis = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
}

void loop()
{
  if (millis() - lastmillis > 2)
  {
    nextRgb();
    lastmillis = millis();
  }
}

void nextRgb()
{
  const unsigned long dunkelPause = 3000;
  const unsigned long hellPause = 1000;
  static unsigned long lastmillis = 0;
  const byte minRot = 30;
  const byte minGruen = 0;
  static bool heller = true;
  static byte hellWert = minRot;
  if ((millis() - lastmillis > dunkelPause) && heller)
  {
    hellWert++;
    if (hellWert > 254)
    {
      heller = false;
      lastmillis = millis();
    }
  }
  if ((millis() - lastmillis > hellPause) && !heller)
  {
    hellWert--;
    if (hellWert < minRot)
    {
      hellWert = minRot;
      heller = true;
      lastmillis = millis();
    }
  }
  Serial.print("R: "); Serial.print(hellWert);
  analogWrite(rotPin, hellWert);
  if (hellWert > 180)     // Hier ist der Einsatz, ab wann der Farbraum verändert wird
  {
    analogWrite(gruenPin, hellWert / 5); // HIer die Intensität verändern
    Serial.print("\tG: "); Serial.print(hellWert / 5);
  }
  else analogWrite(gruenPin, minGruen);
  Serial.println();
}

ich hab mal folgendes probiert:

/*
  Zigarette
  https://forum.arduino.cc/t/wie-bekomme-ich-bei-einer-rgb-led-einen-kurzen-farbwechsel-von-rot-auf-orange-hin/958449/3

  by noiasca
  2022-02-22
*/

#include <Noiasca_led.h>               // download library from https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm
#include <Noiasca_timer.h>

FlickerPin ledA {9};                  // rot UNO PWM pins 3, 5, 6, 9, 10, 11
SmoothPin ledB {9};                   // noch mal der rote PIN zum auf faden
SmoothPin ledC {10};                  // grün UNO PWM pins 3, 5, 6, 9, 10, 11

LittleTimer littleTimer {10000};      // ein timer alle n Millisekunden auslösen

void setup() {
  Serial.begin(115200);
  ledA.begin();   // you have to call the .begin() method for the LED pair
  ledB.begin();   
  ledC.begin();      
  ledB.off();     // die LEDs zum auffaden am Anfang abschalten
  ledC.off();
  ledA.setMaxBrightness(30);  // rot flicker beschränken
  ledB.setOnInterval(20); 
  ledB.setOffInterval(10);
  ledC.setOnInterval(20);     // you can modify the delay time for the smothing
  ledC.setOffInterval(10);    // you can modify the delay time for the smothing
  ledC.setMaxBrightness(20);  // grün nur ganz wenig beimischen (max 255)
}

void loop() {
  ledA.update();   // you have to call update() for the LED
  ledB.update();
  ledC.update();

  static byte state = 0;
  if (littleTimer.hasTriggered())  // if the defined time is over, the timer will execude the followin lines
  {
    Serial.println(F("timer has triggered"));
    if (state == 0)
    {
      ledB.on();
      ledC.on();
      littleTimer.setInterval(3000);
      state = 1;
    }
    else
    {
      ledB.off();
      ledC.off();
      littleTimer.setInterval(10000);
      state = 0;
    }
  }
}

rot flackert so leicht dahin. Alle 10 Sekunden faded rot stärker auf.
vom Grün brauch ich nur ganz wenig. Kann aber auch an meinen Vorwiderständen liegen, die habe ich eher damals "äugisch" ausgewählt dass bei Voll ein alle Farben gefühlt gleich stark leuchten und Weis durchwegs weis bleibt.
Die Pins musst du für dich anpassen, bei mir ist es halt 9 und 10 gewesen.

Dazu brauchst du die Library von meiner Homepage.

@my_xy_projekt ... habs nur mal kurz ausprobiert. Imho schaltest du rot gar nicht ein. Das runter dimmen wäre mir "zu schnell". Das was bei mir vieleicht zu viel flackert, wäre mir bei dir zu "klinisch" ein/aus. Aber mal sehen was der TO dazu sagt.

Mein SerMon meinte ich fange Rot mit 30 an und ziehe nach oben bis 255 - um dann nach 1000ms wieder nach unten zu gehen.

Es war ja nur nen Versuch; das delay() hat mich extrem gestört :wink:

Nochmal laufen gelassen:

20:50:02.468 -> R: 30
20:50:02.468 -> R: 30
20:50:02.468 -> R: 31
[...]
20:50:02.817 -> R: 177
20:50:02.817 -> R: 178
20:50:02.817 -> R: 179
20:50:02.817 -> R: 180
20:50:02.817 -> R: 181	G: 36
20:50:02.817 -> R: 182	G: 36
20:50:02.817 -> R: 183	G: 36
20:50:02.850 -> R: 184	G: 36
20:50:02.850 -> R: 185	G: 37
20:50:02.850 -> R: 186	G: 37
20:50:02.850 -> R: 187	G: 37
20:50:02.850 -> R: 188	G: 37
[...]
20:50:03.016 -> R: 248	G: 49
20:50:03.049 -> R: 249	G: 49
20:50:03.049 -> R: 250	G: 50
20:50:03.049 -> R: 251	G: 50
20:50:03.049 -> R: 252	G: 50
20:50:03.049 -> R: 253	G: 50
20:50:03.049 -> R: 254	G: 50
20:50:03.049 -> R: 255	G: 51
20:50:03.049 -> R: 255	G: 51
20:50:03.049 -> R: 255	G: 51
20:50:03.049 -> R: 255	G: 51
20:50:03.049 -> R: 255	G: 51
20:50:03.082 -> R: 255	G: 51
20:50:03.082 -> R: 255	G: 51
[...]
20:50:04.043 -> R: 255	G: 51
20:50:04.043 -> R: 255	G: 51
20:50:04.043 -> R: 254	G: 50
20:50:04.043 -> R: 253	G: 50
20:50:04.076 -> R: 252	G: 50
20:50:04.076 -> R: 251	G: 50
20:50:04.076 -> R: 250	G: 50
20:50:04.076 -> R: 249	G: 49
20:50:04.076 -> R: 248	G: 49
20:50:04.076 -> R: 247	G: 49
20:50:04.076 -> R: 246	G: 49
20:50:04.076 -> R: 245	G: 49
20:50:04.076 -> R: 244	G: 48
20:50:04.076 -> R: 243	G: 48
20:50:04.076 -> R: 242	G: 48
20:50:04.111 -> R: 241	G: 48
20:50:04.111 -> R: 240	G: 48
20:50:04.111 -> R: 239	G: 47
20:50:04.111 -> R: 238	G: 47

Hab ich irgendwas übersehen?

ich schick dir ein

analogWrite(rotPin

für copy/paste falls eine (mehrere?) deiner Tasten klemmen :wink:

1 Like

Ach ne, nicht copy und paste - da ist in Zeile 48/49 die Ausgabe weg.

Danke!

Zunächst schon einmal vielen Dank dass Ihr Euch so ausführlich einem Anfänger widmet!

@ DrDiettrich: Dass es noch andere Farbräume als RGB gibt, ist schon klar. Da aber eine RGB-LED nur RGB kann und Orange doch immer eine Kombination aus Rot und Grün ist, macht es m.E. wenig Sinn, in einem anderen Farbraum nach dem ‚richtigen‘ Orange zu suchen und es dann in RGB umwandeln zu lassen. Das ist auch wieder bloß eine Näherung und da kann ich doch direkt mit RGB herumexperimentieren.

@my_xy_projekt: Ja, das ist schon in etwa, was ich mir vorgestellt habe. Nur der harte Übergang von Rot > Orange ähnelt noch sehr meinem Umschalten, den fände ich weicher schöner. Und schon habe ich wieder das Problem, dass ich Deinen Sketch nicht wirklich verstehe und darum nicht weiß, an welcher Stelle ich das modifizieren müsste, um den Effekt zu ändern. Eine grundsätzliche Frage (wenn die nicht den Rahmen hier sprengt): Warum nutzt Du millis anstatt delay? Deine persönliche Präferenz oder hat das auch für mich nachvollziehbare Vorteile?

Was ich aus Bereichen kenne, in denen ich selber etwas mehr weiß als viele, schlägt auch hier wieder erbarmungslos zu. Ein Anfänger hat an Stellen Probleme, an die ein versierter Nutzer nicht im Traum denkt.

@noiasca: Danke für Deinen Input und den Sketch. In Deiner Library sind tolle Sachen, die ich sicher auch sonst gerne mal verwenden werde. Da ich bisher nur im tinkercad-Simulator arbeite und Arduino IDE noch gar nicht auf dem Rechner hatte, habe ich das installiert um Deine Bibliothek überhaupt einbinden zu können. Jetzt habe ich erstmal noch das Problem, dass ich die Library bzw. den Sketch mit implementierter Library nicht in das Simulationsprogramm bekomme. Von daher bitte ich um etwas Geduld, bis ich ein Feedback zu Deinem Vorschlag gebe. Schweigen ist kein Desinteresse meinerseits, nur Kampf mit der Materie.

1 Like

@cptblaubaer
imho kannst Simulatoren dafür vergessen. Das musst in echter Hardware bauen um die Farbmischung sehen zu können:

/*
  Zigarette
  https://forum.arduino.cc/t/wie-bekomme-ich-bei-einer-rgb-led-einen-kurzen-farbwechsel-von-rot-auf-orange-hin/958449/3

  by noiasca
  2022-02-22
*/

#include "Noiasca_led.h"              // download library from https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm
#include "Noiasca_timer.h"

FlickerPin ledA {9};                  // rot UNO PWM pins 3, 5, 6, 9, 10, 11
SmoothPin ledB {9};                   // noch mal der rote PIN zum auf faden
SmoothPin ledC {10};                  // grün UNO PWM pins 3, 5, 6, 9, 10, 11

LittleTimer littleTimer {10000};      // ein timer alle n Millisekunden auslösen

void setup() {
  Serial.begin(115200);
  ledA.begin();   // you have to call the .begin() method for the LED pair
  ledB.begin();   
  ledC.begin();      
  ledB.off();     // die LEDs zum auffaden am Anfang abschalten
  ledC.off();
  ledA.setMaxBrightness(30);  // rot flicker beschränken
  ledB.setOnInterval(20); 
  ledB.setOffInterval(10);
  ledC.setOnInterval(20);     // you can modify the delay time for the smothing
  ledC.setOffInterval(10);    // you can modify the delay time for the smothing
  ledC.setMaxBrightness(20);  // grün nur ganz wenig beimischen (max 255)
}

void loop() {
  ledA.update();   // you have to call update() for the LED
  ledB.update();
  ledC.update();

  static byte state = 0;
  if (littleTimer.hasTriggered())  // if the defined time is over, the timer will execude the followin lines
  {
    Serial.println(F("timer has triggered"));
    if (state == 0)
    {
      ledB.on();
      ledC.on();
      littleTimer.setInterval(3000);
      state = 1;
    }
    else
    {
      ledB.off();
      ledC.off();
      littleTimer.setInterval(10000);
      state = 0;
    }
  }
}

Wenn da irgendwo ein delay drin ist, kannst Du in der Zeit nichts weiter machen.
Derzeit gehe ich alle 2ms und stelle den Rot-Wert um 1 weiter.
In der Restzeit kannst Du irgendwas anderes machen, z.B. mit den Augen rollen. Während der reinen RotZeit kannst Du dann Nebel machen etc...
Spätestens wenn Du 2 Dinge scheinbar gleichzeitig machen wilst, kommst Du um millis() nicht drumrum.

Ich fange erst von Rot nach Orange an, wenn rot den (hellWert>180) hat.
Du könntest dort früher anfangen, z.B. bei 90. Das musst Du Dir ggfls. empirisch ermitteln. Ich habe keine rgb-LED :wink:

Da hast Du sicher grundsätzlich Recht. Ich stecke aber in der Reha und bin froh, hier auf diesem Wege via Simulator überhaupt einen initialen Zugang zur Materie zu bekommen.
Ich bin jetzt so weit angefixt, dass ich zu Hause auf jeden Fall mit Hardware 'richtig' experimentieren werde. Dass man sich Farbmischungen und Übergänge live anschauen muss um zu entscheiden, was die finale Version wird, ist klar.

Während der reinen RotZeit kannst Du dann Nebel machen etc...
Spätestens wenn Du 2 Dinge scheinbar gleichzeitig machen wilst, kommst Du um millis() nicht drumrum.

Das macht natürlich viel Sinn. Ich dachte, man könne das mit Splitten der Delays erreichen, war aber bisher eher unbefriedigend.

Viel Erfolg dabei!

Nö, sie kann nur Additive Farbmischung, die durch den HSV-Farbraum beschrieben wird. Dessen Regenbogenfarben über den Farbwinkel sollten sich m. E. bestens für Dein Vorhaben eignen.

Die Bibliothek FastLED ist für Dein Vorhaben dann wieder etwas heftig, andererseits kannst Du Dir die notwendigen Werte möglicherweise abschauen.

Ich habe Dir mal ein kleines Programm geschrieben, um Dich von HSV zu überzeugen. Da ich keine RGB-LED habe, verwende ich zusätzlich einen LED-Pixel vom Typ WS2812B mit eingebautem Controller.

#include <FastLED.h>

const byte REDPIN   = 5;
const byte GREENPIN = 3;
const byte BLUEPIN  = 6;
const byte SMOKEPIN = 13;

#define DATA_PIN 4
#define NUM_LEDS 9
CRGB leds[NUM_LEDS];

void showAnalogRGB( const CRGB& rgb)
{
  analogWrite(REDPIN,   rgb.r );
  analogWrite(GREENPIN, rgb.g );
  analogWrite(BLUEPIN,  rgb.b );
  leds[0] = rgb;
  FastLED.show();
}

void setup() {
  FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);  // GRB ordering is typical
  FastLED.show();
  pinMode(REDPIN,   OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN,  OUTPUT);
  pinMode(SMOKEPIN, OUTPUT);
}

void loop()
{
  glut();
  rauch();
}

void glut() {
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  const uint32_t intervall = 20;
  static uint8_t hue;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    hue = hue + 1;
    // Use FastLED automatic HSV->RGB conversion
    showAnalogRGB( CHSV( hue, 255, 255) );
  }
}

void rauch()
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t intervall = 0;
  static bool ausEIN = false;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    if (ausEIN)
    {
      intervall = 100;
    } else {
      intervall = 2000;
    }
    digitalWrite(SMOKEPIN, ausEIN);
    ausEIN = !ausEIN;
  }
}

Das Programm fadet hin und her durch den Regenbogen und läßt den Rauch mal kurz blinken. Das könntest Du zum Glimmstengel umbauen, wenn Dir meine Anregung zusagt.

Getestet mit UNO, drei LEDs anstelle RGB-LED und einem von neun NeoPixeln.

Danke nochmal für Eure Inputs. Ich versuche geradezu verstehen, wie ich in meinem Sketch delay() durch millis() ersetze, da sind schon ein paar Stündchen drauf gegangen. Wenn ich die Zusammenhänge richtig kapiert habe, ist das die einzige Chance, zwei Farben einer RGB-LED gleichzeitig anzusteuern und das ist ja mein grundsätzliches Anliegen gewesen.

In welchem Farbraum ich welches Orange auswähle und auch das Probieren der freundlicherweise hier geposteten diversen Sketche mit Einbindung von Libraries werde ich dann im März zu Hause am lebenden Objekt probieren und hier kommentieren.

Zunächst mal hoffe ich, dass ich jetzt bald verstehe, wie ich in das mit den millis() hinbekomme um in meinem Sketch am Ende der Aufblendphase von Rot ein wenig Grün dazuzugeben. Das wäre mir schon Erfolg genug.

Vielleicht hilft es beim Verständnis, dass nicht delay() durch millis() ersetzt wird, sondern dass dadurch loop() etwas komplett anderes wird.
loop beschreibt danach nicht mehr einen zeitlichen Ablauf, sondern nur einen Moment.
Ein loop-Durchlauf dauert keine Zeit, sondern kommt unendlich oft dran.
Und ja, das zu verstehen ist deine einzige Chance :slight_smile:

Das geht durchaus, wenn Du als Zeiteinheit das kleinste gemeinsame Vielfache verwendest. Beispiel: Zeit1 = 12 s, Zeit2 = 16 s, kgV = 4, also schaust Du alle 4 s, ob Zähler1 den Wert 3 oder Zähler2 den Wert 4 erreicht hat, dann machst Du mit Led1 oder Led2 etwas. Das kann ich Dir programmieren, denn so habe ich mit Arduino angefangen.

Bei mehreren Zeiten wird das dann ganz schnell unflexibel. Also verwendet man kgV = 1 und macht was, wenn der zugehörige Zähler 12 oder 16 erreicht hat. Bei kürzeren Zeiten bietet sich dann eine Veränderung der Zähler bei 1 ms an, also delay(1).

Letztlich führt Dich das in eine Sackgasse, aber es war mein Umweg zum Verständnis von millis(). Wenn Du Zeit hast, spiele einfach mal mit diesen Überlegungen. Das geht auch in der Simulation :slightly_smiling_face:

Nein, viele Wege führen zum Ziel, beispielsweise:

  1. Die Arduinos haben Timer in Hardware eingebaut, die Du direkt verwenden kannst. Da muß man sich tief in das Datenblatt des µC einarbeiten. delay() und millis() basieren auch darauf.
  2. Es gibt Bibliotheken, die Dir das abnehmen. Beispielsweise bieten Dir die MobaTools Zeitgeber an.
  3. Andere µCs bieten Dir mehrere Kerne an, um Dinge getrennt abhandeln zu können. Bei besonders zeitkritischen Aufgabenstellungen kann das durchaus sinnvoll sein.
  4. Und auch mit delay() kann man Deine Aufgabe lösen, wenn der Arduino sonst nichts machen soll, sonst eine Sackgasse.

Dein Therapeut sagt: "Wir treffen uns in zehn Minuten!", Du schaust auf Deine Uhr, addierst zu der bestehenden Zeit die zehn Minuten und merkst Dir beispielsweise 13:20 Uhr. Zeit zum Händewaschen, Blick zur Uhr, noch nicht 13:20 Uhr, Keks essen, Blick zur Uhr, noch nicht 13:20 Uhr, Schluck trinken, Blick zur Uhr, noch nicht 13:20 Uhr, Haare kämmen, Blick zur Uhr, es ist 13:20 Uhr, auf zum Therapeuten, der vor der Tür wartet, viel Erfolg!

Denksportaufgaben:

  1. Deine Uhr ist heute um Mitternacht auf 0:00 Uhr gewechselt, wärst Du auch pünktlich gewesen, wenn Deine Uhr vor oder nach ginge?
  2. Wärst Du auch pünktlich gewesen, wenn Deine Uhr nur die Minuten seit dem Aufstehen zählen würde?

Dein Arduino zählt die Millisekunden seit Reset, also seit seinem Aufstehen. Der Zahlenbereich von millis() umfaßt 2³² Millisekunden. Diesen Überlauf kann man durch if (aktuelleZeit - gemerkteZeit >= zeitIntervall unproblematisch passieren. Also noch mal mit überlaufsicherer Zeitdifferenz:

Dein Therapeut sagt: "Wir treffen uns in zehn Minuten!", Du schaust auf Deine Uhr und merkst Dir die aktuelle Uhrzeit, beispielsweise 13:10 Uhr. Zeit zum Händewaschen, Blick zur Uhr, Du berechnest die Differenz zwischen aktueller und gemerkter Zeit, noch nicht 10 Minuten, Keks essen, Blick zur Uhr, Du berechnest die Differenz zwischen aktueller und gemerkter Zeit, noch nicht 10 Minuten, Schluck trinken, Blick zur Uhr, Du berechnest die Differenz zwischen aktueller und gemerkter Zeit, noch nicht 10 Minuten, Haare kämmen, Blick zur Uhr, Du berechnest die Differenz zwischen aktueller und gemerkter Zeit, es sind 10 Minuten, auf zum Therapeuten, der vor der Tür wartet, viel Erfolg!

Du kannst FastLED die Farbwerte berechnen lassen. Anstelle 0 bis 360 Grad für den Winkel nutzt FastLED 0 bis 255, also einen Wert vom Typ byte. Rot ist 0, Gelb ist 64. Ich habe mein Programm mal etwas umgeschrieben, damit die RGB-LED zwischen Rot und Gelb pendelt:

#include <FastLED.h>

const byte REDPIN   = 5;
const byte GREENPIN = 3;
const byte BLUEPIN  = 6;
const byte SMOKEPIN = 13;

#define DATA_PIN 4
#define NUM_LEDS 9
CRGB leds[NUM_LEDS];

void showAnalogRGB( const CRGB& rgb )
{
  analogWrite(REDPIN,   rgb.r );
  analogWrite(GREENPIN, rgb.g );
  analogWrite(BLUEPIN,  rgb.b );
  leds[0] = rgb;
  FastLED.show();
}

void ausgabeRGB( const CRGB& rgb, const uint8_t hue )
{
  static uint8_t alt_hue = hue;
  static bool ausgabe = true;

  if (hue < alt_hue) ausgabe = false;
  if (ausgabe)
  {
    char buf[100] = {'\0'};
    snprintf(buf, sizeof(buf), "hue=%d\tR=%d\tG=%d\tB=%d\n", hue, rgb.r, rgb.g, rgb.b);
    Serial.print(buf);
  }
  alt_hue = hue;
}

void setup() {
  Serial.begin(9600);
  Serial.println("\nStart");
  FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);  // GRB ordering is typical
  FastLED.show();
  pinMode(REDPIN,   OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN,  OUTPUT);
  pinMode(SMOKEPIN, OUTPUT);
}

void loop()
{
  glut();
  rauch();
}

void glut() {
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  const uint32_t intervall = 20;
  const uint8_t FARBE_MAX = 64;
  const uint8_t FARBE_MIN = 0;
  static uint8_t hue = FARBE_MIN;
  static int8_t delta = 0;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    hue = hue + delta;
    if (hue >= FARBE_MAX) delta = -1;
    if (hue <= FARBE_MIN) delta = 1;
    // Use FastLED automatic HSV->RGB conversion
    showAnalogRGB( CHSV( hue, 255, 255) );
    ausgabeRGB( CHSV( hue, 255, 255), hue );
  }
}

void rauch()
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t intervall = 0;
  static bool ausEIN = false;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    if (ausEIN)
    {
      intervall = 100;
    } else {
      intervall = 2000;
    }
    digitalWrite(SMOKEPIN, ausEIN);
    ausEIN = !ausEIN;
  }
}

Der serielle Monitor zeigt dann:

Start
hue=0 R=255 G=0 B=0
hue=1 R=253 G=2 B=0
hue=2 R=250 G=5 B=0
hue=3 R=247 G=8 B=0
hue=4 R=245 G=10 B=0
hue=5 R=242 G=13 B=0
hue=6 R=239 G=16 B=0
hue=7 R=237 G=18 B=0
hue=8 R=234 G=21 B=0
hue=9 R=231 G=24 B=0
hue=10 R=229 G=26 B=0
hue=11 R=226 G=29 B=0
hue=12 R=223 G=32 B=0
hue=13 R=221 G=34 B=0
hue=14 R=218 G=37 B=0
hue=15 R=215 G=40 B=0
hue=16 R=212 G=43 B=0
hue=17 R=210 G=45 B=0
hue=18 R=207 G=48 B=0
hue=19 R=204 G=51 B=0
hue=20 R=202 G=53 B=0
hue=21 R=199 G=56 B=0
hue=22 R=196 G=59 B=0
hue=23 R=194 G=61 B=0
hue=24 R=191 G=64 B=0
hue=25 R=188 G=67 B=0
hue=26 R=186 G=69 B=0
hue=27 R=183 G=72 B=0
hue=28 R=180 G=75 B=0
hue=29 R=178 G=77 B=0
hue=30 R=175 G=80 B=0
hue=31 R=172 G=83 B=0
hue=32 R=171 G=85 B=0
hue=33 R=171 G=87 B=0
hue=34 R=171 G=90 B=0
hue=35 R=171 G=93 B=0
hue=36 R=171 G=95 B=0
hue=37 R=171 G=98 B=0
hue=38 R=171 G=101 B=0
hue=39 R=171 G=103 B=0
hue=40 R=171 G=106 B=0
hue=41 R=171 G=109 B=0
hue=42 R=171 G=111 B=0
hue=43 R=171 G=114 B=0
hue=44 R=171 G=117 B=0
hue=45 R=171 G=119 B=0
hue=46 R=171 G=122 B=0
hue=47 R=171 G=125 B=0
hue=48 R=171 G=128 B=0
hue=49 R=171 G=130 B=0
hue=50 R=171 G=133 B=0
hue=51 R=171 G=136 B=0
hue=52 R=171 G=138 B=0
hue=53 R=171 G=141 B=0
hue=54 R=171 G=144 B=0
hue=55 R=171 G=146 B=0
hue=56 R=171 G=149 B=0
hue=57 R=171 G=152 B=0
hue=58 R=171 G=154 B=0
hue=59 R=171 G=157 B=0
hue=60 R=171 G=160 B=0
hue=61 R=171 G=162 B=0
hue=62 R=171 G=165 B=0
hue=63 R=171 G=168 B=0
hue=64 R=171 G=170 B=0

Die Werte für Rot und Grün kannst Du für Dein Programm übernehmen, bei meiner WS2812-LED sieht das ganz gut aus.

#include <FastLED.h>
#define DATA_PIN 4
#define NUM_PIX  9
CRGB pix[NUM_PIX];
// FastLED ENDE

struct Glutfarben
{
  const byte r, g;
};

Glutfarben glutfarben[] =
{
  //Rot, Grün
  {255, 0},
  {253, 2},
  {250, 5},
  {247, 8},
  {245, 10},
  {242, 13},
  {239, 16},
  {237, 18},
  {234, 21},
  {231, 24},
  {229, 26},
  {226, 29},
  {223, 32},
  {221, 34},
  {218, 37},
  {215, 40},
  {212, 43},
  {210, 45},
  {207, 48},
  {204, 51},
  {202, 53},
  {199, 56},
  {196, 59},
  {194, 61},
  {191, 64},
  {188, 67},
  {186, 69},
  {183, 72},
  {180, 75},
  {178, 77},
  {175, 80},
  {172, 83},
  {171, 85},
  {171, 87},
  {171, 90},
  {171, 93},
  {171, 95},
  {171, 98},
  {171, 101},
  {171, 103},
  {171, 106},
  {171, 109},
  {171, 111},
  {171, 114},
  {171, 117},
  {171, 119},
  {171, 122},
  {171, 125},
  {171, 128},
  {171, 130},
  {171, 133},
  {171, 136},
  {171, 138},
  {171, 141},
  {171, 144},
  {171, 146},
  {171, 149},
  {171, 152},
  {171, 154},
  {171, 157},
  {171, 160},
  {171, 162},
  {171, 165},
  {171, 168},
  {171, 170},
};

const byte REDPIN   = 5;
const byte GREENPIN = 3;
const byte SMOKEPIN = 13;


void setup() {
  // FastLED ANFANG
  FastLED.addLeds<WS2812, DATA_PIN, RGB>(pix, NUM_PIX);  // GRB ordering is typical
  FastLED.show();
  // FastLED ENDE

  pinMode(SMOKEPIN, OUTPUT);
}

void loop()
{
  glut();
  rauch();
}

void glut() {
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  const uint32_t intervall = 20;
  const uint8_t FARBE_MAX = 64;
  const uint8_t FARBE_MIN = 0;
  static uint8_t hue = FARBE_MIN;
  static int8_t delta = 0;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    hue = hue + delta;
    if (hue >= FARBE_MAX) delta = -1;
    if (hue <= FARBE_MIN) delta = 1;
    analogWrite(REDPIN,   glutfarben[hue].r );
    analogWrite(GREENPIN, glutfarben[hue].g );
    // FastLED ANFANG
    pix[0] = CHSV( hue, 255, 255);
    FastLED.show();
    // FastLED ENDE
  }
}

void rauch()
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t intervall = 0;
  static bool ausEIN = false;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    if (ausEIN)
    {
      intervall = 100;
    } else {
      intervall = 2000;
    }
    digitalWrite(SMOKEPIN, ausEIN);
    ausEIN = !ausEIN;
  }
}

Wenn Du die FastLED-Sachen rausnimmst, geht das auch gut in der Simulation :slightly_smiling_face:

@michael_x , @agmue : Danke, ich denke, den Unterschied zwischen millis() und delay() auf der Timeline hab ich schon gerafft (mein Problem ist, das dann auch in Programmiersprache zu fassen). Ich muss ja aber trotzdem meinen Sketch mit delay() in einen mit millis() wandeln, da war der Ausdruck 'durch millis() ersetzen' wohl etwas unglücklich gewählt. Mein Problem ist, die millis()-definierten Ereignisse in Programmiersprache zu schreiben.

Mit delay() definiere ich den Zeitraum bis zum nächsten Ereignis, was beim ein- und Ausschalten einer LED halt auch die Leuchtdauer ist und dies passiert unendlich lange im Loop. Verwende ich millis() als Basis meiner wiederkehrenden Ereignisse, muss ich für jedes Ereignis einen Punkt auf der Timeline definieren, den ich auch mit einer Differenz aus der Zeit zum verstrichenen Ereignis und der totalen Zeit seit Starten des Arduino definieren kann. Z.B. über sowas:

const unsigned long eventInterval = 1000;
unsigned long previousTime = 0;

void setup() {
}

void loop() {

  unsigned long currentTime = millis();

  if (currentTime - previousTime >= eventInterval) {
 
 digitalWrite(LED, HIGH);
  
    previousTime = currentTime;
  }
}

Wenn ich dann aber mehrere Ereignisse nacheinander wiederholen will, muss ich für jedes einen eigenen Part schreiben, korrekt?
Sprich, ich muss ein
const unsigned long eventInterval02 = 3000;
schreiben, wenn die LED nach 2 Sekunden wieder ausgehen soll und das so formulieren:

const unsigned long eventInterval = 3000;
unsigned long previousTime = 0;

void setup() {
}

void loop() {

  unsigned long currentTime = millis();

  if (currentTime - previousTime >= eventInterval) {
 
 digitalWrite(LED, LOW);
  
    previousTime = currentTime;
  }
}

auch richtig? Und das muss ich dann für jedes Ereignis in der Timeline wiederholen und das würde bei eingeschaltetem Arduino knappe 50 Tage funktionieren, aber nicht unendlich.
Da dann mit Eigenmitteln das Faden zu erstellen, ist für mich schwer. Aus einer Library einbinden, kann ich es dann, wen ich wieder zu Hause bin. Mein Faden oben funktioniert ja auch mit delay() zwischen den Fadeschritten. Das was @my_xy_projekt in seinem Sketch geschrieben hat, funktioniert ja schon so, wie ich's gerne hätte und auch auf der Basis der millis() Timeline. Da kapiere ich aber schon nicht, wieso die gedimmte Phase die gewünschten 30 Sekunden dauert, obwohl im Sketch 3000 steht. Ebensowenig verstehe ich (und finde auch im Sketch überhaupt nicht, woraus sich das ergibt) warum die Aufleuchtphase der LED mit Farbwechsel mit 28 Sekunden fast genauso lange dauert (ich würde den Zeitraum gerne auf runde 2 Sekunden verkürzen - länger zieht keiner an einer Zigarette, hab aber keinen Plan, wie ich das bewerkstellige).
Aktuell versuche ich gerade, in den Sketch von @my_xy_projekt mein Ein- und Ausschalten des Smokers zu integrieren (mit den oben zitierten const unsigned long eventInterval = 3000;
unsigned long previousTime = 0; seit Starten des Arduino), da scheitere ich bisher auch noch.

Du erkennst das Prinzip des Automaten (state machine) zur Behandlung solcher Abläufe.

Moooment.
Der Sketch aus #3 braucht für einen kompletten Umlauf 5 Sekunden. (Also von dunklem Rot bis hellem Rot mit Gruen und zurück bis dunkles Rot)

Ich hab den komplett kommentiert und einen Stop eingebaut, wenn ein Umlauf durch ist.

const byte rotPin = 5;
const byte gruenPin = 3;
unsigned long lastmillis = 0;                            // Diese lastmillis sind global - Merker für millis der letzten Aktion

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
}

void loop()
{
  if (millis() - lastmillis > 2)                         // Wenn Zeit abgelaufen ist...
  {
    nextRgb();                                           // ... rufe Funktion auf ...
    lastmillis = millis();                               // ... merke Zeit 
  }
}

void nextRgb()                                           // Diese Funktion wird nur aufgerufen, wenn oben die Zeit abgelaufen
{
  const unsigned long dunkelPause = 3000;                // Zeit in ms die der PIN LOW ist
  const unsigned long hellPause = 1000;                  // Zeit in ms die der PIN Dauerhaft HIGH ist
  static unsigned long lastmillis = 0;                   // Die Variable gilt nur lokal
  const byte minRot = 30;                                // kleinster Rot-Wert
  const byte minGruen = 0;                               // kleinster Gruen-Wert
  static bool heller = true;                             // diese Variable bestimmt die Richtung: ob heller(true) oder dunkler(false) werden soll
  static byte hellWert = minRot;                         // Startwert
  if ((millis() - lastmillis > dunkelPause) && heller)   // Wenn Dunkelpause abgelaufen UND die Richtung heller ist...
  {
    hellWert++;                                          // ... Increment ...
    if (hellWert > 254)                                  // ... Am oberen Ende? ...
    {
      heller = false;                                    // ... dann soll es dunkler werden ...
      lastmillis = millis();                             // ... merke die Zeit, wann es am hellsten war
    }
  }
  if ((millis() - lastmillis > hellPause) && !heller)    // Wenn Hellpause abgelaufen ist UND die Richtung dunkler ist...
  {
    hellWert--;                                          // ... decrement ...
    if (hellWert < minRot)                               // ... am unteren Ende? ...
    {
      hellWert = minRot;                                 // ... setze kleinsten Rotwert ...
      heller = true;                                     // ... wechsle Richtung ...
      lastmillis = millis();                             // ... merke Umschaltzeit
      Serial.print("STOP:"); Serial.println(millis());// Diese beiden Zeilen nur 
      while(1);                                       // zum Durchlauftest
    }
  }
  Serial.print("R: "); Serial.print(hellWert);
  analogWrite(rotPin, hellWert);                         // schreibe auf Pin
  if (hellWert > 180)                                    // Hier ist der Einsatz, ab wann der Farbraum verändert wird ...
  {
    analogWrite(gruenPin, hellWert / 5);                 // ... der Gruenwert errechnet sich aus dem Hellwert ...
    Serial.print("\tG: "); Serial.print(hellWert / 5);
  }
  else analogWrite(gruenPin, minGruen);                  // ... Wenn der Hellwert nicht erreicht, schreibe kleinsten GruenWert
  Serial.println();                                      // Und hier ist die Funkltion einmal durchlaufen
}                                                        // der nächste Durchlauf ist erst wieder, wenn im loop die Zeit abgelaufen ist

Ausgabe:

14:46:36.020 -> Start...
14:46:36.020 -> R: 30
14:46:36.053 -> R: 30
14:46:36.053 -> R: 30
14:46:36.053 -> R: 30
[...]
14:46:39.035 -> R: 30
14:46:39.035 -> R: 30
14:46:39.035 -> R: 31 <= Ab hier wird rot heller
14:46:39.035 -> R: 32
14:46:39.035 -> R: 33
[...]
14:46:39.498 -> R: 180
14:46:39.498 -> R: 181	G: 36 <= Ab hier kommt gruen dazu
14:46:39.498 -> R: 182	G: 36
[...]
|14:46:40.724 -> R: 255|G: 51| 
|14:46:40.724 -> R: 254|G: 50| <= hab hier wirds dunkler
[...]
14:46:40.956 -> R: 181	G: 36
14:46:40.956 -> R: 180  <= Hier ist gruen weg
14:46:40.956 -> R: 179
[...]
14:46:41.419 -> R: 30
14:46:41.419 -> STOP:5373 <= Hier ist der Umlauf zu Ende (Zeit in ms)

Ersetze das mal durch Schrittkette (=finite state machine, =endlicher Automat) und füge diese in Dein Programm ein:

const byte LED   = 5;
const byte SMOKEPIN = 13;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(SMOKEPIN, OUTPUT);
}

void loop()
{
  glut();
  rauch();
}

void glut() {
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t intervall = 0;
  static byte schritt = 0;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    switch (schritt)
    {
      case 0:
        digitalWrite(LED, HIGH);
        intervall = 1000;
        schritt = 1;
        break;
      case 1:
        digitalWrite(LED, LOW);
        intervall = 3000;
        schritt = 0;
        break;
    }
  }
}

void rauch()
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t intervall = 0;
  static bool ausEIN = false;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    if (ausEIN)
    {
      intervall = 100;
    } else {
      intervall = 2000;
    }
    digitalWrite(SMOKEPIN, ausEIN);
    ausEIN = !ausEIN;
  }
}

Du kannst beliebig Schritte ergänzen. Das Programm läuft, so lange der Arduino Strom bekommt, also auch deutlich mehr als 50 Tage.