FastLed - Lauflicht als Sinuswelle / Lauflicht mit Schweif vorn und hinten

Hey Leute,

ich finde diesen Codeschnipsel total toll:

void fade()
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 50);
  int pos = beatsin16( 30, 0, 16 );
  leds[pos] = CHSV( 0, 255, 255);
}

was ich partou nicht eben so kurz hinbekomme bzw. auch gleichmäßig, ist, dass es nicht hin und zurück geht, sondern die ganze zeit weiter läuft.

das sieht bei mir nämlich so aus:

CHSV laufendeLed(byte pLed) {
  byte lHelligkeit;
  byte grosserBereich = NUM_LEDS * goldenerSchnitt;
  float faktor = 1.5;

  if (ledPosition[pLed] <= grosserBereich) {
    byte exponent = grosserBereich - ledPosition[pLed];
    lHelligkeit = maxByte * pow(goldenerSchnitt * faktor, exponent);
  } else {
    byte exponent = ledPosition[pLed] - grosserBereich;
    lHelligkeit = maxByte * pow(goldenerSchnitt * faktor, exponent);
  }

  return CHSV(gFarbe, gSaettigung, lHelligkeit);
}

ich finde die Variante oben recht elegant, da man dort einen weichen Verlauf hat und nicht die Position explizit verwenden muss.

Hat jemand einen Tipp für mich?

Hallo,

schau mal hier: https://blog.blinkenlight.net/blog/ oben unter Experiments ... Udo ist leider selten im Forum, aber die Codes sind wirklich gut.

Bin mir nicht sicher, ob ich das Problem

dass es nicht hin und zurück geht, sondern die ganze zeit weiter läuft.

richtig verstehe. Meinst Du ein ganz gewöhnliches Lauflicht, welches immer in die gleiche Richtung läuft? Falls ja, hier eine einfache Lösung.

Die FastLED Wave Functions liefern alle oszillierende Ergebnisse. Ich hatte die hier im Vergleich gezeigt. Das liefert keinen Sägezahn, wie Du ihn suchst.

ABER: Mark hat da mal was geschrieben - "some helpers to perform tasks periodically".

Dies erlaubt mit minimalem Code (ungetestet) einen periodischer Zähler. Ergebnis: Der gesuchte Sägezahn.

void fade()
{
  // runterdimmen (je kleiner die Zahl, umso länger der Schweif)
  fadeToBlackBy( 32 );
  
  // alle 0,1 s pos hochzählen
  EVERY_N_MILLISECONDS(100) { pos++;  }
  
  // Überlauf?
  if ( pos == NUM_LEDS ) { pos = 0; }

  // Farbe zuweisen
  leds[pos] = CHSV( 0, 255, 255);
}

Hilft das?

Gruß, H.

Danke an euch, die Beispiele von Udo sind mir noch zu komplex, dass war noch nicht das richtige.
Die Sinusfunktion, Helmuth ist das was ich gesucht und noch nicht verstanden hatte :wink:

Ich glaube ich habe es aber in der Frage aber auch nicht klar formuliert was ich für ein Muster suche. Knight Rider finde ich, kam dem noch am nächsten.

Ich habe es jetzt hinbekommen, dass es so läuft wie ich will ohne Treppeneffekte etc.:

Habt Ihr noch nen Tipp wie ich die Umdrehungsgeschwindigkeit ermitteln kann bzw. festlegen kann? Ich hab jetzt erstmal mit einer Stoppuhr gemessen und festgestellt, dass zwischen 1ms und 2ms schon starke unterschiede sind also ungefähr 222bpm und 111bpm, wenn ich die Geschwindigkeit nun auf 100bpm gestellt haben möchte, kann den Arduino ja nicht alle 1,231 Millisekunden etwas machen lassen… vermutlich muss ich hier bei der Sinusfunktion einen größeren Faktor(oder Phase?) verwenden? :S.

hier der Sketch:

#include "FastLED.h"

FASTLED_USING_NAMESPACE

#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
#warning "Requires FastLED 3.1 or later; check github for latest code."
#endif

#define DATA_PIN    6
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    16

#define BRIGHTNESS         255
#define FRAMES_PER_SECOND  120

CRGB leds[NUM_LEDS];
byte sinus = 0;
// 111 Schläge pro minute bei sinusPlusInMilli=2 und geschwindigkeit=1
byte sinusPlusInMilli = 2;
byte geschwindigkeit=1; // Wenn gröer 128 geht in andere richtung
byte maxByte = 255;

void setup() {
  delay(1000); // 1 second delay for recovery
  Serial.begin(9600);

  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
  Serial.println("Sinustest");
}

void loop()
{

  EVERY_N_MILLISECONDS(sinusPlusInMilli)
  {
    sinus += geschwindigkeit;
  }

  EVERY_N_MILLISECONDS(1000 / FRAMES_PER_SECOND)
  {
    sinustest();
    FastLED.show();
  }

}

void sinustest() {
  byte lHelligkeit;
  for (byte i = 0; i < NUM_LEDS; i++) {
    lHelligkeit = sin8(sinus + i * NUM_LEDS);
    //Serial.println(lHelligkeit);
    leds[i] = CHSV(0, 255, lHelligkeit);
  }
}

Du willst also doch eine Sinuswelle, aber die Frequenz selbiger würdest Du gern als bpm übergeben, richtig?

Geht mit FastLED Bordmitteln - schau mal hier:

Beat generators which return sine or sawtooth
   waves in a specified number of Beats Per Minute.
   Sine wave beat generators can specify a low and
   high range for the output.  Sawtooth wave beat
   generators always range 0-255 or 0-65535.
     beatsin8( BPM, low8, high8)
         = (sine(beatphase) * (high8-low8)) + low8
     beatsin16( BPM, low16, high16)
         = (sine(beatphase) * (high16-low16)) + low16
     beatsin88( BPM88, low16, high16)
         = (sine(beatphase) * (high16-low16)) + low16
     beat8( BPM)  = 8-bit repeating sawtooth wave
     beat16( BPM) = 16-bit repeating sawtooth wave
     beat88( BPM88) = 16-bit repeating sawtooth wave
   BPM is beats per minute in either simple form
   e.g. 120, or Q8.8 fixed-point form.
   BPM88 is beats per minute in ONLY Q8.8 fixed-point
   form.

Der von dir zuerst gezeigte Code macht genau das:

void fade()
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 50);
  byte pos = beatsin8( bpm, 0, NUM_LEDS-1 );
  leds[pos] = CHSV( 0, 255, 255);
}

Wo genau liegt das Problem?

Gruß, H.

Alsooo :smiley:
Sinustest ist das gewünschte Muster.

Das möchte ich nun nur noch beschleunigen und verlangsamen können.

In der Art, dass eine Umdrehung in bpm angegeben werden kann.

Wenn ich beatsin8 verwende kommt nicht mehr das gewünschte Muster raus. Ich hab keine Idee wie ich das verwenden kann.

Die Geschwindigkeit stelle ich bei Sinustest mithilfe der Aktualisierungsrate ein wie schnell ich sinus inkrementiere. Das ist hier allerdings eingeschränkt, weil die Stufen abhängig von ms sind. Wenn ich nun sinus nicht um 1 sondern um 2 oder ähnliches inkrementiere verliere ich die 8 Bit Auflösung, was ich eigentlich nicht möchte und auch bei der Taktung glaub ich nicht so wirklich weiter hilft.

Momentan sehe ich nur die Lösung, sinus um 5 zu inkrementieren, Auflösung zu verlieren, aber die Geschwindigkeit dann besser einstellen zu können.

Hier mal zur Veranschaulichung:

Sinustest:

byte sinus = 0;
byte geschwindigkeit=1;
void loop()
{

  EVERY_N_MILLISECONDS(geschwindigkeit)
  {
    sinus++;
  }

  EVERY_N_MILLISECONDS(1000 / FRAMES_PER_SECOND)
  {
    sinustest();
    FastLED.show();
  }

}
void sinustest() {
  byte lHelligkeit;
  for (byte i = 0; i < NUM_LEDS; i++) {
    lHelligkeit = sin8(sinus + i * NUM_LEDS);
    //Serial.println(lHelligkeit);
    leds[i] = CHSV(0, 255, lHelligkeit);
  }
}

fade:

void fade()
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 50);
  byte pos = beatsin8( 30, 0, NUM_LEDS-1 );
  leds[pos] = CHSV( 0, 255, 255);
}

Sinustest2:

byte sinus = 0;
byte geschwindigkeit=1;
void loop()
{

  EVERY_N_MILLISECONDS(1)
  {
    sinus += geschwindigkeit;
  }

  EVERY_N_MILLISECONDS(1000 / FRAMES_PER_SECOND)
  {
    sinustest();
    FastLED.show();
  }

}
void sinustest2() {
  byte lHelligkeit;
  for (byte i = 0; i < NUM_LEDS; i++) {
    lHelligkeit = beatsin8( 20, NUM_LEDS, sinus + i * NUM_LEDS );
    //Serial.println(lHelligkeit);
    leds[i] = CHSV(0, 255, lHelligkeit);
  }
}

Dein "Sinustest" produziert das, was andere ein Lauflicht nennen.

Das geht mit

//       beat8( BPM ) returns an 8-bit value that cycles 'BPM' times
//                    per minute, rising from 0 to 255, resetting to zero,
//                    rising up again, etc..

am einfachsten und kürzesten.

void fade2()
{
  fadeToBlackBy( leds, NUM_LEDS, 50);
  byte pos = (beat8(bpm)*NUM_LEDS) / 255;
  leds[pos] = CHSV( 0, 255, 255);
}

Haben wir es jetzt?

Gruß, H.

Hinweis: Das eindeutige Beschreiben der Aufgabenstellung hilft beim Lösen der Aufgabe ungemein.

Eine aussagekräftige Problemstellung würde so aussehen:

"Ich habe ein kreisförmiges LED Setup. Ich will einen durchgehend in die gleiche Richtung umlaufenden Lichtpunkt mit einem Schweif. Die Umdrehungsgeschwindigkeit des Punktes möchte ich als n bpm übergeben. Wie mache ich das?"

"Knight Rider Effekt" verwirrt völlig, weil es A) ein lineares Setup ist und B) der Lichtpunkt hin- u. herläuft.

Es ist ein ganz gewöhnliches Lauflicht im Kreis.

Gruß, H.

Helmuth:
Hinweis: Das eindeutige Beschreiben der Aufgabenstellung hilft beim Lösen der Aufgabe ungemein.

Da stimme ich Dir zu, allerdings lehrt die Erfahrung, daß man eine Frage erst dann richtig formulieren kann, wenn man die Antwort kennt.

Helmuth:
Es ist ein ganz gewöhnliches Lauflicht im Kreis.

Nö, eher wie ein Komet mit doppeltem Schweif, einer nach hinten, einer nach vorne1). Oder wie eine Welle, auch wenn ich eine Sinusform mit dem Auge kaum exakt sehen kann.

Eine Beschleunigung will mir allerdings nicht gelingen, auch wenn ich alle Zeiten herausnehme. Da muß es irgendwo eine Limitierung geben. Ich tippe auf Transferrate WS2812B oder Prozessortakt. Welchen Arduino verwendest Du?

Testloop:

    sinus++;
    sinustest();
    FastLED.show();

Anm.: 1) Manche Kometen haben tatsächlich zwei Schweife, manchmal mit bloßen Auge zu sehen, allerdings meines Wissens nie entgegengesetzt. Daher hinkt der Vergeich.

Nö, eher wie ein Komet mit doppeltem Schweif, einer nach hinten, einer nach vorne

:o Ich sehe da nur einen Schweif welcher allerdings so lang ist, dass sich der neue Lichtpunkt gewissermaßen in seinen eigenen Schwanz beißt.

Die Schweiflänge ist definiert durch die Intensität der Dimmung.

fadeToBlackBy( 8 );

Erzeugt einen längeren Schweif.

Grüße, H.

Helmuth:
:o Ich sehe da nur einen Schweif welcher allerdings so lang ist, dass sich der neue Lichtpunkt gewissermaßen in seinen eigenen Schwanz beißt.

Nö, ich sehe eine durchlaufende Sinuskurve. Naja, den Sinus erkenne ich nicht wirklich, aber die Leds werden nicht nur langsam dunkler, sondern auch langsam heller.

Sinus.png

Das Thema für den TO heißt aber Beschleunigung: Der in #8 gezeigte Programmausschnitt erzeugt mit APA102 LEDs und Hardware-SPI ein Flimmern, ist also deutlich schneller. Damit liegt es nicht am Prozessortakt, sondern am LED-Typ. WS2812B nutzen eine feste Taktfrequenz für die Datenübermittlung.

Da ist es dann auch egal, wie die Berechnung stattfindet, schneller wird es nicht.

Okay, ich denke, dass ich jetzt da Problem verstanden habe.

Dann würde ich das EVERY_N_... Makro weglassen und basierend auf blink without delay alle n µs hochzählen.

Wenn die APAs flackern liegt das i.d.R. an zu schnellem SPI Takt. Bis 12 MHz geht mit allen APAs die ich hatte problemlos.

WS2812 steigen ganz aus oder flackern, wenn man sie mit >400 fps beschreibt.

Beste Grüße, H

Das mit den 12 MHz hatte ich mal für Dich getestet, ist mir bekannt.

Kleines Mißverständnis: Mit "Flimmern" meinte ich keine Fehlfunktion, sondern einen sehr schnellen, mit dem Auge kaum noch zu verfolgenden Ablauf. Das hätte ich präziser formulieren können.

Da stellt sich dann auch die praktische Frage, ob bei so hohen Geschwindigkeiten der gewünschte Effekt noch wahrgenommen wird. Eine Reduzierung der Genauigkeit fällt dann möglicherweise nicht auf, beschleunigt aber die Anzeige.

Hey und schönen guten Abend :slight_smile:

ja tatsächlich ist es mir schwer gefallen zu beschreiben was ich meine. Als ich mit dem Algorithmus angefangen habe, habe ich erst gedacht ich müsste eine verschachtelte if schreiben die die LEDs nach goldenen Schnitt aufsplittet…
Das Ergebnis ist ja wirklich simpel, aber das ist es am Ende ja meistens… zum Glück!!

Wie ihr mittlerweile schon selber erraten habt :wink: meine ich eine Sinuswelle die sich im Kreis rund rum bewegt. Das wusste ich zu Anfang leider noch nicht :wink:
Was ich wusste war, dass ich einen Schweif vorne und hinten haben möchte der exponentiell abnimmt bzw. absteigt und nicht durch led = led[i+1] die ehm Werte wechseln möchte, da man dann diesen “Treppen” Effekt hat, den ich meine… also es nicht smooth aussieht.
Jetzt noch zu deinem Vorschlag Helmuth, ich habe mit dem fadetoblack und beatsin es nicht geschafft was gleichartiges zu programmieren, da bin ich vom Verständnis wohl noch nicht tief genug drin.
Das Problem der Beschleunigung bei der Variante sinustest() ist, durch die wunderbar feine Abstufung durch sin8() die ja genau meinem Wunsch entsteht, erhalte ich ja für jede der 16 LEDs 255 Werte bevor er einmal durch ist, 1000/255ms = also knapp 4 Umdrehungen pro Sekunde wenn ich mich jetzt nicht täusche.
wenn ich also 255bpm haben möchte, wird das so nicht gehen, maximal 235bpm kann ich mit der maximalen Sinus Auflösung darstellen. Das heißt schneller geht nicht.
Nun zum langsamer werden.
Langsamer kann ich nur werden, in ms Schritten. Also wenn ich sinus alle 2 ms iteriere, dann komme ich auf rund 2 Umdrehungen pro Sekunde sprich 123bpm oder so.
Für dieses Geschwindigkeitsproblem ist mir jetzt keine schöne Variante eingefallen. Ich vermute das Helmuth Du mit der beatsin Funktion schon das richtige Tool genannt hast.
Ich habe es jetzt bei mir so gelöst, dass ich die Auflösung verringert habe, sprich geschwindigkeit = 5. Hab dann Zeit gestoppt und komme dann mit 1200/ bpm ungefähr auf echte bpm.
Zwischen drin bin ich noch echt verzweifelt, da hatte ich noch die Serial Ausgaben drin, die mit 9600 bit/sek die Darstellung gebremst haben.
Falls euch doch noch eine Variante einfällt, wie man die Auflösung von sin8() beibehalten kann, wäre das echt cool, ist zwar alles etwas hoch gestochen.
Wie Du auch schon sagst agmue, sieht man praktisch wohl keinen Unterschied wenn die Genauigkeit reduziert ist. Vor allem bei höheren bpm. Interessant wird es wenn man auf unter 1bpm geht. da sieht man dann, dass es nicht mehr so schön verläuft, da könnte man dann aber auch die Auflösung wieder erhöhen.
Ich würde das jetzt erstmal so stehen lassen, es kommt dem sehr nahe was ich gesucht habe, es sei denn ihr habt noch einen Einfall. :smiley:
Ich danke euch auf jeden Fall für die Geduld und Unterstützung, ich hoffe es nützt auch noch jemand anderem :slight_smile:
gute Nacht
Paul
hier noch mal der Sketch:
```
*#include “FastLED.h”

FASTLED_USING_NAMESPACE

#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
#warning “Requires FastLED 3.1 or later; check github for latest code.”
#endif

#define DATA_PIN    6
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    16

#define BRIGHTNESS         255

CRGB leds[NUM_LEDS];

unsigned int bpm = 80;
unsigned int sinusPlusInMilli = 1200/bpm; //
byte geschwindigkeit=5; // Wenn gröer 128 geht in andere richtung
const byte maxByte = 255;
byte sinus = 0;

void setup() {
 delay(1000); // 1 second delay for recovery
 Serial.begin(9600);

FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
 FastLED.setBrightness(BRIGHTNESS);
 Serial.println(“Sinustest”);
 Serial.println(sinusPlusInMilli);
}

void loop()
{

EVERY_N_MILLISECONDS(sinusPlusInMilli)
 {
   sinus += geschwindigkeit;
 }

EVERY_N_MILLISECONDS(20)
 {
   sinustest();
   FastLED.show();
 }

}
void fade()
{
 // a colored dot sweeping back and forth, with fading trails
 fadeToBlackBy( leds, NUM_LEDS, 50);
 byte pos = beatsin8( 30, 0, NUM_LEDS-1 );
 leds[pos] = CHSV( 0, 255, 255);
}

void sinustest2() {
 byte lHelligkeit;
 sinus++;
 for (byte i = 0; i < NUM_LEDS; i++) {
   lHelligkeit = beatsin8( 20, NUM_LEDS, sinus + i * NUM_LEDS );
   //Serial.println(lHelligkeit);
   leds[i] = CHSV(0, 255, lHelligkeit);
 }
}

void sinustest3() {
 //sinus++;
 byte lHelligkeit;
 for (byte i = 0; i < NUM_LEDS; i++) {
   lHelligkeit = sin8(sinus + i * NUM_LEDS);
   //Serial.println(lHelligkeit);
   leds[i] = CHSV(0, 255, lHelligkeit);
 }
}

void sinustest() {
 byte lHelligkeit;
 for (byte i = 0; i < NUM_LEDS; i++) {
   lHelligkeit = sin8(sinus + i * NUM_LEDS);
   //Serial.println(lHelligkeit);
   leds[i] = CHSV(0, 255, lHelligkeit);
 }
}

void fade2()
{
 fadeToBlackBy( leds, NUM_LEDS, 25);
 byte pos = (beat8(60)NUM_LEDS) / 255;
 leds[pos] = CHSV( 0, 255, 255);
}

```

ArduinoGuy255: ... in ms Schritten.

Anstelle EVERY_N_MILLISECONDS() kannst Du auch micros() verwenden. Da könntest Du feiner abstufen, das geht auf 4 µs genau.

Perfekt! Daran hab ich gar nicht mehr gedacht! Das werde ich ausprobieren.

Update:

Ich habe es jetzt damit am laufen :slight_smile: es ist zwar noch nicht richtig synchronisiert, aber die Variabilität ist hergestellt! DANKE!

unsigned long microsAufloesung = 850000000;
unsigned long letzteMicros;
unsigned long intervallMicros = microsAufloesung / NUM_LEDS / maxByte / gHerzschlagFrequenz;

unsigned long shortMicros() {
  extern volatile unsigned long timer0_overflow_count;
  return((timer0_overflow_count << 8) + TCNT0)*(64/16);
}

  if (shortMicros() - letzteMicros > intervallMicros )  {
    //Serial.println(shortMicros());
    sinus += phase;
    letzteMicros = shortMicros();    // Merker aktualisieren
  }