Go Down

Topic: [SOLVED] Slow random dimming of APA102C LEDs (Read 755 times) previous topic - next topic

Lagom

Oct 01, 2018, 09:38 pm Last Edit: Oct 22, 2018, 10:31 am by Lagom
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 http://fastled.io/docs/3.1/modules.html 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!

Grumpy_Mike


Lagom

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.

Grumpy_Mike

#3
Oct 02, 2018, 08:59 am Last Edit: Oct 02, 2018, 10:01 am by Grumpy_Mike
Quote
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

Quote
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
https://github.com/FastLED/FastLED/wiki/FastLED-Temporal-Dithering

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.

Lagom

#4
Oct 02, 2018, 02:31 pm Last Edit: Oct 03, 2018, 11:08 am by Lagom Reason: Updated code with latest findings to not bloat the thread with ever new versions
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.

Code: [Select]
/******* 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

Grumpy_Mike

Quote
when stretched out to one hour, the 256 discrete steps become noticeable
That is the sort of thing that is addressed by colour dithering.


Lagom

#6
Oct 03, 2018, 08:40 am Last Edit: Oct 03, 2018, 11:13 am by Lagom
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?

Grumpy_Mike

Quote
I found "temporal dithering",
Yes that is the same thing.

Quote
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.

Quote
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.

Lagom

#8
Oct 03, 2018, 12:21 pm Last Edit: Oct 03, 2018, 12:27 pm by Lagom
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):

0. library, variables, setup
1. 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.

Grumpy_Mike

Quote
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.

Quote
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.

Lagom

#10
Oct 04, 2018, 06:36 pm Last Edit: Oct 04, 2018, 07:27 pm by Lagom Reason: Added pseudocode
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!

Code: [Select]
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 : (

Grumpy_Mike

Quote
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.

Lagom

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.

Code: [Select]
#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);

}

Grumpy_Mike

Quote
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.


Lagom

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.

Go Up