Software für LED Animationen

Wie steuerst Du Deine LEDs an? Mit Stefans Firmware ist es ja etwas rudimentär und bei der von Thomas ruckelt es sehr.
Ich überlege schon noch einen kleinen Digispark zusätzlich für die Lichtsteuerung einzusetzen, die sanften Effekte von FastLED und Adafruid Neopixel sehen schon toller aus (aber beschäftigen den Atmel halt auch ordentlich).


Edit von @Manuel
Ursprung war hier:

Benutze Fork von Thomas-Lehnert,

Bei mir ruckelt es nicht.

Habe dieses gekauft,

bestimmt gibs biliger aber ich brauchte es schnell :slight_smile:

Das läuft auf dem AtTiny leider nicht.
da brauchst du schon einen größeren Controller oder du kommst mit einer einfachen Animation aus

Hab ich schon so umgesetzt, mit einem Micro

1 Like

Stimmt so nicht ganz, ich kann die meisten Beispiele der genannten Bibliotheken auch auf den Digispark spielen und es faded hübsch und sanft vor sich hin.
Bei Thomas Firmware springen die Farben bei der Regenbogenanimation immer nur eine LED weiter, klar der Nano soll ja noch andere Dinge machen neben dem LED-Update.

Ja das stimmt, die einzel Beispiele laufen
aber die sind dann ohne Zusammenhang zur Musik.

Also bei mir ruckelt da nichts. Die Animation läuft schön gleichmäßig durch.

Ich habe rausgefunden, warum Deine Animation nicht flüssig wirkt: Voll Speed war mir zu hektisch, also hab ich die Countdowntimer auf 10 bzw. 15 gesetzt, da ist die Geschwindigkeit dann okay, allerdings passiert auch nur noch alle 10 bzw. 15 Durchläufe etwas. Schöner wäre es, wenn man statt des Countdowns mehr Zwischenschritte im Farbwechsel machen würde oder hat das dann Einfluß auf die restlichen Funktionen (Knöpfe prüfen, Musik spielen)?

das sollte eigentlich keinen allzugroßen Einfluss haben, da hier ja nicht mit delays gearbeitet wird. Ist aber ein interessanter Ansatz. Werde mich mal damit beschäftigen, wenn ich mal wieder etwas mehr Zeit habe. Bin im Moment ziemlich eingespannt, Enkel sind hier, die nehmen mich voll in Anspruch.

2 Like

Ich habe mir probehalber mal den Rainbow cycle aus dem Adafruit strandtest Beispiel gemopst und ein Bißchen für unsere Zwecke angepaßt (äußere Schleife und delay(wait) entfernt) und Deinen Default Mode damit ersetzt:

// ----------   Loop Animation: Default Mode   ---------- //
if (lsrAnimationMode == 0 && loopCount == 0 && isPlaying() == false && knownCard == false)
{
  loopCountWait = 0;               // Geschwindigkeit der Animation, je größer um so langsamer

    for(int i=0; i<strip.numPixels(); i++) {
      pixelHue = firstPixelHue + (i * 65536L / (strip.numPixels()*4));
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    
    firstPixelHue += 64;
    
  loopCount = loopCountWait;
}

Oben muß man natürlich noch die Variablen definieren:

long  firstPixelHue = 0;
int   pixelHue;

Gefällt mir schonmal sehr gut. Die Geschwindigkeit läßt sich über den Summanden (hier 64) einstellen, über den Faktor bei strip.numPixels() kann man steuern wieviel des Spektrums dargestellt wird. Eins ist ein kompletter Regenbogen der rotiert, /2 wären zwei, *4 ist in dem Fall ein viertel, das durchläuft.

    for(int i=(strip.numPixels()/2)-1; i>=0; i--) {
      pixelHue = firstPixelHue - (i * 65536L / (strip.numPixels()));
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
      strip.setPixelColor(strip.numPixels()-i-1, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    
    firstPixelHue += 128;

Damit faden die Regenbogenfarben langsam und sanft symetrisch zur Mitte des LED-Streifens. Das sieht als Defaultanimation sehr gut aus, finde ich.

1 Like

Du lässt dabei das oberste/unterste LED aus, weil i nicht 0 wird.

1 Like

Du hast Recht, da hatte ich wohl einen Denkfehler. Habs korrigiert.

for(int i=0; i<strip.numPixels(); i++) {
  pixelHue = firstPixelHue + (i * 65536L / (strip.numPixels()*1));
  strip.setPixelColor(strip.numPixels()-1-i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents

firstPixelHue += 384;

Das ist jetzt meine „Loop Animation: Musik spielt“. Ich hab die Richtung umgedreht, da bei der Einbauposition meines Streifens das erste Pixel rechts ist.

1 Like

Ich habe mir noch den Balken für die Lautstärkeänderung in @Thomas-Lehnert s Firmware neu gebastelt, der Teil sieht jetzt so aus :

// ----------   Einmalige Animation: Prozentuale Lautstärkenanpassung   ---------- //
if (lsrAnimationMode == 2 && loopCount == 0)
{
  if (animCount != 0)
  {
    animCount--;
  }

  if (currentDetectedVolume != lastDetectedVolume)
  {
    loopCountWait = 10; 
  }
  
#ifndef EarPhone
  volumeScope = (mySettings.maxVolume - mySettings.minVolume);
  volumeScopeAmount = (volume - mySettings.minVolume) * (LED_COUNT - 1) / volumeScope; // Lautstärkenanzeige angepasst an die Anzahl der LEDs
  volumeScopeAmountMod = (volume - mySettings.minVolume) * (LED_COUNT - 1) % volumeScope;
#endif
#ifdef EarPhone
 if (SpkisOn == false)
  { 
  volumeScope = (mySettings.maxEarVol - mySettings.minEarVol);
  volumeScopeAmount = (volume - mySettings.minEarVol) * (LED_COUNT - 1) / volumeScope; // Lautstärkenanzeige angepasst an die Anzahl der LEDs
  volumeScopeAmountMod = (volume - mySettings.minEarVol) * (LED_COUNT - 1) % volumeScope; 
  }
  else
  {
  volumeScope = (mySettings.maxVolume - mySettings.minVolume);
  volumeScopeAmount = (volume - mySettings.minVolume) * (LED_COUNT - 1) / volumeScope; // Lautstärkenanzeige angepasst an die Anzahl der LEDs
  volumeScopeAmountMod = (volume - mySettings.minVolume) * (LED_COUNT - 1) % volumeScope;
  }
#endif

  uint8_t VolMaxBrightness = 255;
  uint16_t VolColor = 21845L - (((volume - mySettings.minVolume) * 21845L) / volumeScope);

  for (i = 0; i < strip.numPixels(); i++)
    {
      if (i<=volumeScopeAmount){
            strip.setPixelColor(i, strip.ColorHSV(VolColor, 255, VolMaxBrightness)); 
        }
      else if (i==volumeScopeAmount+1){
            strip.setPixelColor(i, strip.ColorHSV(VolColor, 255, ((volumeScopeAmountMod * VolMaxBrightness) / volumeScope))); 
        }
      else{
            strip.setPixelColor(i, strip.ColorHSV(VolColor, 255, 0)); 
        }
    }

  strip.show();
  

  if (animCount == 0)
  {
    //delay(20);
    lsrAnimationMode = 0;
  }
  loopCount = loopCountWait;
}

Die Farbe des Balken wechselt von grün (leise) zu rot (laut) und der Balken wächst proportional zur Lautstärke, wobei nicht einfach die nächste LED angeht, sondern prozentual eingeblendet wird (bei mir gibt es mehr Lautstärkestufen als LEDs). Dadurch sieht die Veränderung des Balkens harmonischer aus.

Ach oben noch volumeScopeAmountMod deklarieren. @Thomas-Lehnert Welchen Vorteil hat es die Variablen wie volumeScope und volumeScopeAmount global anzulegen?

1 Like

Eigentlich keinen, da die Variablen nirgends sonst gebraucht werden. Ich habe wegen der besseren Übersicht die meisten Variablen global angelegt. Notwendig ist das nicht für alle. Da im Moment noch genügend Speicher für die Variablen zur Verfügung steht, hab ich das so gemacht. Wäre der Speicher hier zu knapp, sollte man das ändern, ist aber bishierhin nicht erforderlich.

Übrigens finde ich das toll, dass du die Animation noch verbesserst. Ich hatte die von @tON übernommen und nur ein paar Kleinigkeiten geändert, wie z.B. das ein und ausschalten und die Animation bei Pause.

Derzeitiger Stand, ich habe die Variablen etwas geändert:

// ******************** Definitionen für LED Animation ******************************************************

#ifdef LED_SR

// Declare NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LedPin, NEO_GRB + NEO_KHZ800);

// Zählvarbiablen
uint16_t loopCount;                             // Runterzählen der Loops
uint8_t animCount;                              // Wie oft die einmalige Animation ausgeführt wird bevor es zurück in die Hauptschleife (Animationsmodus 0) geht
uint8_t i;

// Datenvariablen
uint8_t currentDetectedVolume;                  // Speichern der aktuellen Lautstärke für späteren Vergleich
uint8_t lastDetectedVolume;                     // Speichern der Lautstärke um die Animation nur ein mal zu triggern
uint8_t volumeScope;                            // Differenz der von euch eingestellten minimalen und maximalen Lautstärke
uint8_t volumeScopeAmount;                      // Lautstärkenwert in deinem Scope
uint8_t volumeScopeAmountMod;

uint32_t  firstPixelHue = 0;
uint32_t  pixelHue;

uint32_t  TrckChgColor_FFW = 21845L;            // Farbe wird bei Animation nächstes Lied verwendet
uint32_t  TrckChgColor_REW = 43690L;            // Farbe wird bei Animation Lied zurück verwendet
uint8_t   TrckChgSpeed = 64;                    // Geschwindigkeit Animation Lied vor/zurück (1 bis 255)
uint8_t   TrckChgMaxBrightness = 255;           // Helligkeit Animation Lied vor/zurück (1 bis 255)
uint32_t  TrckChgProgress = 0;

uint8_t lsrAnimationMode;                       // Animationsmodus - 0: Daueranimation, 1-2 einmalige Animation (als Unterbrechung zu 0)
uint8_t lsrAnimationTrackMode;                  // Bei Animationsmodus Liedwechsel bestimmung der Farbe und Richtung
uint8_t currentDetectedTrack;                   // Speichern des aktuellen Tracks für späteren Vergleich
uint8_t lastDetectedTrack;                      // Speichern des Tracks um die Animation nur ein mal zu triggern

#ifdef LED_SR_Switch
bool lsrSwitch = false;                         // Ein-Ausschalten aktiviert, true wenn up und down Button long press
bool lsrEnable = true;                          // LED-Animation-Freigabe
bool lsrAudioBreak = false;                     // Wiedergabe gestoppt während Ein-Ausschalten LED-Animation .
bool lsrOffByStdby = false;                     // Ausschaltsignal LED-Animation durch Standbytimer Software only
#endif
#endif

Und die Animationen eigentlich komplett umgeschrieben:

//*************LED-Animaion inspired by @tON and @Thomas-Lehnert **************************
///////////////// Prüfung der einmaligen Animationen /////////////////

// ----------   Liedänderung erkennen und Animation aktivieren   ---------- //
currentDetectedTrack = currentQueueIndex;
if (currentDetectedTrack != lastDetectedTrack)
{
  strip.clear();   // über schwarz oder über die vorherige Animation, dann ausskommentieren
  if (currentQueueIndex > lastDetectedTrack) //nächstes Lied
  {
    lsrAnimationTrackMode = 1;
  }
  if (currentQueueIndex < lastDetectedTrack) // Lied zurück
  {
    lsrAnimationTrackMode = 2;
  }
  lsrAnimationMode = 1;
}

// ----------    Lautstärkenanpassung erkennen und Animation aktivieren    ---------- //
currentDetectedVolume = volume;
if (currentDetectedVolume != lastDetectedVolume)
{
  lsrAnimationMode = 2;
  animCount = strip.numPixels();
}

///////////////// Dauerhafte Loop Animationen /////////////////

// ----------   Loop Animation: Default Mode   ---------- //
if (lsrAnimationMode == 0 && loopCount == 0 && isPlaying() == false && knownCard == false)
{
    for(int i=(strip.numPixels()/2)-1; i>=0; i--) {
      pixelHue = firstPixelHue - (i * 65536L / (strip.numPixels()));
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
      strip.setPixelColor(strip.numPixels()-i-1, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    
    firstPixelHue += 128;         // Geschwindigkeit der Animation, je kleiner um so langsamer
}

// ----------   Loop Animation: Musik spielt   ---------- //
if (lsrAnimationMode == 0 && loopCount == 0 && isPlaying() == true && knownCard == true)
{
    for(int i=0; i<strip.numPixels(); i++) {
      pixelHue = firstPixelHue + (i * 65536L / (strip.numPixels()*1));
      strip.setPixelColor(strip.numPixels()-1-i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    
    firstPixelHue += 384;       // Geschwindigkeit der Animation, je kleiner um so langsamer
 }

// ----------   Loop Animation: Musik pausiert   ---------- //
if (lsrAnimationMode == 0 && loopCount == 0 && isPlaying() == false && knownCard == true)
{
    for(int i=0; i<strip.numPixels(); i++) {
      pixelHue = firstPixelHue + (i * 65536L / (strip.numPixels()*1));
      strip.setPixelColor(strip.numPixels()-1-i, strip.ColorHSV(pixelHue, 192, 12));       
    }
    //strip.setBrightness(64);
    strip.show(); // Update strip with new contents
    
    firstPixelHue += 16;         // Geschwindigkeit der Animation, je kleiner um so langsamer
}
  
///////////////// Einmalige Animationen bei einem Ereignis /////////////////

// ----------   Einmalige Animation: Liedänderung    ---------- //
if (lsrAnimationMode == 1 && loopCount == 0)
{
  animCount = strip.numPixels();
  uint32_t  Balken = TrckChgProgress;
  i = 0;
  
  while (Balken > TrckChgMaxBrightness)
    {
       if (lsrAnimationTrackMode == 1){
            strip.setPixelColor(strip.numPixels()-i-1, strip.ColorHSV(TrckChgColor_FFW, 255, TrckChgMaxBrightness)); 
       }else{
            strip.setPixelColor(i, strip.ColorHSV(TrckChgColor_REW, 255, TrckChgMaxBrightness));             
       }
       Balken -= TrckChgMaxBrightness;
       i++;
       if (animCount != 0)
          {
            animCount--;
          }
    }
  
  if ( animCount != 0 && Balken > 0 )
    {
       if (lsrAnimationTrackMode == 1){
            strip.setPixelColor(strip.numPixels()-i-1, strip.ColorHSV(TrckChgColor_FFW, 255, Balken)); 
       }else{
            strip.setPixelColor(i, strip.ColorHSV(TrckChgColor_REW, 255, Balken));             
       }
    }
  
  strip.show();
  
  TrckChgProgress += TrckChgSpeed;
  
  if (animCount == 0)
  {
    lsrAnimationMode = 0;
    TrckChgProgress = 0;
    loopCount = 40;
  }
}

// ----------   Einmalige Animation: Prozentuale Lautstärkenanpassung   ---------- //
if (lsrAnimationMode == 2 && loopCount == 0)
{
  if (animCount != 0)
  {
     animCount--;
  }

  if (currentDetectedVolume != lastDetectedVolume)
  {
     animCount = 50; 
  }
  
  volumeScope = (mySettings.maxVolume - mySettings.minVolume);
  volumeScopeAmount = (volume - mySettings.minVolume) * (LED_COUNT - 1) / volumeScope; // Lautstärkenanzeige angepasst an die Anzahl der LEDs
  volumeScopeAmountMod = (volume - mySettings.minVolume) * (LED_COUNT - 1) % volumeScope;

#ifdef EarPhone
 if (SpkisOn == false)
  { 
  volumeScope = (mySettings.maxEarVol - mySettings.minEarVol);
  volumeScopeAmount = (volume - mySettings.minEarVol) * (LED_COUNT - 1) / volumeScope; // Lautstärkenanzeige Kopfhörer angepasst an die Anzahl der LEDs
  volumeScopeAmountMod = (volume - mySettings.minEarVol) * (LED_COUNT - 1) % volumeScope; 
  }
#endif

  uint8_t VolMaxBrightness = 255;
  
  uint16_t VolColor = 23000L - (((volume - mySettings.minVolume) * 23000L) / volumeScope);
  for (i = 0; i < strip.numPixels(); i++)
    {
      if (i<=volumeScopeAmount){
            strip.setPixelColor(strip.numPixels()-i-1, strip.ColorHSV(VolColor, 255, VolMaxBrightness)); 
        }
      else if (i==volumeScopeAmount+1){
            strip.setPixelColor(strip.numPixels()-i-1, strip.ColorHSV(VolColor, 255, ((volumeScopeAmountMod * VolMaxBrightness) / volumeScope))); 
        }
      else{
            strip.setPixelColor(strip.numPixels()-i-1, strip.ColorHSV(VolColor, 255, 0)); 
        }
    }

  strip.show();
  

  if (animCount == 0)
  {
    lsrAnimationMode = 0;
    TrckChgProgress = 0;        
  }
}

// ----------   Countdown Zähler über den loop als Ersatz zur delay Funktion   ----------
if (loopCount != 0 ) {
  loopCount--;
}

// ----------   Dadurch wird die Änderung der Lautstärke bzw. Track nur ein mal registiert   ----------
lastDetectedVolume = currentDetectedVolume;
lastDetectedTrack = currentDetectedTrack;

#ifdef LED_SR_Switch
  }
#endif

  if (lsrAudioBreak == true)    // wenn Wiedergabe von LED On/Off gestoppt
  {
mp3.start();                 // Wiedergabe fortsetzen
disablestandbyTimer();
lsrAudioBreak = false;       // Marker Wiedergabe läuft wieder
  }
}
#endif                           // Ende Abfrage Aktivierung LED Animation (#ifdef LED_SR)

// *************************** Ende LED Animation ******************************************

Vielleicht nutzt es ja auch anderen.

3 Like

Mich hat noch gestört, dass es eine ganze Weile dauert, bis nach dem Tastendruck zum Einschalten die LED Animation startet. Da ich die AiO Platine nutze und keine weitere optische Anzeige für den Einschaltzustand nutze siht man ziemlich spät, dass der Tonuino überhaupt angeschaltet ist. Deshalb habe ich im Setup eine Ergänzung gemacht, die die LED’s sofort nach dem Einschalten in einer festgelegten Farbe leuchten lässt. Nach der kompletten Inititialisierung des Tonuino gehen dann auch die LED’s in die Animation .

  // ------------------------------------------------------------------------------------
  //*************** LED Animation initialisieren ************
#ifdef LED_SR
// uint32_t magenta = strip.Color(255, 0, 255);
uint32_t lightblue = strip.Color(32, 64, 128, 64);
  strip.begin();
  strip.clear();
  strip.setBrightness(64);
  strip.show();
  strip.fill(lightblue);
  strip.show();
 
  
  loopCount = 0;
  animCount = 1;
  lastDetectedTrack = 0;
#endif


  // ---------------------------------------------------------------------------------------
  //*************** DFPlayer Mini initialisieren *******************************************
  mp3.begin();

Die Farbe kann man sich beliebig deklarieren über die ersten drei Werte. Der Vierte Wert bestimmt die Helligkeit.

3 Like

Hallo!
(Edit: hatte versehentlich erst im ursprünglichen Thread geantwortet)
Ich habe bisher auch die Version von @tON in Verwendung, was auch wunderbar funktioniert.
Was mich etwas stört, beim Liedwechsel ist die Animation in voller Helligkeit, die Helligkeitseinstellung bezieht sich wohl auf die sonstigen Animationen. So wie ich es verstehe, kann man bei deiner Version die Helligkeit dafür auch ändern. Was genau muss zu dem von dir geposteten Code noch zusätzlich eingebaut werden? (Am besten so „idiotensicher“ wie in tONs Post hier ).
Oder vielleicht könntest du den Sketch als Beispiel posten, schon mal vielen Dank!!

Ich habe mittlerweile die Version von @atomphil am laufen. Er hat die Version von @tON noch verbessert, so dass die Animation weicher abläuft, was mir sehr gut gefällt. Er hat die Grundhelligkeit auf 64 eingestellt. Man kann das auch problemlos ändern.