[GELÖST] Wie FastLED Animation bzw. Effekt überlagern?

Hej,

auf dem englischen Forum ging's nicht weiter, vielleicht gibt es deutschsprachige FastLED Anwender, die mir weiterhelfen können.

Ich habe eine Animation sky(), die alle 144 LEDs eines APA102C Streifens im Verlauf einer Stunde sukzessive mit Farben aus einer Palette füllt.

DEFINE_GRADIENT_PALETTE(skyPalette) {
  Position,  R, G, B ,
  Position, ...
  ...
};
...

CRGBPalette16 activePalette = skyPalette;
...

CRGB colour = ColorFromPalette(activePalette, paletteIndex);
fill_solid(leds, ledCount, colour);
...

EVERY_N_SECONDS(interval) {
    if (paletteIndex < 240) {
      paletteIndex++;
    }
}

Alle N Sekunden wird also der gesamte leds array mit einem neuen CRGB colour bzw. weichem Übergang zwischen den extrapolierten Palettenfarben gefüllt. Das funktioniert, wie in der FastLED Anleitung beschrieben, gut.

Eine zweite Animation shimmer() soll nun einige wenige immer wieder neu zufällig ausgewählte LEDs if ( random8() < 7) des durch die Palette "befüllten" leds array um z.B. 30% aufhellen/abdunkeln, so daß ein "Schimmer-Effekt" entsteht. Das zufällige Auswählen und bestimmen, wann aufgehellt oder abgedunkelt werden soll, funktioniert mit status flags enum {ledConstant, ledBrightens, ledDarkens}; ebenfalls, wie beschrieben.

Aber, wie lassen sich die ständig durch die Palette gefüllten LEDs des leds arrays in ihrer Helligkeit modifizieren? Mit

leds[number].addToRGB(value);
leds[number].subtractFromRGB(value);

geht es nicht gut, da Farben wie z.B. {241, 17, 238} auf/über 255 gehen können, und so Farbinformation verlieren oder Farben verfälschen.

Gibt es eine Möglichkeit, LEDs via HSV zu beeinflussen, und zwar nur den Wert von V?

Danke für hilfreiche Hinweise!

Doku lesen könnte zu Erkenntnissen führen. z.B. hier.

Oder einfach

FastLED.setBrightness( BRIGHTNESS );

Gruß Tommy

Doku lesen könnte zu Erkenntnissen führen.

In der Tat. Dann hätten Sie, bevor Sie eine schnippische Antwort geben, nämlich erkennen können:

"Use FastLED.setBrightness( 0..255) to adjust the brightness of your whole animation."

Das jedoch war nicht meine Frage. In meinem Fall geht es um die Modifikation der Helligkeit einzelner zufällig ausgewählter LEDs eines zuvor mit Farbwerten beschriebenen leds arrays.

Beste Grüße

Ok, dann habe ich wohl Deine Beschreibung falsch verstanden. Das schnippisch gebe ich gern zurück und viel Spaß noch.

Gruß Tommy

Hi

@Tommy - man muß auch Mal einstecken können, fand die Antwort eigentlich ganz passabel.

@Lagom
Du kannst die LED-Farbe (sehr wahrscheinlich) auslesen, Deine Helligkeit aufaddieren und die Werte, gedeckelt auf 255, wieder zurück schreiben.
Dabei geht Dir aber die Grund-Farb-Information verloren.
Wenn Dein Zufall hier quasi 2x den gleichen Pixel trifft, wird Dieser 'super-hell', statt nur aufgehellt.

MfG

PS: (sehr wahrscheinlich) eingefügt, da ich die FastLED-Lib noch nie benutzt habe - bei meinen 08/15 WS2812B-Spielereien reichte mir bisher die NeoPixel-Lib von AdaFruit.

@Tommy56 Nichts für ungut

@postmaster-ino danke für Ihren Hinweis. Ich ging auch davon aus, daß sich das in jedem leds array element (= LED) befindliche Farbentripel auslesen läßt.

Das CRGB object hat drei one-byte data members und diverse setter methods, dito das CHSV object siehe hier. Aber tatsächlich keine getter methods. Man kann also, als Laie zumindest, bereits in den leds array geschriebene Werte nicht auslesen (= getter). Es gibt allerdings auch keine setter method, die es erlaubt, nur den V-Wert von HSV zu setzen, obwohl das für H und S geht. Eine merkwürdige Auslassung, denn wer will nicht die Helligkeit einzelner LEDs im leds array programmatisch modifizieren, ohne daß sich die Farbe ändert, bzw. die Farbwerte ab 255 "überlaufen", wie es im RGB Modell stattfände. Bei HSV kann das nicht passieren.

Die Adafruit library kenne ich noch nicht. Gibt es da Zugriff auf V oder andere getter methods, um nur die Helligkeit eines schon im leds array (oder wie es bei Adafruit benannt ist) befindlichen Farbtripels zu ändern?

Danke im Voraus!

Hi

Meines Wissen kann die NeoPixel-Library kein HSV.
Bei der FastLED-Library habe ich, auf die Schnelle, auch Nichts gefunden, womit man den Farbwert zurück lesen könnte - allerdings sind dort einige zig .h-Files, Die ich nicht Alle durchgesucht habe.

Was mir aber gerade unter kam: die Umrechnung RGB<->HSV - könnte sogar direkt im Arduino laufen, also ähnelt einem Sketch schon sehr:

Vll. kannst Du mit der NeoPixel-Library und dieser Formeln was zusammen backen.

MfG

Für das, was Du willst, hast Du im Endeffekt nach meiner Ansicht 3 Wege.

  1. Du arbeitest mit dem Array CRGB leds[anzahl];
  2. (der Beste Weg) Du fragst die Jungs von fastled.io
  3. Du hällst lokal ein HSV-Array deiner LEDS vor und nimmst das als Quelle (wenn der Speicher es her gibt)

Wobei Dein Array leds (1) schon die RGB-Werte Deiner Leds beinhaltet, Du hast also alles.

Gruß Tommy

Das Problem ist, daß man aus dem leds array keine Werte auslesen kann, um sie dann zu modifizieren. Funktionen wie z.B. die außerordentlich nützliche gradient palette extrapolieren die Farbwerte aus nur wenigen Farbtripeln, aber man kommt nicht an diese Werte heran, weil es keine getter methods gibt, während der leds array vollgeschrieben wird.

Bei FastLED erfuhr ich von den maintainern: Es gibt nur eine leds[value].setHue(#);, aber keine leds[value].setSat(#); bzw. leds[value].setVal(#); und um diese der library hinzuzufügen, reichen meine Kenntnisse bei Weitem nicht aus.

Bleiben also nur die vorhandenen RGB methods (brighten/darken):

leds[value].addToRGB(#);
leds[value].subtractFromRGB(#);

Also nochmals trotzdem vielen Dank!

schau dir die ,addToRGB und .substractFromRGB halt mal in der Lib an.

Im einfachsten Fall:
mach eine Lokale Kopie der Lib in deinem Projekt
include die lokale Kopie mit "" stat < >
bau deine Grenzwertabsicherung in die zwei Methoden - oder zwei neue ein.

Das man das normalerweise anders löst ist schon klar, aber obiges finde ich anfängertauglich.
Wenn du den Inhalt der zwei Methoden nicht verstehst, poste eine davon hier in code-Tags, vieleicht gibt es dann jemanden der dich in die richtige Richtung schubst.

Schau mal, ob Du damit was anfangen kannst:

#include "FastLED.h"
#define NUM_LEDS 9
#define DATA_PIN 2
CRGB leds[NUM_LEDS];
CHSV hsvleds[NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
  CHSV myColor( 160, 255, 30);
  hsvleds[0] = myColor;
  leds[0] = hsvleds[0];
  CHSV newColor( hsvleds[0] );
  newColor.val = 200;
  leds[1] = newColor;
  FastLED.show();
}

void loop() {}

Lagom:
Das Problem ist, daß man aus dem leds array keine Werte auslesen kann

Wieso? Schau Dir doch einfach mal die Datei an. Da steht doch, wie Du auf die Werte zugreifen kannst.

/// Representation of an RGB pixel (Red, Green, Blue)
struct CRGB {
	union {
		struct {
            union {
                uint8_t r;
                uint8_t red;
            };
            union {
                uint8_t g;
                uint8_t green;
            };
            union {
                uint8_t b;
                uint8_t blue;
            };
        };
		uint8_t raw[3];
	};

Also mit leds[index].green solltest Du den Wert für Grün des Pixels an Position index bekommen und setzen können.

Gruß Tommy

Tommy56:
Also mit leds[index].green solltest Du den Wert für Grün des Pixels an Position index bekommen und setzen können.

Vollkommen richtig, man kann Werte aus dem Feld leds auslesen. Der TO möchte aber die Helligkeit beeinflussen, die im HSV-Farbmodell enthalten ist.

Lagom:
Das Problem ist, daß man aus dem leds array keine Werte auslesen kann, um sie dann zu modifizieren.

Diese Aussage ist falsch, siehe oben. Allerdings ist das nicht Dein Problem, sondern dies: "There is no conversion back from CRGB to CHSV provided with the library at this point."

Wenn Du es donnoch machen möchtest, findest Du eine Formel im Link in #6. Da mußt Du auf die abweichenden Wertebereiche 0 bis 360 und 0 bis 1 in Fließkomma achten.

#include "FastLED.h"
#define NUM_LEDS 9
#define DATA_PIN 2
CRGB leds[NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812, DATA_PIN, RGB>(leds, NUM_LEDS);
  leds[0] = CRGB( 255, 0, 0);
  byte h, s, v;
  RGBtoHSV( leds[0].r, leds[0].g, leds[0].b, &h, &s, &v);
  CHSV newColor( h, s, v );
  newColor.v = 100;
  leds[1] = newColor;
  FastLED.show();
}

void loop() {}

void RGBtoHSV( byte r, byte g, byte b, byte *h, byte *s, byte *v) {
  float fh, fs, fv;
  RGBtoHSVfloat( r / 255.0, g / 255.0, b / 255.0, &fh, &fs, &fv);
  *h = byte(fh / 360.0 * 255.0);
  *s = byte(fs * 255.0);
  *v = byte(fv * 255.0);
}

// Quelle: https://wisotop.de/rgb-nach-hsv.php
// r,g,b-Werte zwischen 0 und 1
// h = [0,360], s = [0,1], v = [0,1]
// Wenn s == 0, dann h = -1 (undefined)
void RGBtoHSVfloat( float r, float g, float b, float *h, float *s, float *v) {
  float minwert, maxwert, delta;
  minwert = min(r, g); // MIN( r, g, b );
  minwert = min(minwert, b);
  maxwert = max(r, g); // MAX( r, g, b );
  maxwert = max(maxwert, b);
  *v = maxwert;                        // v
  delta = maxwert - minwert;
  if ( maxwert != 0 ) *s = delta / maxwert;
  else {                           // r = g = b = 0
    // *s = 0; *h = -1; return;
    *s = 0; *h = 0; return;
  }
  if (maxwert == minwert) {                // hier ist alles Grau
    *h = 0; *s = 0; return;
  }
  if ( r == maxwert )
    *h = ( g - b ) / delta;       // zwischen Gelb und Magenta
  else if ( g == maxwert )
    *h = 2 + ( b - r ) / delta;   // zwischen Cyan und Gelb
  else
    *h = 4 + ( r - g ) / delta;   // zwischen Magenta und Zyan
  *h *= 60;                     // degrees
  if ( *h < 0 )
    *h += 360;
}

Möglicherweise kommt Dir "Rainbow" vs "Spectrum" in die Quere. Dann könnte eine eigene Funktion HSVtoRGB Abhilfe schaffen.

Wenn Du Dich gedanklich von HSV löst: Hast Du schon fadeLightBy() probiert?

@noiasca @Tommy56 @agmue

Vielen Dank für die Hinweise. An der library zu Werke zu gehen übersteigt meinen Horizont. Ich ging davon aus, daß es wie mit

leds[i].addToRGB(#);
leds[i].subtractFromRGB(#);

dergleichen für HSV gibt - "add..." bzw. "subtract..." ist klar und auch dem Laien verständlich. Ich werde es mit

leds[i].fadeLightBy(#);
leds[i].maximizeBrightness(#); < maximiert anscheinend nicht, sondern akzeptiert einen Wert

probieren, obwohl, alleine schon per Namensgebung, diese beiden methods vielleicht nicht das bewirken, was ich benötige, nämlich "addToV" und "subtractFromV" oder wie immer man es bezeichnen sollte. Der maintainer schreibt dazu auch etwas in seinen GitHub Kommentaren.

Es ist scheint wohl am Besten zu sein, der Einfachheit halber erst einmal bei RGB zu bleiben und bei "add..." bzw. "subtract..." aufzupassen, daß man keine Werte über 255 generiert. Vielleicht hat Daniel Garcia irgendwann einmal vielleicht Zeit, methods für H, S und V bereitzustellen und auch für Paletten ,wie er schrieb, HSV anzubieten.

Hi

Den Post von agmue hast Du aber schon gesehen?
Vll. auch Mal auf Dein 'Steinchen' aufgebrutzelt?

So wie ich den Sketch verstehe, wird der erste Pixel auf 'Voll-Rot' gesetzt.
Dann werden die Werte des Pixel ausgelesen, umgerechnet, und als H-S-V dem zweiten Pixel gesetzt.
Mit etwas Glück leuchten beide Pixel in gleicher Farbe.

Nun ist es an Dir, zwischen der Umrechnung und dem Setzen an den Werten zu Spielen - z.B. was an der Helligkeit drehen.

MfG

postmaster-ino:
Mit etwas Glück leuchten beide Pixel in gleicher Farbe.

Gut gesehen, aber kein Glück! Ich habe das natürlich ausprobiert, sonst würde ich das hier nicht reinstellen. Allerdings habe ich nicht alle Farben probiert :wink:

Clou ist aber eigentlich newColor.v = 100; mit einer neuen Helligkeit bei gleicher Farbe.

Aber der TO hat sich so auf die gesuchten Methoden versteift, daß er alternative Lösungen nicht sehen "will".

#10 ist eigentlich die Idee, die Animation in HSV zu rechnen und dann nach RGB zu kopieren, weil FastLED.show ein RGB-Feld benötigt. Diese Variante dürfte farbtreuer sein.

Oder man macht sich eben eine Funktion zum Umrechnen von RGB nach HSV.

Hi

Das 'Glück' war auch eigentlich nur so gesagt :wink:
Der Link zu Deinem Sketch ist auch schon in den Lesezeichen ... muß 'nur noch' in die Arduino-IDE finden (mir gehen gerade die Nano's aus ... wobei ich auch noch Uno's da hätte ... und einen WS2812B-Streifen habe ich Gestern auch recycled (... gegen auf Flach-Alu geklebte Streifen getauscht).

MfG

Ich komme nochmal auf meinen Kommentar zurück: Halte Deine Werte in einem eigenen CHSV-Array.
Das sind 3 Byte * 144 = 432 Byte. Das sollte auf einem MEGA machbar sein, bei Verwendung eines I2C-FRAM (evtl. SPI - ist wesentlich schneller) sogar auf einem UNO. Da wäre aber die notwendige Geschwindigkeit noch in Betracht zu ziehen.

Dann änderst Du die Werte im CHSV-Array und füllst von dort das CRGB-Array (diese Richtung ist ja implementiert), evtl. auch nur für einzelne Pixel.

Also Lösungen gibt es.

Also

leds[i].addToRGB(#);
leds[i].subtractFromRGB(#);

sind nicht brauchbar, da die Farbe erheblich verändert wird.

Der Hinweis von @agmue auf

leds[i].fadeLightBy(#);

war, was das dimmen betrifft, gut. Allerdings dimmen Werte von 0-255 nicht linear, erst ab einem Wert von über 196 tut sich etwas Wahrnehmbares, höhere Werte als 237 verändern zusehends die Farbe. Auch bei

leds[i].fadeToBlackBy(#);

tut sich unter 196 nichts, wobei man dann allerdings ohne Farbverfälschung bis auf 255 (LED ist aus) gehen kann; das ist also, was die Farbstabilität angeht, sehr gut, wenn man zwischen 196 und 254 bleibt.

Wenn jetzt noch

leds[i].maximizeBrightness(#);

das genaue Gegenstück dazu ist, wäre die Sache gelöst.

@Tommy56 Ich verwende 24 Adafruit Trinket 5V der Kleinheit halber. Aber das Problem ist schon zur Hälfte gelöst. Fortgeschrittene codes schaue ich mir natürlich auch gerne an, aber erst wenn ich sie auch nachvollziehen kann. Alles in allem hat dieser thread im Gegensatz zum Englischen konkret ein Ergebnis gebracht, das ist schon sehr gut, wie ich finde. Wenn nun noch maximizeBrightness das Gegenstück zu fadeToBlackBy ist, dann ist sicher auch anderen Laien geholfen.

Hier ist ein Beitrag zur Nichtlinearität von LED und Auge.

Gruß Tommy