FastLED Library- strukturiertes Vorgehen bei genauen Vorstellungen

Hallo,

ich würde gerne wie bereits in einem anderen Thread beschrieben ein Treppenlicht bauen.

Hierbei habe ich mich nun für die FastLED-Library entschieden und bin dabei mir zu überlegen, wie ich das Ganze strukturieren/aufbauen soll.

Das Ziel soll Folgendes sein:

Per PIR am oberen und unteren Ende der Treppe sollen sämtliche Stufen entweder von oben nach unten und/oder von unten nach oben sanft eingefadet werden.

Nach einer definierten Zeit sollen alle Stufen gemeinsam wieder ausfaden.

Nun zu den Unwägbarkeiten:

Ich möchte grundsätzlich zwei jeweils wählbare LED-Programme "fahren".

Das eine "Programm" soll laufen, wenn niemand einen PIR-Sensor angeregt hat.

Mögliche Programme hierbei:

"alle LEDs aus", "grünes Gewabber" (animiertes Programm für Halloween), "wildes Geflacker" (animierter Partymodus), "Glitter" (animiertes Programm für Sylvester)

Das andere Programm soll laufen, wenn ein PIR-Sensor angeregt wurde.

Mögliche Programme hierbei:

"weiß", "Farbe X", "Farbe Y", "Farbe Z"

Aufgrund dessen, dass es nun Zustände gibt, bei denen beide Programme gleichzeitig zu sehen sein werden (z.B. wenn ein- oder ausgefadet wird), könnte ich mir zum jetzigen Zeitpunkt gut vorstellen, dass es in diesem Fall sinnvoller sein dürfte im HSV-Farbspektrum "Rainbow" zu arbeiten und erst wenn es an die Ausgabe geht das Array dann nach RGB zu "wandeln".

Da ich die Dokumentation zur FastLED-Library als etwas schwierig zu durchschauen empfinde, bin ich nun auf der Suche nach Projekten, die bereits etwas Ähnlich beinhalten.

Ich möchte gerne gleich zu Beginn vom Aufbau her den richtigen Weg einschlagen und mich nicht verzetteln, obwohl bereits jmd. diese Problematik elegant für sich gelöst hat.

Insbesondere das Mischen der "Programme" stellt mich gedanklich vor eine Herausforderung.

Ganz konkret geht es mir um den Moment, wenn die LED-Streifen, nachdem sie alle an waren, ausfaden und genau in diesem Moment dann jmd. wieder die Treppe betritt.

Bin für jeden Tipp/ Hinweis dankbar.

Gruß Chris

Endlicher Automat zur Bestimmung der Beleuchtungsart.

Ich sehe es nicht sinnvoll die Fabmodi zu mischen. Bei Beleuchtung für Treppenbenutzung müssen die Stufen beleuchtet werden und nicht irgendwie herumflackern.

Grüße Uwe

Wenn du eine Variable(z.B. ein Byte) hast mit den Unterschiedlichen Programmen dann ist switch case genau das Richtige für dich dann musst du dich "nur" noch darum kümmern wann welches Programm gestartet werden soll. Also erstmal die einzelnen LED Animationen testen das sie trocken auch so funktionieren wie du das willst.
Dann z.B. mit einem Button die Animationen auswählen. Wenn das dann funktioniert kannst du dich um die Auswertung der PIRs kümmern.
Was passiert z.B. wenn einer von unten das "Programm startet" und oben dann einer in diesem Moment reinläuft.
Mit der Auswertung schreibst du dann die Programm Nummmer in deine Programm Variable und lässt sie dann in den Switch Case laufen der dir dann die richtige Animation abspielt.
Immer nur eine zusätzliche Sache implementieren um zu sehen ob es läuft.

Für Animationen schaue mal nach den Post von Helmuth ist sowas wie der FastLED-Grandmaster

Gruß
DerDani

Aufgrund dessen, dass es nun Zustände gibt, bei denen beide Programme gleichzeitig zu sehen sein werden (z.B. wenn ein- oder ausgefadet wird), könnte ich mir zum jetzigen Zeitpunkt gut vorstellen, dass es in diesem Fall sinnvoller sein dürfte im HSV-Farbspektrum "Rainbow" zu arbeiten und erst wenn es an die Ausgabe geht das Array dann nach RGB zu "wandeln".

Es läuft darauf hinaus, dass Du 2 Animationen zur gleichen Zeit berechnen musst. Das ist grundsätzlich kein Problem.

Ich würde die in 2 verschiedene CRGB Arrays schreiben lassen und diese dann am Ende mit einem "Videocrossfade" zusammenführen. Erkläre ich Dir im Detail, wenn Du alles andere fertig hast. Ist nicht schwer.

Ob die Animationen mittels HSV oder RGB oder Paletten oder sonstwie die Farben berechnen ist völlig egal.

Gruß,

Helmuth

"grünes Gewabber" --> Simplex Noise Field

"Glitter" --> Marks Twinkle Fox Code

Geiles Forum hier! +Karma klick

Gruß Chris

So,

ich hab den Code nun mal so grob strukturiert. Was mir noch fehlt sind sämtliche animierten Geschichten. Da ich mich noch immer nicht für eine bestimmte LED-Streifen-Art entscheiden konnte, in wenigen Wochen aber der Rohbau beginnt, sollte ich langsam etwas Gas geben. Im Zweifelsfall werde ich WS2810B-Streifen kaufen, da weniger Verkabelungsaufwand.

Nun aber nochmals zu einer konkreten Frage zwecks Machbarkeit.

Zwei gleichzeitig zu berechnende Animationen via Videocrossfade einfach ineinanderzublenden wird in dem von mir angegebenen Fall leider nicht möglich sein. Dies möchte ich anhand einer Grafik verdeutlichen:

x-Achse = Zeit
y-Achse = Verteilung der Animationen über den kompletten LED-Streifen

Bei der roten Linie betritt jmd. die Treppe von unten, während bei der grünen Linie jmd. die Treppe von oben betritt.

Kurz bevor die zweite Person die Treppe betritt haben wir den Fall, dass es sowohl Bereiche gibt, in denen die BGD-Animation zu 100% auf den oberen LEDs zu sehen sein soll, während im unteren Bereich der Treppe bereits zu 100% die Layer-Animation zu sehen ist. Dies wäre mit einem einfachen Crossfade so nicht möglich, da sobald gefadet werden würde keiner der beiden Animationen 100% "Deckkraft" mehr erreichen könnte.

Wäre jedoch auch das mit den Möglichkeiten, die die FastLED-Library bietet, machbar und sind die WS2810Bs schnell genug um dies bei einer Anzahl von max. 200 LEDs ohne Ruckeln wiedergeben zu können?

Gruß Chris

Hier mal der bisherige Code inkl. einer Farbraumkonvertierung, die vermutlich wieder rausfliegen wird, da ja komplett in RGB "gearbeitet" werden kann und Umschaltmöglichkeiten für die in "BGD" und "Layer" implementierten Programme via zweier Taster:

#include "Bounce2.h"
#include "FastLED.h"

#define DATA_PIN 4      // Manuelle Eingabe!
#define ALL_LEDS 188    // Manuelle Eingabe!
#define REAL_LEDS 175   // Manuelle Eingabe!
#define BRIGHTNESS  64  // Manuelle Eingabe!

// Debugging

#define DEBUG Serial // Debugging

#ifdef DEBUG
#define debug(...) DEBUG.print(__VA_ARGS__)
#define debugln(...) DEBUG.println(__VA_ARGS__)
#define debugbegin(...) DEBUG.begin(__VA_ARGS__)
#else
#define debug(...)
#define debugln(...)
#define debugbegin(...)
#endif

// _Entprellung

#define INPUTMODE INPUT                   // INPUT oder INPUT_PULLUP
const byte butPin[] = {                   // Pin-Nummern der angeschlossenen Buttons
  5, 6
};
#define NUMBUTTONS sizeof(butPin)         // Die Anzahl der Tasten durch die Anzahl der Bytes des Arrays butPin ermitteln (wird automatisch definiert)
Bounce debouncer[NUMBUTTONS];             // Mehrere Bounce-Instanzen erstellen

byte buttonAction;                        // Gibt an, für welche Einzeltaste eine Aktion ausgelöst wird.

enum {
  NOACTION,
  BUT1PUSH, BUT2PUSH
};

//  PIR-Sensor

const byte pir[] = {2, 3};  // PIR-Sensoren über Pulldown_Widerstand an Pin 2 und 3
const byte pirLed[] = {12, 13};   // PIR LEDs an Pin 12 und 13

// Modes

boolean mode = 0;  // BGD oder Layer

enum {
  BGD,
  LAYER
};

byte bgdPattern = 0;   // 0 = schwarz, 1 = Halloween, 2 = Sylvester, 3 = Partymode
byte layerPattern = 0; // 0 = weiß,    1 = rot,       2 = grün,      3 = blau

// LED-Arrays

byte stairs[] = {9, 9, 12, 13, 16, 16, 13, 13, 16, 16, 13, 12, 9, 9}; // Anzahl der LEDs pro Stufe
int gaps[] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; // Anzahl der übersprungenen LEDs zwischen jeder Stufe

CHSV hsvPix[ALL_LEDS];  // Sämtliche HSV-LED Daten
CRGB rgbPix[REAL_LEDS]; // Gemappte RGB-LED Daten

void setup()
{
  for (int i = 0; i < NUMBUTTONS; i++)  // Buttons
  {
    pinMode(butPin[i], INPUTMODE);
    debouncer[i].attach(butPin[i]);
    debouncer[i].interval(10);
  }
  for (boolean i = 0; i < sizeof(pir); i++) // PIR-Sensoren
  {
    pinMode(i, INPUT);
  }
  delay(500);
  debugbegin(9600);
  debugln("System bereit");
  FastLED.addLeds<WS2812B, DATA_PIN, RGB>(rgbPix, REAL_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
}

void loop()
{
  eingabe();        // Taster und Sensoren auswerten
  verarbeitung();   // Farbberechnungen
  ausgabe();        // Verteilung und Ausgabe der Pixel
}

void eingabe()
{
  bounce();   // Tastenentprellung
  pirCheck(); // PIR-Sensoren auswerten
}

void bounce()
{
  buttonAction = NOACTION;
  for (int i = 0; i < NUMBUTTONS; i++)
  {
    debouncer[i].update();   // Status prüfen
    if (debouncer[i].fell()) // Wenn die Taste gedrückt wird
    {
      buttonAction = i + 1; // Übergibt die passende buttonAction an den Sketch
    }
  }
}

void pirCheck()
{
  static boolean oldPirState[sizeof(pir)];  // Zuletzt ermittelter Zustand
  static boolean newPirState[sizeof(pir)];  // Aktuell ermittelter Zustand

  for (boolean i = 0; i < sizeof(pir); i++)
  {
    newPirState[i] = digitalRead(pir[i]);

    if (newPirState[i] != oldPirState[i])         // Wenn PIR_Zustandswechsel..
    {
      if (newPirState[i])                         // PIR_Zustandswechsel auf 3.3V
      {
        debug("PIR an Pin ");
        debug(pir[i]);
        debugln(" hat Bewegung registriert");
        digitalWrite(pirLed[i], newPirState[i]);  // LED einschalten
      }
      if (!newPirState[i])                        // PIR_Zustandswechsel auf 0V
      {
        digitalWrite(pirLed[i], newPirState[i]);  // LED ausschalten
      }
      oldPirState[i] = newPirState[i];
    }
  }
}

void verarbeitung()
{
  patternSwitch();  // Wechselt Pattern und Mode
  patternRun();     // Erzeugt Werte für animierte Pattern
}

void patternSwitch()
{
  switch (buttonAction)
  {
    case BUT1PUSH:  // BGD-Taste
      switch (mode)
      {
        case LAYER:
          mode = BGD;
          bgdPattern--;
        case BGD:
          bgdPattern++;
          if (bgdPattern > 3) bgdPattern = 0;
          for (int i = 0; i < ALL_LEDS; i++)
          {
            switch (bgdPattern)
            {
              case 0: // schwarz
                hsvPix[i] = CHSV(0, 0, 0);
                break;
              case 1: // Halloween
                break;
              case 2: // Sylvester
                break;
              case 3: // Partymode
                break;
            }
          }
      }
      break;
    case BUT2PUSH:  // Layer-Taste
      switch (mode)
      {
        case BGD:
          mode = LAYER;
          layerPattern--;
        case LAYER:
          layerPattern++;
          if (layerPattern > 3) layerPattern = 0;
          for (int i = 0; i < ALL_LEDS; i++)
          {
            switch (layerPattern)
            {
              case 0:
                hsvPix[i] = CHSV(0, 0, 255);
                break;
              case 1:
                hsvPix[i] = CHSV(0, 255, 255);
                break;
              case 2:
                hsvPix[i] = CHSV(96, 255, 255);
                break;
              case 3:
                hsvPix[i] = CHSV(160, 255, 255);
                break;
            }
          }
      }
  }
}

void patternRun() // Erzeugt Werte für animierte Pattern
{
  switch (bgdPattern)
  {
    case 0: // schwarz
      break;
    case 1: // Halloween
      break;
    case 2: // Sylvester
      break;
    case 3: // Partymode
      break;
  }
}

void ausgabe()
{
  ledMapping();   // Pixelmapping
  FastLED.show(); // Sämtliche Pixel beschalten
}

void ledMapping() // Pixelmapping
{
  byte i = 0;
  byte stairsSum = stairs[0]; // Zwischensummenberechnung
  byte gapsSum = gaps[0]; // Zwischensummenberechnung
  for (byte j = 0; j < sizeof(stairs); j++)  // Stufenberechnung
  {
    Serial.println("");
    do
    {
      hsv2rgb_rainbow(hsvPix[i + gapsSum], rgbPix[i]); // Farbraumkonvertierung inkl. Mapping
      Serial.print("rgbPix: ");
      Serial.print(rgbPix[i]);
      Serial.print(" = LED: ");
      Serial.println(i);
      i++;
    }
    while (i < (stairsSum));
    stairsSum = stairsSum + stairs[j + 1];
    gapsSum = gapsSum + gaps[j + 1];
  }
}

Im Zweifelsfall werde ich WS2810B-Streifen kaufen, da weniger Verkabelungsaufwand.

Eine dürre Strippe weniger....

Chris72622:
Im Zweifelsfall werde ich WS2810B-Streifen kaufen, da weniger Verkabelungsaufwand.

Ein paar Bauformen gibt es nur als WS2812B und diese sind preiswerter, der Verkabelungsaufwand ist aber das vollkommen falsche Argument.

Kannst Du was empfehlen? Hab mich wie gesagt noch nicht festgelegt. :slight_smile:

Gruß Chris

Bin so frei: APA102 weil sicherer und schneller. Hatten wir das nicht schon mal behandelt oder war das jemand anderes?

Zu Deinem Sketch: NUMBUTTONS und int mag mein Compiler nicht

for (byte i = 0; i < NUMBUTTONS; i++)  // Buttons
...
  for (byte i = 0; i < NUMBUTTONS; i++)

Und das mag der Ablauf nicht:

for (boolean i = 0; i < sizeof(pir); i++)
 for (byte i = 0; i < sizeof(pir); i++) // PIR-Sensoren
  {
    pinMode(pir[i], INPUT_PULLUP);
    pinMode(pirLed[i], OUTPUT);
  }

Mir scheint Dein Programm nicht getestet zu sein, darum höre ich jetzt auf. Ich wollte es probieren, um dann zwei Effekte zu kreuzen. Aber das dauert mit jetzt zu lange, sorry die Sonne lacht.

Also theoretisch: Ich würde immer dem durch PIR ausgelösten Effekt Vorrang gewähren, da damit ja die Treppe beleuchtet werden soll, so daß niemand stolpert. (Hatte das nicht Uwe schon mal geschrieben?) Bei zwei PIRs gleichzeitig den helleren Wert nehmen. Viel Zeit für Spielereien hast Du da sowieso nicht. Laß mal Frau und Kinder die Treppe laufen und stoppe die Zeit. :slight_smile:

Ja, über die Led-Typen hatten wir in dem anderen Thread bereits gesprochen. WS2801 hat den Nachteil, dass es keine Led-Gehäuse mit integrierten Treiber-IC gibt. Nehm die APA102. Bei einem Neukauf macht es wenig Sinn, noch die WS2812B zu nutzen. Die Preisdifferenz ist nur geringfügig höher. Des weiteren sind diese deutlich komfortabler und es braucht nur wenige Änderung, wenn andere µC genutzt werden. Lediglich die SPI Schnittstelle muss dann angepasst werden.

Welcher Led-Treiber zum Einsatz kommt, sollte für die Programmierung keine Rolle spielen. Man sollte hier immer als ordentlich trennen. Spezielle Funktionen (z.B. FastLED.show()) sollten nicht in den einzelnen Animationen eingebaut werden.

Als Beispiel hier eine Animationen. Hier wird keine spezielle Lib gebracuht, die eigentliche Zuweisung würde dann extern erfolgen.

uint8_t leds_heartbeat(struct cRGB *leds, uint8_t numLeds, struct cRGB *color)
{
	// Speicher fuer Helligkeit
	static uint8_t data[1];
	uint8_t dataScale[8];
	const uint8_t values[5] = {255, 63, 255, 0};
	const uint8_t steps = 0x07;
	static uint8_t posValues = 0;

	// Anzahl Durchlaeufe, die gewartet werden
	const uint8_t skipCycles = 0x7f;
	// Durchlaeufe verbleiben, bis neuer Effekt startet
	static uint8_t remCylce	= 0x00;
	
	if(remCylce == 0) {
		if(fade(data, 1, values[posValues], steps)) {
			if(posValues < sizeof(values)-1) posValues++;
			else {
				posValues = 0;
				remCylce = skipCycles;
			}
		}
		else {
			heartbeat(dataScale, 8, data[0]);

			for(uint8_t led = 0; led < numLeds; led++) {
				leds[led] = scaleRGB(color[led], dataScale[led]);
			}
			return 1;
		}
	}
	else remCylce--;
	return 0;
}

In deinem Code sind ansonsten auch noch ziemlich viele Fehler. Das agmue bereits angemerkt. Die for(boolean ....) Schleife ist grundlegend falsch. i kann nur den Wert 0 und 1 annehmen. sizeof(pir) ist hier komplett falsch.

Danke für die vielen Hinweise und Tipps. Hab jetzt die APAs bestellt und hoffe, den noch fehlerhaften Code dadurch dann auch schneller "geradebiegen" zu können. :smiley:

Gruß Chris

Chris72622:
Hab jetzt die APAs bestellt ...

Ich hoffe, irgendwann freust Du Dich, diese Entscheidung getroffen zu haben.

Nach ein paar kleinen Änderungen zeigen meine APA102 nun leuchtende LEDs: mit Button an 6 kann man einschalten und dann die Farben durchschalten. Button 5 schaltet dann aus. Eine Funktion der PIRs habe ich noch nicht feststellen können, nur LED an Pin 2 oder 3 geht an.

Ich habe den Code nun soweit fertig, so dass ich mich um die Programmierung bzg. der einzelnen Zustände machen kann.

Da das Ganze nach einem endlichen Zustandsautomaten riecht, machte ich mir zunächst erstmal grundsätzlich Gedanken über die einzelnen Zustände.

Zustände:

  1. BGD (Hintergrundanimationen sichtbar)
  2. Weiche Wischblende von BGD nach Layer (ggfs. von beiden Seiten, für den Fall wenn zwei Personen die Treppe gleichzeitig betreten)
    Sonderfall: Während vom Layer zu BGD geblendet wird (siehe 4.), betritt mind. eine Person erneut die Treppe. Hierbei sollte dann weiterhin der Layer ausgeblendet werden, während die weiche(n) Wischblende(n) aus 2. das Ganze erneut überlagern sollen.
  3. Layer ("Füllfarbe" überlager BGD vollumfänglich)
  4. Blende von Layer nach BGD

Fades:

  1. "PIR1" (unten) löst aus: Per weicher Wischblende wird BGD von unten nach oben von "Layer" überlagert
  2. "PIR2" (oben) löst aus: Per weicher Wischblende wird BGD von oben nach unten von "Layer" überlagert
  3. Layer-Zeitüberschreitung: Sobald Layer ist x Sekunden sichtbar war, soll gleichmäßig weich in BGD geblendet werden.

Sobald ein Fade gestartet wurde, darf dieser während er läuft nicht erneut ausgelöst werden.
Würde jedoch z.B. der "PIR unten"-Fade ausgelöst, soll zusätzlich gegebenenfalls noch der "PIR oben"-Fade ausgelöst werden können.

Ich steh grad echt mal wieder wie der Ochs vorm Berg, da ich zwar genau weiss wie das Endergebnis auszusehen hat und welches Werkzeug (FSM) ich hierfür verwenden sollte, aber keinen Plan hab wie ich das Chaos an Abhängigkeiten verwalten soll.

Hier nochmals zur Verdeutlichung das Ganze bildlich dargestellt:

Bei der roten Linie betritt jmd. die Treppe von unten, während bei der grünen Linie jmd. die Treppe von oben betritt.

Gruß Chris

Meinen bisherigen Code musste ich leider anhängen, da mittlerweile zu groß. Aufgrund von Speicherproblemen steuert er gewollt testweise momentan lediglich 100 LEDs an.
Zwei Tasten dienen zum umschalten der BGD- und Layer-Pattern, zwei weitere Taster sollen momentan noch das Auslösen der PIR-Sensoren simulieren um das Ganze besser testen zu können.
Dass für die Blenden jeweils ein BGD- und ein Layer-Pattern parallel berechnet werden muss, ist mir klar.

FastLEDtest.ino (7.19 KB)

Chris72622:
Zustände:

  1. BGD (Hintergrundanimationen sichtbar)
  2. Weiche Wischblende von BGD nach Layer (ggfs. von beiden Seiten, für den Fall wenn zwei Personen die Treppe gleichzeitig betreten)
    Sonderfall: Während vom Layer zu BGD geblendet wird (siehe 4.), betritt mind. eine Person erneut die Treppe. Hierbei sollte dann weiterhin der Layer ausgeblendet werden, während die weiche(n) Wischblende(n) aus 2. das Ganze erneut überlagern sollen.
  3. Layer ("Füllfarbe" überlager BGD vollumfänglich)
  4. Blende von Layer nach BGD

Fades:

  1. "PIR1" (unten) löst aus: Per weicher Wischblende wird BGD von unten nach oben von "Layer" überlagert
  2. "PIR2" (oben) löst aus: Per weicher Wischblende wird BGD von oben nach unten von "Layer" überlagert
  3. Layer-Zeitüberschreitung: Sobald Layer ist x Sekunden sichtbar war, soll gleichmäßig weich in BGD geblendet werden.

Sobald ein Fade gestartet wurde, darf dieser während er läuft nicht erneut ausgelöst werden.
Würde jedoch z.B. der "PIR unten"-Fade ausgelöst, soll zusätzlich gegebenenfalls noch der "PIR oben"-Fade ausgelöst werden können.

Um die Problemstellen zu erkennen, habe ich mir Deinen Code bislang nicht angesehen, sondern es mal so gemacht, wie ich es angehen würde. Herausgekommen sind zwei endliche Automaten für jede Richtung und eine Mischung von drei Feldern für drei getrennt gestaltete Animationen zu einem vierten für die Ausgabe. Die PIRs lösen eine schlichte Animation aus, die die LEDs auf Weiß schalten. Da kann man noch mehr machen, aber Dir geht es ja vorwiegend um die Logik.

Ich teste mit 62 APA102C-LEDs an einem Nano mit Hardware-SPI, das mußt Du eventuell anpassen. Hardware-SPI solltest Du aber auch nutzen. Bin gespannt auf Dein Echo :slight_smile:

EDIT 22:38: Überflüssige delays entfernt.

Test_Forum.ino (4.43 KB)

Hallo agmue,

ich hab mir Deinen Code jetzt mal angeschaut, obwohl ich leider noch immer nicht mit LED-Streifen testen kann.

Variablen mit Namen wie z.B. anausRauf oder Zeilen wie zustandRauf = ANRAUF; machen es dem Leser nicht leicht den Code zu durchschauen.

Was mir jedoch gefallen hat, sind in der anzeige-Funktion diese getLuma-Geschichten. :slight_smile:

Ich hatte nun auch mal etwas Zeit, mich an den von mir gewünschten weichen Wischblenden (4:14min) zu versuchen.

Diese würde ich in der finalen Version gerne per blend und einer for-Schleife implementieren.

Anbei ein funktionsfähiger Beispielsketch mit serieller Ausgabe.

Was mir noch nicht gefällt ist die Tatsache, dass an aufeinander folgende Pixel jeweils fest berechnete Werte (6, 12, 19,..) "gesendet" werden, anstatt dass die Pixel jeweils einzeln berechnet werden.

Ich kann es leider nicht besser beschreiben, aber wenn man sich die serielle Ausgabe einmal anschaut, versteht man was gemeint ist.

Bin für jeden Tipp dankbar, wie man den Fadevorgang noch weicher gestalten kann. Mit "Fadevorgang" meine ich die Werterhöhung der einzelnen Index von 0 auf 255. Diese Index sollen später die Basis für den Fadevorgang erzeugen.

Gruß Chris

#define NUM_LEDS 20    // Alle LEDs

byte fadePir1[NUM_LEDS]; // Zustand des PIR1-Keys
byte fadePir2[NUM_LEDS]; // Zustand des PIR2-Keys
byte fadeBgd[NUM_LEDS];  // Zustand des BGD-Keys
byte *fade[] = {fadeBgd, fadePir1, fadePir2};

byte blending[NUM_LEDS];  // Zustand des Keys

enum {OFF, START, FADE, BGD, LAYERSTART, LAYER};
byte globalState = BGD;

byte fadeState[] = {0, 0, 0}; // Speichert den Zustand der BGD-, PIR 1- und PIR 2-Fades
byte pAmount = 40;            // Anzahl der Pixel für die Rampe

static unsigned long fadeTime;
const int fadeDur = 10000;
int p;
int pix;
int pixWrite;

void setup()
{
  Serial.begin(9600);
  delay(5000);
  Serial.println("Start");
  fadeTime = millis();
}

void loop()
{
  p = (millis() - fadeTime) / (fadeDur / NUM_LEDS); // Berechnet permanent die Position von p

  if (fade[1][NUM_LEDS] != 255) // So lange die letzte LED noch nicht den Wert 255 hat
  {
    for (int i = 0; i <= pAmount; i++)
    {
      pix = p - i;      // pix ausgehend von p gleichmäßig reduzieren
      if (pix <= NUM_LEDS && pix >= 0)
      {
        fade[1][pix] = (255.0 / pAmount) * i; // Rampe beschreiben
      }
    }
  }
  else
  {
    Serial.println("Fertig!");
  }
  serielleAusgabe();
}

void serielleAusgabe()
{
  static unsigned long lastTime = 0;

  if (millis() - lastTime >= 500)
  {
    for (byte i = 0; i < NUM_LEDS; i++)
    {
      Serial.print(fade[1][i]);
      Serial.print("\t");
      if (i == NUM_LEDS - 1) Serial.println("");
    }
    lastTime = millis();
  }
}

Das geht einfacher und besser. :wink:

Ich empfehle erneut (und im Wissen, was Du später vorhast) den Ansatz, 2 unabhängige Animationen in 2 extra Arrays zu berechnen. Eins davon kann ja komplett mit "Schwarz" gefüllt sein.

Dann wird alles sehr einfach. Ein Beispiel - die Wischblende. Animation A sind rote Streifen, B ein Regenbogen. Grenze für den Wischeffekt berechnen / festlegen und dann nach leds kopieren.

So hier:

void animation37() {
  // Animation A (rot)
  for (int i = 0; i < NUM_LEDS; i++) {
    leds2[i] = CRGB(millis() + (i * 10), 0, 0);
  }

  // Animation B (Regenbogen)
  for (int i = 0; i < NUM_LEDS; i++) {
    leds3[i] = CHSV(millis()-(i*10), 255, 255);
  }

  // Grenze vom Wischeffekt bestimmen
  uint16_t ratio = (beatsin8(20) * NUM_LEDS) / 255;

  // Stripanfang bis zur Grenze Animation A
  for (int i = 0; i < ratio; i++) {
    leds[i] = leds2[i];
  }

  // Grenze bis Ende Animation B
  for (int i = ratio; i < NUM_LEDS; i++) {
    leds[i] = leds3[i];
  }

  FastLED.show();
}

FÜr die Überblendung ganz genau so:

void animation38() {
  // Animation A (rot)
  for (int i = 0; i < NUM_LEDS; i++) {
    leds2[i] = CRGB(millis() + (i * 10), 0, 0);
  }

  // Animation B (Regenbogen)
  for (int i = 0; i < NUM_LEDS; i++) {
    leds3[i] = CHSV(millis()-(i*10), 255, 255);
  }

  // Verhältnis der Überblendung bestimmen
  uint16_t ratio = (beatsin8(20));

  // Crossfade
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = blend( leds2[i], leds3[i], ratio );
  }

  FastLED.show();
}

Vorteile von meinem Ansatz:

  1. Die Animation läuft unabhängig von Framerate und Prozessor immer gleich schnell. (Zeitgesteuert)
  2. Du schöpfst die 8 Bit Auflösung der Steuerparamer aus. (=weiche Animation)
  3. Später musst Du nur noch die Grenze und/oder Überblendratio zwischen den Animationen in Anhängigkeit der Bewegungsdaten verändern. (Universeller Ansatz)
  4. Du hast Animationen und "Videoeffekte" komplett getrennt und musst Dir keinen Kopf über Wechselwirkungen machen und kannst Dich auf die Logik der State Machine konzentrieren.
  5. Last but not least: keine Floats!

Hoffe, das hilft.
Beste Grüße,

Helmuth

Hi Helmuth,

ich will und werde, genau so wie Du es vorgeschlagen hast, jede Animation (bei mir als "Pattern" benannt) in einem eigenen Array speichern, da ich ja weiss, dass während den Wischblenden Teile zweier Animationen/Pattern auf jeden Fall gleichzeitig zu sehen sein werden.

Bin gerade noch dabei, die von Dir vorgeschlagenen Codeideen zu analysieren.. dauert bei mir immer etwas. :wink:

beatsin8 hab ich da z.B. entdeckt, was ich bisher nicht kannte.

Gruß Chris

Probier den Code einfach aus, dann erklärt sich vieles von selbst. Nimm einen Testsketch, lege 3 CRGBs leds leds2 und leds3 an and schon kann es losgehen.

Es erleichtert die Programmlogik ungemein, wenn IMMER 2 Animationen laufen.

Beatsin8 habe ich nur genommen, um mit wenig Code einen sich verändernden Parameter zu haben.

Später kontrollierst Du den selbst.

Gruß, H.