Page 1 of 1
Forum

Welcome to the Tweaking4All community forums!
When participating, please keep the Forum Rules in mind!

Topics for particular software or systems: Start your topic link with the name of the application or system.
For example “MacOS X – Your question“, or “MS Word – Your Tip or Trick“.

Please note that switching to another language when reading a post will not bring you to the same post, in Dutch, as there is no translation for that post!



Gradient with movin...
 
Share:
Notifications
Clear all

[Solved] Gradient with moving ball

37 Posts
2 Users
0 Reactions
11.4 K Views
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

Hello everyone, hello Hans,

I reall like the fastled color gradient function. And I was wondering if it is possible to have a static gradient with a moving color from left to right?
So the gradient will be there the whole time. And another narrow color will run down the gradient (like a ball) without having the gradient move.

My first idea was to combine the modified "meteorrain" effect with a gradient. But the fuction "fade to black" respectively "fade to background color" only works for a single color, not a gradient.

Thanks for any suggestions.


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 

Hi Trace 😊 

Well, instead of FadeToBlack you could try this function (fadeTowardColor - I found it here):

// Helper function for fadeTowardColor that blends one uint8_t toward another by a given amount
void nblendU8TowardU8( uint8_t& cur, const uint8_t target, uint8_t amount)
{
  if( cur == target) return;
  
  if( cur < target ) {
    uint8_t delta = target - cur;
    delta = scale8_video( delta, amount);
    cur += delta;
  } else {
    uint8_t delta = cur - target;
    delta = scale8_video( delta, amount);
    cur -= delta;
  }
}

// Blend one CRGB color toward another CRGB color by a given amount.
// Blending is linear, and done in the RGB color space.
// This function modifies 'cur' in place.
CRGB fadeTowardColor( CRGB& cur, const CRGB& target, uint8_t amount)
{
  nblendU8TowardU8( cur.red,   target.red,   amount);
  nblendU8TowardU8( cur.green, target.green, amount);
  nblendU8TowardU8( cur.blue,  target.blue,  amount);
  return cur;
}

 

As far as I can see the function call would then be:

leds[x] = fadeTowardColor( leds[x], TargetColor, Amount);

 

So meteor rain would look something like this:

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) {  
  setAll(0,0,0);
 
  for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) {
   
    // fade brightness all LEDs one step
    for(int j=0; j<NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10)>5) ) {
        leds[j] = fadeTowardColor(leds[j], targetColor, meteorTrailDecay );        
      }
    }
   
    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j <NUM_LEDS) && (i-j>=0) ) {
        leds[i-j] = CRGB(red, green, blue);
      }
    }
   
    showStrip();
    delay(SpeedDelay);
  }
}

 

The Target color, since you're using a gradient, I'd keep track of the gradient in a separate array, so we can reference that second array as target colors.

For example:

CRGB leds[NUM_LEDS]; // original LEDs array
CRGB GradientLeds[NUM_LEDS]; // reference array for gradient colors

...
// Fill "GradientLeds" with the gradient colors
...

leds[x] = fadeTowardColor( leds[x], GradientLeds[x], Amount);

 

Filling the reference array can be done with the usual FastLED functions, and the target color would then be taking from the reference array.

So you could rewrite (for example) the TheatreRainbowChase as such to fill that reference array:

void rainbowCycle(int SpeedDelay) {
  byte *c;
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< NUM_LEDS; i++) {
      c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
      GradientLeds[i] = CRGB(*c, *(c+1), *(c+2)); // changed this line
    }
    // showStrip(); // no  longer needed
    // delay(SpeedDelay); // not sure if we would still need this
  }
}

// used by rainbowCycle and theaterChaseRainbow
byte * Wheel(byte WheelPos) {
  static byte c[3];
 
  if(WheelPos < 85) {
   c[0]=WheelPos * 3;
   c[1]=255 - WheelPos * 3;
   c[2]=0;
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   c[0]=255 - WheelPos * 3;
   c[1]=0;
   c[2]=WheelPos * 3;
  } else {
   WheelPos -= 170;
   c[0]=0;
   c[1]=WheelPos * 3;
   c[2]=255 - WheelPos * 3;
  }

  return c;
}

 

So when this I'd first update the theatre chase rainbow colors and the call the meteor rain functions:

rainbowCycle(20); // update rainbow colors (or whatever function you have in mind)
meteorRain(0xff,0xff,0xff,10, 64, true, 30);

 

 Just a thought ... hope this gets you in the right direction.


   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

@hans

Hi Hans, the speed in which you are wrap your head around such things is astonishing.

I had a hard time rewriting the code, but I think I figured it out. The problem is, I don´t know what I have done :D

I now have a dark strip at the beginning with a yellow (chosen color) meteor running down and a fading trail to red.
It seems "rainbowCycle" doesn´t show up at all.
And how can I change the color inside "rainbowCycle" to have the gradient I want (red at the beginning and green at the end - for example)?

Here is the Code (pls also read below the code):

#include "FastLED.h"
#define NUM_LEDS 65
CRGB leds[NUM_LEDS];
CRGB GradientLeds[NUM_LEDS];
#define PIN 5

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(5);
}

void loop() 
{
  rainbowCycle(20);
  meteorRain(0xf7,0xff,0x00, 10 ,60 ,true, 30);
  
} 

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) 
{  

  for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) 
  {
    // fade color to background color for all LEDs
    for(int j=0; j < NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10) > 5) ) {
        leds[j] = fadeTowardColor(leds[j], GradientLeds, meteorTrailDecay ); 
      }
    }

    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j < NUM_LEDS) && (i-j >= 0) ) {
        leds[i-j]= CRGB(red, green, blue);
      }
    }
   
    FastLED.show();
    delay(SpeedDelay);
  }
}

// Functions from Kriegsman example
CRGB fadeTowardColor( CRGB& cur, const CRGB& target, uint8_t amount)
{
  nblendU8TowardU8( cur.red,   target.red,   amount);
  nblendU8TowardU8( cur.green, target.green, amount);
  nblendU8TowardU8( cur.blue,  target.blue,  amount);
  return cur;
}

// function used by "fadeTowardColor"
void nblendU8TowardU8( uint8_t& cur, const uint8_t target, uint8_t amount)
{
  if( cur == target) return;
  
  if( cur < target ) {
    uint8_t delta = target - cur;
    delta = scale8_video( delta, amount);
    cur += delta;
  } else {
    uint8_t delta = cur - target;
    delta = scale8_video( delta, amount);
    cur -= delta;
  }
}

void rainbowCycle(int SpeedDelay) {
  byte *c;
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< NUM_LEDS; i++) {
      c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
      GradientLeds[i] = CRGB(*c, *(c+1), *(c+2)); 
    }
  }
}

// used by rainbowCycle and theaterChaseRainbow
byte * Wheel(byte WheelPos) {
  static byte c[3];
 
  if(WheelPos < 85) {
   c[0]=WheelPos * 3;
   c[1]=255 - WheelPos * 3;
   c[2]=0;
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   c[0]=255 - WheelPos * 3;
   c[1]=0;
   c[2]=WheelPos * 3;
  } else {
   WheelPos -= 170;
   c[0]=0;
   c[1]=WheelPos * 3;
   c[2]=255 - WheelPos * 3;
  }

  return c;
}

 

Maybe I was not clear with what effect I want:
Do you remember when we have changed the MeteorRain effect from "fading to black" to a specific background color. But this time, it is not a single background color, it is a gradient.

That´s why I thought it could be possible to rewrite this code from background color to gradient.

Here is the Meteor Code you have written with fading to background color:

#include "FastLED.h"
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
#define PIN 6

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
}

void loop() 
{
  meteorRain(CRGB(0x10,0x00,0x00), CRGB(0xff,0xff,0x00),10 ,64 ,true, 30);
} 

void meteorRain(CRGB ColorBackground, CRGB ColorMeteor, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) 
{  
  // set background color
  fill_solid( leds, NUM_LEDS, ColorBackground );

  for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) 
  {
    // fade color to background color for all LEDs
    for(int j=0; j < NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10) > 5) ) {
        leds[j] = fadeTowardColor(leds[j], ColorBackground, meteorTrailDecay ); 
      }
    }

    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j < NUM_LEDS) && (i-j >= 0) ) {
        leds[i-j]= ColorMeteor;
      }
    }
   
    FastLED.show();
    delay(SpeedDelay);
  }
}

// Functions from Kriegsman example
CRGB fadeTowardColor( CRGB& cur, const CRGB& target, uint8_t amount)
{
  nblendU8TowardU8( cur.red,   target.red,   amount);
  nblendU8TowardU8( cur.green, target.green, amount);
  nblendU8TowardU8( cur.blue,  target.blue,  amount);
  return cur;
}

// function used by "fadeTowardColor"
void nblendU8TowardU8( uint8_t& cur, const uint8_t target, uint8_t amount)
{
  if( cur == target) return;
  
  if( cur < target ) {
    uint8_t delta = target - cur;
    delta = scale8_video( delta, amount);
    cur += delta;
  } else {
    uint8_t delta = cur - target;
    delta = scale8_video( delta, amount);
    cur -= delta;
  }
}

 

 

 


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 

Hi Trace,

apologies for the late response,... Mother's Day came in between 😊 
Thank you for the compliment - it is kind-a what I do for work (when I have a job) 😊 

At first glance, I did see a typo in the meteorRain() function;

leds[j] = fadeTowardColor(leds[j], GradientLeds, meteorTrailDecay );

 

This references to the entire GradientLeds array, and this is incorrect. So this should be:

leds[j] = fadeTowardColor(leds[j], GradientLeds[j], meteorTrailDecay );

 

I have to do a few groceries today and when I get back I'll do some testing ...

So the ideal: gradient-color background, with the meteor rain going over it.
Did you have a particular gradient in mind? Just so I take the right one right away when doing a little testing 😉 


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 

Alrighty ... I did some experimenting and this is what I got so far:

1. Create a gradient (function "makeGradient").

I call this function once in setup() since you mentioned a gradient (assuming a static gradient).
You can do anything you'd like here.

I've used the FastLED function fill_gradient_RGB() - this function takes minimum 2 and maximum 4 colors, and builds a gradient automatically.
We then store this in our reference array GradientLeds.

2. We run the meteor rain effect as usual.

In the effect, we use fade to color instead of fade to black.

Give it a try 😊

Note:

  • I picket random colors for the gradient (darker blue to a darker red) - up to you to play with it an see what looks best 😊 
  • My strip uses pin 6 and has 60 LEDs (you used pin5 and had 65 LEDs)
#define FASTLED_INTERNAL    // just used to mute the Pragma messages when compiling
#include "FastLED.h"

#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
CRGB GradientLeds[NUM_LEDS];
#define PIN 6

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  makeGradient();
}

void loop() 
{
  meteorRain(0xf7,0xff,0x00, 10 ,60 ,true, 30);  
} 

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) 
{  

  for(int i = 0; i < NUM_LEDS+NUM_LEDS; i++) 
  {
    // fade color to background color for all LEDs
    for(int j=0; j < NUM_LEDS; j++) {
      if( (!meteorRandomDecay) || (random(10) > 5) ) {
        leds[j] = fadeTowardColor(leds[j], GradientLeds[j], meteorTrailDecay ); 
      }
    }

    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j < NUM_LEDS) && (i-j >= 0) ) {
        leds[i-j]= CRGB(red, green, blue);
      }
    }
   
    FastLED.show();
    delay(SpeedDelay);
  }
}

// Functions from Kriegsman example
CRGB fadeTowardColor( CRGB& cur, const CRGB& target, uint8_t amount)
{
  nblendU8TowardU8( cur.red,   target.red,   amount);
  nblendU8TowardU8( cur.green, target.green, amount);
  nblendU8TowardU8( cur.blue,  target.blue,  amount);
  return cur;
}

// function used by "fadeTowardColor"
void nblendU8TowardU8( uint8_t& cur, const uint8_t target, uint8_t amount)
{
  if( cur == target) return;
  
  if( cur < target ) {
    uint8_t delta = target - cur;
    delta = scale8_video( delta, amount);
    cur += delta;
  } else {
    uint8_t delta = cur - target;
    delta = scale8_video( delta, amount);
    cur -= delta;
  }
}

void makeGradient() {
  // make a gradient (can take up to 4 colors, just using 2 for this example)
  fill_gradient_RGB(GradientLeds, NUM_LEDS, CRGB(0,0,10), CRGB(10,0,0));
  
  // Copy the gradient to the actual leds and show it.
  for(int i=0; i<NUM_LEDS; i++) {
    leds[i] = GradientLeds[i];
  }
  FastLED.show();
}

   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

@hans

Hi Hans. No worries, I have spend time with my Mom too :D

I have seen the typo too. And I was thinking "damn, this cant be so hard". But I see there still is some basic knowledge missing in my mind when it comes to coding.

The idea to call the gradient function first was also in my mind. But didn´t thought about putting it in an extra void. And I didn´t knew how to do it and writing it the way it can be read from the meteor function.

Can´t test it today, cause the Arduino is broken. Need to wait for a new one. It should be here on Wednesday.

But I already see a question popping in my head. In the Gradient void, you write:

leds[i] = GradientLeds[i]

 

But in the MeteorVoid you write:

 leds[j] = fadeTowardColor(leds[j], GradientLeds[j], meteorTrailDecay )

Shouldn´t it bot be [i] or both [j] ?


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 

No worries Trace 😊 

So in the Gradient() function, I've first filled our reference array with the colors we'd like to see when there is no meteor effect running.
Since this is the first call, and no effect has been active yet, I copy the reference array to the actual leds array.

Also note that this function is called only once: during setup.
So the GradientLeds array remains static and is used each time as the "fade to" color.

Doing this is in a separate function, is something I came up with while testing.
I do like making separate functions when it makes sense. This makes the code easier to read and easier to test. 😊 

In void loop() we call meteor rain over and over again.

The use of "i" or "j" depends on what we chose in the for-loop. This can be anything we like, as long as we define it.

So in the Gradient function we say:

for(int i=0; i<NUM_LEDS; i++) {

 

The "int i=0;" tells it that we use "i" as the counter.

In the meteor rain function we say:

for(int j=0; j < NUM_LEDS; j++) {

 

So "int j=0;" tells us to use "j" as the counter.

This could have been more consistent of course.

basic knowledge and experience will grow over time 😉 


   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

@hans

Hi Hans,

thanks for your code and explanation. The basics of "i" and "j" is clear. I just don´t understand why we use "GradientLeds[i]" in the Gradient and in the Meteor we use "GradientLeds[j]".

Your Code works flawlessly. Thank you so much. It is always easier to undestand something when you got the working code.

I changed some bits here and there to fit the code to my needs. But there is still something I don´t get my head around.

The first thing is that the gradient isn´t smooth. I got blocks of colors instead a linear blend (dark red, to orange, to yellowish, to lightgreen, to green). Can I somewhere put "linear" in it as a blend type, like you can when using "fill_palette".

And the second thing is. I have added a button function (not very elegant), where I want to light up other LEDs on the strip with a push button (independently from the gradient and meteor).

This works fine within this sketch:

#include <FastLED.h>

#define NUM_LEDS  70
#define LED_PIN   5

#define buttonPin1 11
#define buttonPin2 10
#define buttonPin3 9
#define buttonPin4 8
#define buttonPin5 7

int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;
int buttonState5 = 0;

CRGB leds[NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(10);
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  pinMode(buttonPin3, INPUT_PULLUP);
  pinMode(buttonPin4, INPUT_PULLUP);
  pinMode(buttonPin5, INPUT_PULLUP);
}

void loop() {

   
  CRGB startColor( CRGB( 255, 0, 0));
  CRGB endColor( CRGB (0, 255, 0));

  int startPos = 0; // start gradient at first pixel
  int endPos   = 65-1; // end gradient at last pixel

  fill_gradient_RGB( leds, startPos, startColor, endPos, endColor);  


      buttonState1 = digitalRead(buttonPin1);       // read the state of the pushbutton value
  if (buttonState1 == LOW) { 

    
      CRGB startColor( CRGB( 0, 0, 0));
  CRGB endColor( CRGB (0, 0, 0));
  fill_gradient_RGB( leds, startPos, startColor, endPos, endColor); 


  }
  
  leds[66] = CRGB(0, 0, 0);
      buttonState2 = digitalRead(buttonPin2);       // read the state of the pushbutton value
  if (buttonState2 == LOW) { 
    leds[66] = CRGB(0, 0, 255);
  }

    leds[67] = CRGB(0, 0, 0);
      buttonState3 = digitalRead(buttonPin3);       // read the state of the pushbutton value
  if (buttonState3 == LOW) { 
    leds[67] = CRGB(255, 0, 255);
  }

      leds[68] = CRGB(0, 0, 0);
      buttonState4 = digitalRead(buttonPin4);       // read the state of the pushbutton value
  if (buttonState4 == LOW) { 
    leds[68] = CRGB(255, 255, 0);
  }

      leds[69] = CRGB(0, 0, 0);
      buttonState5 = digitalRead(buttonPin5);       // read the state of the pushbutton value
  if (buttonState5 == LOW) { 
    leds[69] = CRGB(0, 255, 255);
  }
 
  FastLED.show();

}

 

But implemented into the "Gradient plus Meteor" sketch, I don´t know where to put it. The way I did it now can´t work, cause I call the function before the meteor function. So the push button only works when the button is pushed before the meteor starts and the LED stays on as long as it takes for the meteor to decay.
But I want it so whenever i push the button, the LED lights up, releasing the button and the LED goes off.

This is the Gradient plus Meteor sketch with not working push buttons:

#include <FastLED.h>

#define NUM_LEDS  70
#define LED_PIN   5
#define GRADIENT 64

#define buttonPin1 11
#define buttonPin2 10
#define buttonPin3 9
#define buttonPin4 8
#define buttonPin5 7


int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;
int buttonState5 = 0;

CRGB leds[NUM_LEDS];
CRGB GradientLeds[GRADIENT];


void setup() {
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(5);
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  pinMode(buttonPin3, INPUT_PULLUP);
  pinMode(buttonPin4, INPUT_PULLUP);
  pinMode(buttonPin5, INPUT_PULLUP);
  makeGradient();
}


void loop() 
{
  buttonfunction();
  meteorRain(0xfc,0xff,0xa6, 10 ,60 ,false, 30);
    
 
} 

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) 
{  

  for(int i = 0; i < GRADIENT+GRADIENT; i++) 
  {
    // fade color to background color for all LEDs
    for(int j=0; j < GRADIENT; j++) {
      if( (!meteorRandomDecay) || (random(10) > 5) ) {
        leds[j] = fadeTowardColor(leds[j], GradientLeds[j], meteorTrailDecay ); 
      }
    }

    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j < GRADIENT) && (i-j >= 0) ) {
        leds[i-j]= CRGB(red, green, blue);
      }
    }
   
    FastLED.show();
    delay(SpeedDelay);
  }

}

// Functions from Kriegsman example
CRGB fadeTowardColor( CRGB& cur, const CRGB& target, uint8_t amount)
{
  nblendU8TowardU8( cur.red,   target.red,   amount);
  nblendU8TowardU8( cur.green, target.green, amount);
  nblendU8TowardU8( cur.blue,  target.blue,  amount);
  return cur;
}

// function used by "fadeTowardColor"
void nblendU8TowardU8( uint8_t& cur, const uint8_t target, uint8_t amount)
{
  if( cur == target) return;
  
  if( cur < target ) {
    uint8_t delta = target - cur;
    delta = scale8_video( delta, amount);
    cur += delta;
  } else {
    uint8_t delta = cur - target;
    delta = scale8_video( delta, amount);
    cur -= delta;
  }
}

void makeGradient() {


  // make a gradient (can take up to 4 colors, just using 2 for this example)
  fill_gradient_RGB(GradientLeds, GRADIENT, CRGB(255,0,0), CRGB(0,255,0));
  
  // Copy the gradient to the actual leds and show it.
  for(int i=0; i<GRADIENT; i++) {
    leds[i] = GradientLeds[i];
  }
 
  FastLED.show();
  }



void buttonfunction() {

    leds[66] = CRGB(0, 0, 0);
      buttonState2 = digitalRead(buttonPin2);       // read the state of the pushbutton value
  if (buttonState2 == LOW) { 
    leds[66] = CRGB(0, 0, 255);
  }

    leds[67] = CRGB(0, 0, 0);
      buttonState3 = digitalRead(buttonPin3);       // read the state of the pushbutton value
  if (buttonState3 == LOW) { 
    leds[67] = CRGB(255, 0, 255);
  }

      leds[68] = CRGB(0, 0, 0);
      buttonState4 = digitalRead(buttonPin4);       // read the state of the pushbutton value
  if (buttonState4 == LOW) { 
    leds[68] = CRGB(255, 255, 0);
  }

      leds[69] = CRGB(0, 0, 0);
      buttonState5 = digitalRead(buttonPin5);       // read the state of the pushbutton value
  if (buttonState5 == LOW) { 
    leds[69] = CRGB(0, 255, 255);
  }
  FastLED.show();
}


   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

@hans

Damn, I feel like a Hacker god from the 90´ :D Why you ask? Well I found the place to put in the push buttons. I just deleted the Button Void and put the buttons in between here:

    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j < GRADIENT) && (i-j >= 0) ) {
        leds[i-j]= CRGB(red, green, blue);
      }
    }
   
    FastLED.show();
//////////////////
// I have put the push buttons in here and it works just fine
/////////////////
    delay(SpeedDelay);

 

There are only two things left now.....the smooth gradient problem. And if there is a way to also use a push button to turn off the gradient and meteor effect (dark stripe) but still beeing able to light up the other LEDs with those other buttons? So FastLED.Clear(); won´t work.

Doing this was easy before we had the meteor. I have just filled the gradient LEDs with black. This of couse also doesn´t work anymore.


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 

Hi Trace,

Hacker God from the 90's -- there is nothing more cool than that 😁 

The problem with the button, is that we need to read the button state frequently to not miss a button press.
So this is why this (in this code) works best by testing in the meteor function.

To make the gradient disappear, you could either write a function to set the reference gradient to all black.

For example:

void clearGradient() {
  fill_solid(GradientLeds, NUM_LEDS, CRGB::Black);
}

 

This however could come with unwanted and unnecessarily complex situations.

Instead we could also define a global variable (somewhere near the defines, at the top of the code):
Note: start with Gradient enabled (true), or set it to false if you'd like to start with the gradient disabled.

bool GradientON = true;

 

When a button press is detected, we toggle this value (GradientOn = not GradientOn):

GradientON = !GradientOn;

 

The if-loop in meteor rain then can be modified to act accordingly:

  for(int i = 0; i < GRADIENT+GRADIENT; i++) 
  {
    // fade color to background color for all LEDs
    for(int j=0; j < GRADIENT; j++) {
      if( (!meteorRandomDecay) || (random(10) > 5) ) {
        if (GradientON) {
          leds[j] = fadeTowardColor(leds[j], GradientLeds[j], meteorTrailDecay ); 
        } else {
          leds[j] = fadeToBlack(j, meteorTrailDecay );  
        }
      }
    }

 

Note though that when doing it this way, the gradient will slowly fade to black.

You could go to instantly off, but this would make the active meteor disappear right away as well - I think fading away of the gradient would be more elegant (and less problematic).

I see this reply is going to be long haha 🤣 

To get the meteor rain to be black, we could use a global variable, say "meteorColor" which changes when you press the button. This may take some tinkering/experimenting ...

CRGB meteorColor;

...
void setup() {
  ...
  meteorColor = CRGB(0xfc,0xff,0xa6); // initial meteor color
  ...
}

void loop() {
  ...
  meteorRain(meteorColor.red, meteorColor.green, meteorColor.blue, 10 ,60 ,false, 30);
  ...
}


....
// button pressed:
if(meteorColor==CRGB::Black) {
  meteorColor=CRGB(0xfc,0xff,0xa6);
} else {
  meteorColor=CRGB::Black;
}
....
Posted by: @trace

thanks for your code and explanation. The basics of "i" and "j" is clear. I just don´t understand why we use "GradientLeds[i]" in the Gradient and in the Meteor we use "GradientLeds[j]".

You could use "i" for both - which would be more consistent indeed. You'd have to change the for-loop to using "i" then as well (where "j" was used).
It's just a temporary variable. The name (for the Arduino) doesn't matter.

Posted by: @trace

The first thing is that the gradient isn´t smooth. I got blocks of colors instead a linear blend (dark red, to orange, to yellowish, to lightgreen, to green). Can I somewhere put "linear" in it as a blend type, like you can when using "fill_palette".

fillgradient can be used with multiple colors (up to for), so in your example (it can take up to 4 colors):

fill_gradient_RGB(GradientLeds, NUM_LEDS, CRGB(50,0,0), CRGB(255,50,0), CRGB(50,50,0), CGRB(0,50,0));

// I was just guessing the RGB values for dark red, orange, yellowish,to green

 

FastLed does offer more options for a gradient fill (see FastLED documentation) - and I agree it is a little though at times to read that documentation.
You could indeed define blocks with colors, and there is even a fill_rainbow function.
FastLED has a few predefine palette's of colors, which you can apply with fill_palette (documentation).
There is also the option to hardcode your own palette of colors - but you'd have to hand type the values of the array (60 items - yikes).
I'm just not sure if these alternatives will make life any easier 😉 


   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

@hans Hi Hans,

the way I coded the buttons works fine withouth any button press missings. I know it could be more cleaner and more efficient but it works for now and with knowledge comes better coding.

I will try out the turning off gradient/meteor and experiment with different ways.

What I have meant with the blocky colors was not that I wanted those values (yellowish, greenish and so on), it was like it is now. I have just two values (red and green for example) but the outcome is in blocks of colors, not smooth. But I think it has something to do with my low brightness right? That´s fine, cause I only use low brightness for testing, to not burn my USB port. And Iam too lazy to write the FastLED power management lines :D

So when setting it to a higher brightness, the gradient is smooth. But the gradient itself is a bit weird. Insted of a nice gradient between red and green, I get a gradient beginning with red fading to a very light yellow (close to white) and then to green.

The other gradient functions do not use a target array, so i cannot use them, because of this part:

  // Copy the gradient to the actual leds and show it.
  for(int i=0; i<GRADIENT; i++) {
    leds[i] = GradientLeds[i];

 

But maybe it will work better with 4 colors. And yes, the FastLED palette function is great, but again not using a target array, so not usable for this code. Or am I wrong?

I know the way to write my own palette with 16 values, but 60 😲 


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 

Brightness will indeed impact the gradient.
The brighter the gradient is, the more color steps can be used to build a gradient.
Obviously you do not want the gradient too bright otherwise is messes too much with the meteor rain effect.
You may have to tinker a little with the colors to get the most reasonable gradient I guess.

As for power; you only have to disconnect the +5V from the LED strip so it is no longer connected to the Arduino.
Next run the +5V from the LED strip to a separate power supply, and connect the GND (or minus) of that power supply to GND on the Arduino.
But I think you already knew this. 😉 

I ALWAYS do it this way, since I really would hate to blowup a USB bus or even worse 😉 

 

As far as I can see, for any of these functions a target array must be provided.
Granted the documentation does look complex at times.
The target array is sometimes listed as "CRGB *leds" which means as much as a the address of a leds array.

So for example the FastLED palette function:

void fill_palette (CRGB *L, uint16_t N, uint8_t startIndex, uint8_t incIndex, const PALETTE &pal, uint8_t brightness, TBlendType blendType)

 

The first parameter ( CRGB *L ) would be the target array (GradientLeds).
Writing 60 led colors by hand is a pain indeed. Sometimes I abuse Excel for that 😉 


   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

Yeah, I found out it is better to trust the eye instead of values to get the color I want.

Of course it is the better way to power the LEDs by an external device and that´s how I do it, when it gets mounted, but again, Im too lazy to set it up just for testing...maybe I should really do it.

The FastLed documentation is really great and helpfull but indeet, it is a bit confusing, cause I don´t know the programming language well enought to understand the terms.

So when (CRGB*L) is the target array (GradientLeds), then (uint16_t N) is Gradient (aka NUM_LEDS) right?! I will test this out.

I will also try to get just the Gradient (without Meteor) but instead of the Meteor passing by, just the brightness is passing by. So the Gradient is still still, only the brightness shifts from left to right.

And the other thing is to have again just the Gradient and the whole brightness raises and falls (fade in and out). We will see :D


   
ReplyQuote
(@trace)
Estimable Member
Joined: 5 years ago
Posts: 170
Topic starter  

@hans

Hi Hans,

had a hard time figuring out what went wrong. Because I wasn´t able to upload any code to the Arduino and then I was able again and a few times later, I wasnt. It turned out, the USB-cable is broken 🤪 

After that, I did some experiments to be able to turn off the gradient and meteor effect. Indeed I wanted them to turn black instantly, so I did this:

I just put the button function inside the meteorRain void and fill the gradient LEDs with black by button press.

void meteorRain(byte red, byte green, byte blue, byte meteorSize, byte meteorTrailDecay, boolean meteorRandomDecay, int SpeedDelay) 
{  

  for(int i = 0; i < GRADIENT+GRADIENT; i++) 
  {
    // fade color to background color for all LEDs
    for(int j=0; j < GRADIENT; j++) {
      if( (!meteorRandomDecay) || (random(10) > 1) ) {
        leds[j] = fadeTowardColor(leds[j], GradientLeds[j], meteorTrailDecay ); 
      }
    }

    // draw meteor
    for(int j = 0; j < meteorSize; j++) {
      if( ( i-j < GRADIENT) && (i-j >= 0) ) {
        leds[i-j]= CRGB(red, green, blue);
      }
    }


      buttonState1 = digitalRead(buttonPin1);       // read the state of the pushbutton value
  if (buttonState1 == LOW) { 
    fill_solid(leds,GRADIENT, CRGB(0, 0, 0));
    

  }

   
    FastLED.show();

 

What I don´t undestand is, why I had to use  fill_solid(leds, GRADIENT, CRGB(0,0,0)).

Why I can´t just refer to the gradient leds like this: fill_solid(GradientLeds, GRADIENT, CRGB(0,0,0))

When I do this, only the gradient turns black but the meteor remains.....oh wait....just while writing this, I think, I get the answer.....when I refer to all leds first, then the gradient array, it doesn´t matter what those leds are part of (gradient-void or meteor-void). Because I tell the code to set just all LEDs off which are within the amount of the gradient array (in my case the first 64 LEDs). Right?

Now I want to experiment with two other animations. First, to just have the gradient withouth meteor to breath (fade in and out). And the second animation: insted of having the metor running down the strip with a color, having just the gradient getting brighter at the spot of the metor.


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2791
 
Posted by: @trace

What I don´t undestand is, why I had to use  fill_solid(leds, GRADIENT, CRGB(0,0,0)).

Why I can´t just refer to the gradient leds like this: fill_solid(GradientLeds, GRADIENT, CRGB(0,0,0))

When using GradfientLeds, we just changed our set of reference colors where it has to fade to, with each meteor rain step.

So each time a step of the meteor rain is handled, the fading is done to the reference color.
So it would take a few steps before it would reach full black.

When using leds, we instantly make the real leds black - which would interrupt the flow of drawing meteors.

Also note that when doing it this way, this would not change the reference colors, so in a next cycle those reference colors would reappear.
So ideally you'd apply both.

When enabling the gradient again, you'd have to call makeGradient again to get the proper reference colors.

I hope that makes sense 😉 

 

When using other effects (so far we used a static gradient), you'd basically have to make sure that with each step the other effect updates the GradientLeds array (reference colors) instead of the actual leds. This can be quite challenging for more complex effects like a bouncing ball or the fire effect.

So the basic concept would be, assuming each step of an effect can be called individually;

1. Update the GradientLeds array, so the reference colors are what the first effect should do
2. Do a meteor rain step on top of that, which changes the actual colors of the LEDs and keep the reference colors fro step 1 in mind.

Breaking up an effect in individual steps can be tricky though. 😉 


   
ReplyQuote
Page 1 / 3
Share: