[SOLVED] Slow random dimming of APA102C LEDs

Hej,

for an installation, I would like to dim the 144 LEDs of an APA102C strip randomly up and down (not to black!) and very slowly so as to convey the impression of light passing through tall grass or foliage, some kind of very subtle "shimmering" effect. In parallel to that happening, the entire strip should change from cold white to warm white over 1-2 hours and then revert. I am using a Metro Mini.

I have downloaded the FastLED 3.1 library and found the following reference page FastLED: Modules but I don't quite understand which of these functions does what in an application context.

Regarding dimming up and down, I saw that when decrementing the hue value, the LED gets darker but also changes colour (white becomes yellow and eventually red). Can this be avoided? With the Pololu LED strip library (gradient example) I saw that it seems possible to dim across the entire 144 LED strip to almost black without the colour changing.

In short, how could I use FastLED or another library to accomplish what I'm after? I found no "manual" for these hundreds of FastLED functions, for example, only videos and code where users did bouncy disco lights and similar effects all extremely colourful and animated very fast. I don't really know where to start with what I'm after. Is the FastLED library the right thing or rather Pololu's?

Some hints very much appreciated!

I found no "manual" for these hundreds of FastLED functions,

Yeah, I saw that, but it doesn't really explain all of these "LIB8STATIC uint8_t
dim8_video (uint8_t x) Adjust a scaling value" etc. functions in terms of their usage or why the dimming by value (HSV) changes the colour of an LED so much. What does "LIB8" or "adjust a scaling value" even mean? All other libraries I have used before were easily understood, so if you have some other information, I'd really appreciate it.

LIB8STATIC uint8_t
dim8_video (uint8_t x) Adjust a scaling value" etc. functions in terms of their usage or why the dimming by va

These are just function definitions http://fastled.io/docs/3.1/group___dimming.html

why the dimming by value (HSV) changes the colour of an LED so much.............. Regarding dimming up and down, I saw that when decrementing the hue value,

Because you are changing the hue component you need to change the V component for dimming.

That type of strip has a variable for a brightness control which you are much better off using. I am not sure if that libiary lets you change this.
But they have a software feature that might help

You could always ask in the fast led forum.

You have seen this? http://fastled.io/docs/3.1/index.html

Basically I think your problem is the libiary is rather like a collection of paint tubes but you want a painting. It takes skill and experience to know what colours to mix and how to apply the paint to a canvas to get the painting you want. It is fine having a vision of what you want to do but translating that into what code you need, more experience and experimentation is needed.

Thanks GM, ok, this is a trickier library than what I previously used. But I have something that I understand how it works and why.

I have not found a way how the entire show can be de-saturated to have less cheesy colours. Also, I have not found out how, with FastLED, I can get all LEDs to shimmer (dimming up/down ever so slightly and very slowly). Then, when stretched out to one hour, the 256 discrete steps become noticeable and I'd like to have the blending occur smoother. As you suggest, I should also ask at the Google + group and link to the code here.

At least I'm beginning to understand a bit, how this FastLED thing works.

/******* Skylight *******/

/******* Adafruit Metro Mini, 1m APA102C LED strip *******/

/******* LIBRARIES *******/

#include "FastLED.h"

/******* VARIABLES *******/

const byte pinData = 3;
const byte pinClock = 4;

const byte ledCount = 144; // Standard number for 1m APA102C LED strip
byte maxBrightness = 96; // Values from 0 - 255, can be changed interactively with a potentiometer

DEFINE_GRADIENT_PALETTE(testPalette) {
  0, 255, 0,  0,
  128,  0,  255,  0,
  255,  0,  0,  255
}; // http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Sunset_Real.png.index.html

DEFINE_GRADIENT_PALETTE(sunsetSky) {
  0,  10, 62, 123,
  36,  56, 130, 103,
  87, 153, 225, 85,
  100, 199, 217, 68,
  107, 255, 207, 54,
  115, 247, 152, 57,
  120, 239, 107, 61,
  128, 247, 152, 57,
  180, 255, 207, 54,
  223, 255, 227, 48,
  255, 255, 248, 42
};

DEFINE_GRADIENT_PALETTE(midsummerSky) {
  0,  33, 55, 153,
  25,  80, 119, 197,
  89, 153, 215, 250,
  95, 199, 233, 252,
  102, 255, 255, 255,
  120, 197, 219, 240,
  147, 150, 187, 223,
  200, 159, 171, 172,
  255, 169, 154, 128
};

DEFINE_GRADIENT_PALETTE(cloudySky) {
  0, 152, 164, 155,
  30, 139, 152, 140,
  64, 126, 141, 128,
  92,  80, 95, 82,
  107,  46, 59, 47,
  114,  74, 88, 71,
  123, 110, 124, 102,
  196,  46, 58, 39,
  255,  11, 18,  8
};

CRGBPalette16 myPal = cloudySky;

struct CRGB leds[ledCount];

/******* FUNCTIONS *******/

void setup() {

  LEDS.addLeds<APA102, pinData, pinClock, BGR>(leds, ledCount); // BGR for APA102C LED strips
  LEDS.setCorrection(Tungsten40W);
  // LEDS.setCorrection(UncorrectedColor);

} // End of setup

void loop() {

  skyEffect();

  FastLED.show();

} // End of loop

void skyEffect() {

  static const float transitionDuration = 10; // Minutes

  static const float interval = ((float)(transitionDuration * 60) / 256) * 1000; // Steps in milliseconds

  static uint8_t paletteIndex = 0; // Current gradient palette colour

  CRGB colour = ColorFromPalette(myPal, paletteIndex, maxBrightness, LINEARBLEND); // Or use a built-in palette

  fill_solid(leds, ledCount, colour); // Light the whole strip with the colour fetched from the palette

  EVERY_N_MILLISECONDS(interval) { // Traverse the palette

    if (paletteIndex < 240) { // Don't use 255, see https://github.com/FastLED/FastLED/issues/515#issuecomment-340627525

      paletteIndex++;

    }

  }

} // End of skyEffect

when stretched out to one hour, the 256 discrete steps become noticeable

That is the sort of thing that is addressed by colour dithering.

Thanks, GM, now that sounds interesting. Do you happen to know how "colour dithering" can be invoked programmatically? I found "temporal dithering", but that seems only to deal with delay functions (I thought one should avoid delay and I am not using delay in my first attempt above) and the global brightness of the entire strip, if I understood that correctly?

I also noticed that, although my palette ends in a blue colour, it transitions a bit further into some brighter pinkish reddish colours, as if the whole transition does not stop when the index is 255 (as I tried coding it), but "overshoots" as if the transition had "inertia"?

Found out why the transition overruns, see code update above.

So, the only thing missing now would be the very subtle random "twinkling" or "shimmering" effect running in parallel to the gradient palette transition.

Do you have any recommendations how that could be incorporated?

I found "temporal dithering",

Yes that is the same thing.

but that seems only to deal with delay functions (I thought one should avoid delay and I am not using delay in my first attempt above)

As a rule you should by implementing a state machine, but that is only for large delays where you want to do something in between, like check for a button to change the pattern. Small dithering delays are much less troublesome because you normally don't want to do anything between the delays, but you could implement a state machine if you did.

So, the only thing missing now would be the very subtle random "twinkling" or "shimmering" effect running in parallel to the gradient palette transition.

Not done it but I would have thought it was the same as the dithering only on a slightly longer time period. But sorry I haven't got any examples as I have never done that with a strip.

Ok, but considering the (just updated) code above, which does exactly what it should, where would I introduce a delay and, more importantly, why would I need to introduce a delay in my program? Is a delay the only way to obtain temporal dithering? I found no information pertaining to that on the FastLED wiki.

More important than dithering (a rather cosmetic issue for the moment) is the subtle random up/down dimming, while the transition does its thing.

What currently happens (correct me if I am not describing it correctly):

  1. library, variables, setup
  2. loop calls skyEffect
    2a. skyEffect determines the duration of each step, and
    2b. fetches the next colour from the palette, then
    2c. fills the entire strip with that colour, finally
    2d. updates the palette index
  3. loop shows the result

And so forth and so forth, until 10 minutes have passed.

So, the way I understand it, it is between step 2b. and 2c. where, before filling the entire strip, the LEDs must randomly receive brightness information on top of the colour information to appear to "twinkle" or "shimmer". Is that the right way to think about it?

But also, how could I influence the "twinkling" or "shimmering" speed independently of the speed of the palette traversal, which depends on the overall duration? Each of these duration steps is 2.343,75 milliseconds long @ 10 minutes overall duration.

So, the way I understand it, it is between step 2b. and 2c. where, before filling the entire strip, the LEDs must randomly receive brightness information on top of the colour information to appear to "twinkle" or "shimmer". Is that the right way to think about it?

Yes that is right.

how could I influence the "twinkling" or "shimmering" speed independently of the speed of the palette traversal

A bit more tricky. Maybe the best way is to have two led arrays you fill with the colour, and sprinkle your twinkles through one of them.

Then have another task in the state machine that toggles between each array and displays them. This could be done at a rate interdependent of the main effect.

Thanks, ok, so I understood what has to happen now reasonably well. I meanwhile thought that it should/could be done this way:

I write a second function, pulseRandom(), which has a second timer component, just like the first function. This function is called in loop() after the skyEffect() function wrote the colours fetched from the palette to the entire array. Pseudocode below.

In other words, this second function modifies only the brightness of what has previously been written, and at a different interval.

Would that be the right approach? If so, the question would be how a subtle brightness modulation, a bit of up/down dimming, could be done with FastLED's functions, and randomly at that. I spend much time searching within "twinkle" or "sparkle" examples, but found nothing I could learn from for this case. Maybe because I still have some difficulty understanding how FastLED dims/brightens LEDs with a timer. Do you have any ideas how I should proceed, if what I suggest here is a practical way forward?

Thanks!

void pulseRandom() {

  static const float transitionDuration = 2; // Minutes (2 minutes: one step = 0.46875 milliseconds ~ 0.5 seconds)

  static const float interval = ((float)(transitionDuration * 60) / 256) * 1000; // Steps in milliseconds

  EVERY_N_MILLISECONDS(interval) {

    *** RANDOMLY CHANGE THE BRIGHTNESS OF
    *** A RANDOM SELECTION OF LEDS
    *** WITHIN THIS SECOND TIMER

    }

  }
  
} // End of pulseRandom

Searching more, I found a comment on reddit saying that a "wave function" could be used for very slow up/down dimming = "shimmering" and operate on CHSV. Unfortunately, none of this was fleshed out, and the OP's question was different from mine. There seem to be quite few FastLED discussions around on forums that go beyond simply changing the demo effects?

In any case, that "wave function" thing sounds somewhat applicable. But my palette uses CRGB and not CHSV, where one could then modify the "V" of each randomly selected LeD : (

Would that be the right approach?

Sorry no.
You have pushed all the state machine stuff into the skyEffect() function, so it won't return to the loop function for ten minuets.

Shimmering involves a bit of randomness, you can do that by adding a random component to the time the next shimmer occurs.

Ok, that's too bad, because to have a main effect function and then an overlay effect function would have been nice. A bit more modular in the code. Maybe there is a way still to achieve that? What do you think?

Meanwhile, I managed to cobble together pulseRandom, some kind of "shimmer" effect, see below, that at the moment uses a dreaded for-loop. At least, it gets near to the desired behaviour. The up/down dimming is of course much too fast and the fadeToBlackBy() function is no good, since I don't want to go to black; it also changes the colours when approaching zero/black.

#include <FastLED.h>

const byte pinData = 3;
const byte pinClock = 4;

const byte ledCount = 144;
byte maxBrightness = 16;

struct CRGB leds[ledCount];

void setup() {

  LEDS.addLeds<APA102, 3, 4, BGR>(leds, ledCount);
  LEDS.setBrightness(maxBrightness);
  LEDS.setCorrection(Candle);

}

void loop () {

  pulseRandom();

}

void pulseRandom() {

  int i = random8(ledCount);

  if (i < ledCount) leds[i] = CHSV(0, 0, random(255));
  for (int j = 0; j < ledCount; j++) leds[j].fadeToBlackBy(1); // Smaller value = LEDs change colour in fading out : (

  LEDS.show();
  LEDS.delay(1);

}

because to have a main effect function and then an overlay effect function would have been nice. A bit more modular in the code

Thing is, it can't be modular because you are acting on the same LED strip, and what you do in the main function determines what you do in the twinkle effect.

Ok, so then I have to stick to filling the array from a palette AND then modifying the brightness in a single function. Too bad.

Should I use two seperate EVERY_N_MILLISECONDS based code blocks; one for the palette issue (ultra slow timing) and one for the random LED random dimming issue (independent not as slow timing)?

It really is too bad that the FastLED library is hardly documented with simple step-by-step examples of what the functions do, there are countless examples with tons of convoluted disco effects, nearly always derived from the same "demos" apparently, but no basics like how dim specific LEDs or random LEDs. There was no information on Google + either. Do you maybe know some other source? There is this "Gitter" thing on FastLED, but in Chrome or Safari, Gitter page content always vanishes when the page loads.

Maybe it's best to just give this thing up, although it sounded so very simple compared to those bouncy pulsing disco thingies.

AND then modifying the brightness in a single function. Too bad.

No that is not what I said. Brightness is a global function.

Should I use two seperate EVERY_N_MILLISECONDS based code blocks; one for the palette issue (ultra slow timing) and one for the random LED random dimming issue

Yes

Alright. I saw in many of the well-known demos that the brightness was not globally modified, but programmatically addressed some LEDs only, using fadeToBlackBy() or nscale8() or with an array only containing every third LED and so forth, so that not the entire 144 LED strip was affected at the same time. But I need to find more information anyway, because fadeToBlackBy() is not what I can use in my case anyway (only want to have the brightness modulated (= the shimmer) between maybe 128 and 255).

Good to see you saying than EVERY_N_MILLISECOND can be invoked multiple times in parallel. That really sounds like a way forward to have two differently timed animations happening at the same time. I came eventually across this thread on GitHub.

Over night, I also found this thread showing that one can render one animation into one object and another animation into another object, and then fuse both into another object that is then displayed.

Meanwhile, with bits and bobs of FastLED information from all over the place, based on Mark Kriegsman's example, this is a good "shimmer" effect, lava-lamp like or hectic, depending on adjustment. Three issues remaining:

  1. random8() does not seem to be very random (not the biggest issue, but still)
  2. FastLED.delay() slows the shimmer down, but just 100 milliseconds already introduces more and more visible “steppiness” in the dimming and brightening (a fairly big issue) - maybe using EVERY_N_MILLISECONDS to get rid of the delay will alleviate things?
  3. how shimmer() can modulate what is constantly being written into the leds array from the palette (the main issue)
#include <FastLED.h>

const byte pinData = 3;
const byte pinClock = 4;

const byte ledCount = 144;
byte maxBrightness = 255; // Can be changed on the fly; 10k potentiometer, etc.

#define DARKEST_COLOUR CRGB(24,32,24)
#define BRIGHTEST_COLOUR CRGB(178,186,178)
#define DARKEN_LED CRGB(7,9,7)
#define BRIGHTEN_LED CRGB(17,19,17)

enum {ledConstant, ledBrightens, ledDarkens}; // A flag for the LED states
byte ledState[ledCount];

struct CRGB leds[ledCount];

void setup() {
  LEDS.addLeds<APA102, pinData, pinClock, BGR>(leds, ledCount);
  LEDS.setBrightness(maxBrightness);
  LEDS.setCorrection(Candle); // Candle too warm, Tungsten40W too cold - in-between correction how?
  memset(ledState, ledConstant, sizeof(ledState)); // Set all LEDs to constant state
  fill_solid(leds, ledCount, DARKEST_COLOUR);
}

void loop()
{
  shimmer();
  FastLED.show();
  FastLED.delay(50); // Higher number = slower, but introduces "steppiness" : (
}

void shimmer()
{
  for ( byte i = 0; i < ledCount; i++) { // Go through the entire strip
    if ( ledState[i] == ledConstant) { // If LED is in constant state...
      if ( random8() < 3) { // ...randomly select it based on a threshold (higher number = hectic shimmer)...
        ledState[i] = ledBrightens; // ...and set it to brightening state
      }

    } else if ( ledState[i] == ledBrightens ) { // If LED is in brightening state...
      if ( leds[i] >= BRIGHTEST_COLOUR ) { // ...and reached brightest colour...
        ledState[i] = ledDarkens; // ...set it to darkening state
      } else {
        leds[i] += BRIGHTEN_LED; // If not, continue brightening it
      }

    } else {
      if ( leds[i] <= DARKEST_COLOUR ) { // If LED reached darkest colour...
        leds[i] = DARKEST_COLOUR; // ...keep it there...
        ledState[i] = ledConstant; // ...and set it to constant state
      } else {
        leds[i] -= DARKEN_LED; // If not, continue darkening it
      }
    }
  }
}

Sorry for not replying sooner but I am not sure I have anything else to add to this thread.

Edith: In an answer you recently gave another poster, you had leds[value].setHSV( 200,255,brightness); so that seems close to what I need - the difference being that where you directly set the hue (200) and the saturation (255), I would need to keep the values of H and S that are already in the array element from the ultra slow gradient palette writing. If you could let me know how I would accomplish a leds[value].setV(brightness), that would be it.

I made a bit of progress, collecting bits and bobs of information about FastLED spread out all over the place. Sounded very basic at the beginning, but it'll take me a good while to get this done.

There is no brighten(value) or darken(value) function in FastLED, operating only on the "V" (as in HSV) of leds[value], and leds[value].addToRGB(value) will destroy the colour information or r, g or b, or all three, when going over 255; likewise leds[value].subtractFromRGB(value) for the opposite case.

The code below provides a very nice and adjustable shimmer() function, just as intended; but it needs to be run in its own EVERY_N_MILLISECONDS block and relies on pre-defined colours, instead of dealing with the RGB values the sky() function writes into the leds array EVERY_N_SECONDS from the palette.

enum {ledConstant, ledBrightens, ledDarkens}; // A flag for the LED states

...

void shimmer()
{
  for ( byte i = 0; i < ledCount; i++) { // Go through the entire strip
    if ( ledState[i] == ledConstant) { // If LED is in constant state...
      if ( random8() < 7) { // ...randomly select it based on a threshold (higher number = busier shimmer)...
        ledState[i] = ledBrightens; // ...and set it to brightening state
      }

    } else if ( ledState[i] == ledBrightens ) { // If LED is in brightening state...
      if ( leds[i] >= BRIGHTEST_COLOUR ) { // ...and reached brightest colour...
        ledState[i] = ledDarkens; // ...set it to darkening state
      } else {
        leds[i] += BRIGHTEN_LED; // If not, continue brightening it
      }

    } else {
      if ( leds[i] <= DARKEST_COLOUR ) { // If LED reached darkest colour...
        leds[i] = DARKEST_COLOUR; // ...keep it there...
        ledState[i] = ledConstant; // ...and set it to constant state
      } else {
        //leds[i] -= DARKEN_LED; // If not, continue darkening it
        leds[i].subtractFromRGB(10); // If not, continue darkening it; randomise amount?
      }
    }
  }
}

Maybe using the FastLED library is not a good idea? Would you rather try to solve this with the Pololu library or the Adafruit library?