Digitale RGB-LED-Animation

7

Ich habe versucht, Farben für ein Projekt, an dem ich arbeite, ineinander übergehen zu lassen. Ich habe dies mit dem Regenbogeneffekt erreicht, den einige aus Adafruits Beispielcode haben, aber ich möchte in der Lage sein, die Farben auszuwählen (z. B. Dunkelblau in Hellblau).

Ich habe die Farben geändert und verblasst, aber die Überblendung schaltet alle LEDs aus und beginnt, die Helligkeit der neuen Farbe zu erhöhen. Ich brauche die Farben, um zu mischen, anstatt auszublenden und die Helligkeit zu erhöhen.

Kann mich jemand in die richtige Richtung weisen?

#include "LPD8806.h"
#include "SPI.h"
#define stripSize 64

int nLEDs = 160;

int dataPin  = 2;
int clockPin = 3;

// First parameter is the number of LEDs in the strand.  The LED strips
// are 32 LEDs per meter but you can extend or cut the strip.  Next two
// parameters are SPI data and clock pins:
LPD8806 strip = LPD8806(64, dataPin, clockPin);

// You can optionally use hardware SPI for faster writes, just leave out
// the data and clock pin parameters.  But this does limit use to very
// specific pins on the Arduino.  For "classic" Arduinos (Uno, Duemilanove,
// etc.), data = pin 11, clock = pin 13.  For Arduino Mega, data = pin 51,
// clock = pin 52.  For 32u4 Breakout Board+ and Teensy, data = pin B2,
// clock = pin B1.  For Leonardo, this can ONLY be done on the ICSP pins.
//LPD8806 strip = LPD8806(nLEDs);

void setup() {
  // Start up the LED strip
  strip.begin();

  // Update the strip, to start they are all 'off'
  strip.show();
}

void loop() {
  //turnAllOn(strip.Color(30,30,30),4000);
  fade(0, 127, 0, 100); //red, green, blue, delay - fade up all pixels one color
  //turnAllOn(strip.Color(30,100,30),4000);
  fade(50, 127, 02,100); //red, green, blue, delay - fade up all pixels one color
  //turnAllOn(strip.Color(100,30,100),4000);
  fade(50, 127, 50, 100); //red, green, blue, delay - fade up all pixels one color
}

void fade(uint32_t r, uint32_t g, uint32_t b, uint32_t wait) {
  int i, j;
  for (j=0; j < 384; j++) {
    for (i=0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, strip.Color((r*j)/1000,(g*j)/1000,(b*j)/1000));
    }
  strip.show();
  }
  delay(wait);
}

void turnAllOn(uint32_t c, uint32_t wait) {
  int i;  
  for (i=0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, c);  // turn all pixels on
  }
  strip.show();   // write all the pixels out
  delay(wait); 
}

uint32_t Wheel(uint16_t WheelPos)
{
  byte r, g, b;
  switch(WheelPos / 128)
  {
    case 0:
      r = 127 - WheelPos % 128;   //Red down
      g = WheelPos % 128;      // Green up
      b = 0;                  //blue off
      break; 
    case 1:
      g = 127 - WheelPos % 128;  //green down
      b = WheelPos % 128;      //blue up
      r = 0;                  //red off
      break; 
    case 2:
      b = 127 - WheelPos % 128;  //blue down 
      r = WheelPos % 128;      //red up
      g = 0;                  //green off
      break; 
  }
  return(strip.Color(r,g,b));
}
Rhys Edwards
quelle

Antworten:

6

Derzeit beginnt Ihre Überblendfunktion bei 0 und interpoliert effektiv bis zur gewünschten Farbe. Um zwischen den Farben zu verblassen, benötigen Sie die Skizze, um sich an die zuvor verwendete Farbe zu erinnern und von dieser statt von 0 aus zu verblassen.

Der Ansatz, den ich verwenden würde, besteht darin, zunächst zu berechnen, um wie viel sich jede Komponente bei jedem Schritt ändern muss. Wenn Sie beispielsweise in 50 Schritten von 100 auf 200 blenden möchten, muss dies bei jedem Schritt um +2 geändert werden. Wenn Sie dasselbe in umgekehrter Reihenfolge tun möchten (200 bis 100), muss es sich um -2 ändern.

Eines der Probleme ist, dass sich jede Komponente wahrscheinlich um einen anderen Betrag ändert (Rot kann von 0 auf 200 gehen, aber Blau kann nur von 50 auf 70 gehen). Wenn Sie ständig Ganzzahlen verwenden, kann dies zu ungleichmäßigen Übergängen führen. Daher würde ich empfehlen, stattdessen Gleitkommazahlen zu verwenden. Es ist technisch langsamer (weniger effizient), aber wahrscheinlich nicht genug, um sich Sorgen zu machen.

So würde ich es wahrscheinlich schreiben:

void fade(uint8_t oldR, uint8_t oldG, uint8_t oldB, uint8_t newR, uint8_t newG, uint8_t newB, uint32_t numSteps, uint32_t waitPerStep)
{
    // Guard against division by zero
    if (numSteps == 0) numSteps = 1;

    // Calculate how how much each colour needs to change on each step
    const float
        stepR = (newR - oldR) / (float)numSteps,
        stepG = (newG - oldG) / (float)numSteps,
        stepB = (newB - oldB) / (float)numSteps;

    // These values will store our colours on the way along
    float r = oldR, g = oldG, b = oldB;
    uint8_t byteR = oldR, byteG = oldG, byteB = oldB;

    // Go through each fade step
    const uint16_t numPixels = strip.numPixels();
    for (uint32_t step = 0; step < numSteps; ++step) {
        // Move one step towards the target colour
        r += stepR;
        g += stepG;
        b += stepB;

        // Round the colours to integers here so we don't have to do it repeatedly in the loop below
        byteR = (uint8_t)(r + 0.5f);
        byteG = (uint8_t)(g + 0.5f);
        byteB = (uint8_t)(b + 0.5f);

        // Apply the colour to each pixel
        for (uint16_t pixel = 0; pixel < numPixels; ++pixel) {
            strip.setPixelColor(pixel, byteR, byteG, byteB);
        }

        strip.show();
        delay(waitPerStep);
    }
}

Wie Sie sehen können, übergeben Sie es die alte Farbe (die Sie Verblassen aus ) und die neue Farbe (die Sie Verblassen zu ). Wie oben erwähnt, bedeutet dies, dass sich Ihre Skizze merken muss, welche Farbe sie zuvor verwendet hat, da die Bibliothek meiner Meinung nach keine Möglichkeit bietet, die aktuelle Farbe zurückzulesen.

Ich habe einige Optimierungen hinzugefügt, damit es schneller läuft. Sie können noch mehr tun, um es bei Bedarf noch weiter zu optimieren.

Um dies zu verwenden, würden Sie ungefähr Folgendes tun:

// Fade from black to red and pause briefly
fade(0, 0, 0,  255, 0, 0,  100, 10);
delay(500);

// Fade from red to purple and pause briefly
fade(255, 0, 0,   255, 0, 255,  100, 10);
delay(500);

// Fade from purple to green and pause briefly
fade(255, 0, 255,  0, 255, 0,  100, 10);
delay(500);

Ich habe einige andere Änderungen gegenüber Ihrer eigenen Fade-Funktionsfunktion vorgenommen. Zunächst habe ich es so gemacht, dass Sie die Anzahl der Schritte festlegen können, die ausgeblendet werden sollen. Dies kann sehr nützlich sein, da größere Farbänderungen mehr Schritte erfordern, um glatt auszusehen.

Ich habe auch den waitParameter geändert . In Ihren Code setzen Sie die Verzögerung, nachdem die gesamte Überblendung abgeschlossen war, was wie ein seltsamer Ansatz erscheint. Es ist sinnvoller, zwischen den einzelnen Schritten des Überblendens eine kleine Verzögerung zuzulassen, damit Sie steuern können, wie schnell es geht.

In meinem obigen Beispiel sehen Sie die 100, 10Parameter am Ende jedes Aufrufs von fade(). Dies bedeutet, dass die Farbänderung in 100 Schritte mit einer Verzögerung von 10 ms zwischen den einzelnen Schritten unterteilt wird. Das Ergebnis ist, dass jede Überblendung ungefähr 1 Sekunde dauert (ohne die Zeit, die zum tatsächlichen Aktualisieren des LED-Streifens benötigt wird).

Peter Bloomfield
quelle
1
Sehr gut beantwortet. Ein großes Lob für alle Codedetails, Optimierungen und Erklärungen. Möglicherweise ein kleiner Fehler: Die from-Farbe wird nie angezeigt, wenn Sie Ihre Schleife mit dem ersten Schritt beginnen. Sie sollten eine weitere Iteration hinzufügen und nur am Ende der Schleife erhöhen.
jfpoilpret
@jfpoilpret Danke. Den ersten Schritt zu verpassen ist eigentlich absichtlich. Aufgrund der Frage gehe ich davon aus, dass die LEDs bereits die 'alte' Farbe anzeigen, bevor sie fade()aufgerufen werden. Du hast aber absolut recht. Unter anderen Umständen möchten Sie möglicherweise tatsächlich diesen zusätzlichen Schritt.
Peter Bloomfield
Das ist wirklich toll. Vielen Dank für das ausführliche Schreiben!
Rhys Edwards