(solved) Fading a RGB strip from one content to another one / Math problem

Hi all,

I´d like to have a possibility, to fade smoothly from one thing I see on a RGB strip right now to something else I want to see in future by calculating and showing some frames in between.

The basic idea is, to check for any color for any pixel, if the targetvalue is bigger or smaller than the current one. And then calculate the single steps - in case of a bigger targetvalue for example:

red=red+((new_red-old_red)/steps_i_want)*current_step

That seems logic to me...?!

I wrote the code, tested it and, well, it "kind of" does what i expect. For some frames its visible, that it comes from the random pattern (old_frame) closer to the defined one (new_frame) - but much faster, than I expected. And after 3 or 4 interations it turns into colorful chaos.

What did I do wrong? Are the bytes/integers the wrong datatypes? How could I solve it with the limited memory the Uno has? Or am I just a moran and the formula/concept itself is the problem?

Any hints are appreciated.

Thanks, Helmuth and sorry for my bad english.

Here is my comlete code:

#include <FastSPI_LED.h>

//that is the length of your strip
#define NUM_LEDS 20

//change the order of your rgb here
struct CRGB { unsigned char r; unsigned char g; unsigned char b; }; 
struct CRGB *leds; 

//to save, what you see right now (current frame)
byte old_frame[NUM_LEDS][3]; 

//to store, what you want to see in future (next frame)
byte new_frame[NUM_LEDS][3]; 

//some tools first:
//set color of a single LED, don´t show
void set_color_led(int adex, int cred, int cgrn, int cblu) {  
  leds[adex].r = cred;
  leds[adex].g = cgrn;
  leds[adex].b = cblu;  
  }

//save what you see right now to old_frame for playing with it later  
void copy_led_array_to_old_frame(){ 
  for(int i = 0; i < NUM_LEDS; i++ ) {
    old_frame[i][0] = leds[i].r;
    old_frame[i][1] = leds[i].g;
    old_frame[i][2] = leds[i].b;
  }  
}

//show something unique
void create_random_pixels(){ //show something unique
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    set_color_led(i, random(255), random(255), random(255));
  }
  FastSPI_LED.show();
}

//fill new_frame with some linear color fade
void fill_new_frame_with_content(){ //some linear fade
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    new_frame[i][0] = i*(255/NUM_LEDS);
    new_frame[i][1] = 255-(i*(255/NUM_LEDS));
    new_frame[i][2] = 0;
  }
}

//make the content of new_frame visible
void show_new_frame(){
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    leds[i].r = new_frame[i][0];
    leds[i].g = new_frame[i][1];
    leds[i].b = new_frame[i][2];
  }
  FastSPI_LED.show(); 
}

//just to compare: show the frame you started with again
void show_old_frame(){
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    leds[i].r = old_frame[i][0];
    leds[i].g = old_frame[i][1];
    leds[i].b = old_frame[i][2];
  }
  FastSPI_LED.show(); 
}

//fade to new content over a numer of steps with wait_ms delay between the steps
void fade_from_old_to_new_frame(int steps, int wait_ms){
  for(int a=0; a < steps; a++){ // over the steps
    for(int b=0; b < NUM_LEDS; b++){ // over the whole lenght
      //modify red
      if (old_frame[b][0] < new_frame[b][0]) 
        {leds[b].r=leds[b].r+(new_frame[b][0]-old_frame[b][0])*a/steps;}
      if (old_frame[b][0] > new_frame[b][0]) 
        {leds[b].r=leds[b].r-(old_frame[b][0]-new_frame[b][0])*a/steps;}
      //modify green
      if (old_frame[b][1] < new_frame[b][1]) 
        {leds[b].g=leds[b].g+(new_frame[b][1]-old_frame[b][1])*a/steps;}
      if (old_frame[b][1] > new_frame[b][1]) 
        {leds[b].g=leds[b].g-(old_frame[b][1]-new_frame[b][1])*a/steps;}
      //and blue
      if (old_frame[b][2] < new_frame[b][2]) 
        {leds[b].b=leds[b].b+(new_frame[b][2]-old_frame[b][2])*a/steps;}
      if (old_frame[b][2] > new_frame[b][2]) 
        {leds[b].b=leds[b].b-(old_frame[b][2]-new_frame[b][2])*a/steps;}
    }
    FastSPI_LED.show(); 
    delay(wait_ms);
  }
}
   
void setup(){
  FastSPI_LED.setLeds(NUM_LEDS);
  //select your chipset according to your strip type here - have a look at FastSPI documentation
  //i´m using a LPD1101 right know who behaves like a LPD6803
  FastSPI_LED.setChipset(CFastSPI_LED::SPI_LPD6803); 
  FastSPI_LED.init();
  FastSPI_LED.start();
  leds = (struct CRGB*)FastSPI_LED.getRGBData(); 
}

void loop() {
  /* just for testing 
  create_random_pixels();  
  copy_led_array_to_old_frame();
  delay(1000);
  fill_new_frame_with_content();
  show_new_frame();
  delay(1000);
  show_old_frame();
  delay(1000);
  show_new_frame();
  delay(1000);
  ok, that seems to work so far*/
  create_random_pixels();  
  copy_led_array_to_old_frame();
  fill_new_frame_with_content();
  fade_from_old_to_new_frame(10,500);  //10 steps with 500 ms delay in between
  delay(1000);  
}

Ok, I used the wrong stuff to calculate with, now it works fine:

#include <FastSPI_LED.h>

//that is the length of your strip
#define NUM_LEDS 20

//change the order of your rgb here
struct CRGB { unsigned char r; unsigned char g; unsigned char b; }; 
struct CRGB *leds; 

//to save, what you see right now (current frame)
byte old_frame[NUM_LEDS][3]; 

//to store, what you want to see in future (next frame)
byte new_frame[NUM_LEDS][3]; 

//some tools first:
//set color of a single LED, don´t show
void set_color_led(int adex, int cred, int cgrn, int cblu) {  
  leds[adex].r = cred;
  leds[adex].g = cgrn;
  leds[adex].b = cblu;  
  }

//save what you see right now to old_frame for playing with it later  
void copy_led_array_to_old_frame(){ 
  for(int i = 0; i < NUM_LEDS; i++ ) {
    old_frame[i][0] = leds[i].r;
    old_frame[i][1] = leds[i].g;
    old_frame[i][2] = leds[i].b;
  }  
}

//show something unique
void create_random_pixels(){ //show something unique
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    set_color_led(i, random(255), random(255), random(255));
  }
  FastSPI_LED.show();
}

//fill new_frame with some linear color fade
void fill_new_frame_with_content(){ //some linear fade
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    new_frame[i][0] = i*(255/NUM_LEDS);
    new_frame[i][1] = 255-(i*(255/NUM_LEDS));
    new_frame[i][2] = 0;
  }
}

//make the content of new_frame visible
void show_new_frame(){
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    leds[i].r = new_frame[i][0];
    leds[i].g = new_frame[i][1];
    leds[i].b = new_frame[i][2];
  }
  FastSPI_LED.show(); 
}

//just to compare: show the frame you started with again
void show_old_frame(){
  for(int i = 0 ; i < NUM_LEDS; i++ ) {
    leds[i].r = old_frame[i][0];
    leds[i].g = old_frame[i][1];
    leds[i].b = old_frame[i][2];
  }
  FastSPI_LED.show(); 
}

//fade to new content over a numer of steps with wait_ms delay between the steps
void fade_from_old_to_new_frame(int steps, int wait_ms){
  for(int a=0; a < steps; a++){ // over the steps
    for(int b=0; b < NUM_LEDS; b++){ // over the whole lenght
      //modify red
      if (old_frame[b][0] < new_frame[b][0]) 
        {leds[b].r=old_frame[b][0]+(new_frame[b][0]-old_frame[b][0])*a/steps;}
      if (old_frame[b][0] > new_frame[b][0]) 
        {leds[b].r=old_frame[b][0]-(old_frame[b][0]-new_frame[b][0])*a/steps;}
      //modify green
      if (old_frame[b][1] < new_frame[b][1]) 
        {leds[b].g=old_frame[b][1]+(new_frame[b][1]-old_frame[b][1])*a/steps;}
      if (old_frame[b][1] > new_frame[b][1]) 
        {leds[b].g=old_frame[b][1]-(old_frame[b][1]-new_frame[b][1])*a/steps;}
      //and blue
      if (old_frame[b][2] < new_frame[b][2]) 
        {leds[b].b=old_frame[b][2]+(new_frame[b][2]-old_frame[b][2])*a/steps;}
      if (old_frame[b][2] > new_frame[b][2]) 
        {leds[b].b=old_frame[b][2]-(old_frame[b][2]-new_frame[b][2])*a/steps;}
    }
    FastSPI_LED.show(); 
    delay(wait_ms);
  }
}
   
void setup(){
  FastSPI_LED.setLeds(NUM_LEDS);
  //select your chipset according to your strip type here - have a look at FastSPI documentation
  //i´m using a LPD1101 right know who behaves like a LPD6803
  FastSPI_LED.setChipset(CFastSPI_LED::SPI_LPD6803); 
  FastSPI_LED.init();
  FastSPI_LED.start();
  leds = (struct CRGB*)FastSPI_LED.getRGBData(); 
}

void loop() {
  /* just for testing 
  create_random_pixels();  
  copy_led_array_to_old_frame();
  delay(1000);
  fill_new_frame_with_content();
  show_new_frame();
  delay(1000);
  show_old_frame();
  delay(1000);
  show_new_frame();
  delay(1000);
  ok, that seems to work so far*/
  create_random_pixels();  
  copy_led_array_to_old_frame();
  fill_new_frame_with_content();
  fade_from_old_to_new_frame(50,100);  //10 steps with 500 ms delay in between
  delay(1000);  
}

:

As I already wrote in the German forum, your formula ("red=red+((new_red-old_red)/steps_i_want)*current_step") does not work with integer math but in your posted code you changed that already. I shortened your fade routine to make it a bit more readable and casted the types where necessary.

void fade_from_old_to_new_frame(int steps, int wait_ms){
  for (int a = 1; a <= steps; a++) { // over the steps
    for (int b = 0; b < NUM_LEDS; b++) { // over the whole lenght
      for (byte c = 0; c < 3; c++) { // for R,G,B
        leds[b].r = (int)leds[b].r + ((int)new_frame[b][c] - (int)old_frame[b][c]) * a /steps;
      }
    }
    FastSPI_LED.show(); 
    delay(wait_ms);
  }
}

Try this a post about the results.

struct CRGB *leds;

This is a pointer. It doesn't point to anything.

void set_color_led(int adex, int cred, int cgrn, int cblu) {  
  leds[adex].r = cred;
  leds[adex].g = cgrn;
  leds[adex].b = cblu;  
  }

This code pretends that leds DOES point to something.

That dosn´t work like it is.

leds[b].r = (int)leds[b].r + ((int)new_frame[b][c] - (int)old_frame[b][c]) * a /steps;

Needs a to be changed to

leds[b].r = (int)old_frame[b][c] + ((int)new_frame[b][c] - (int)old_frame[b][c]) * a /steps;

But how to handle leds__.g and leds**.b?**__

PaulS:

struct CRGB *leds;

This is a pointer. It doesn't point to anything.

void set_color_led(int adex, int cred, int cgrn, int cblu) {  

leds[adex].r = cred;
 leds[adex].g = cgrn;
 leds[adex].b = cblu;  
 }



This code pretends that leds DOES point to something.

Hi PaulS, what do you reccomend to do about it? So far the code works now, what makes a beginner happy...

Greetings, Helmuth

@PaulS: the leds array gets filled by this call:

  leds = (struct CRGB*)FastSPI_LED.getRGBData();

@Helmuth: your right, I didn't realize the structure of the leds array. So here's the code with splitted colors:

void fade_from_old_to_new_frame(int steps, int wait_ms){
  for (int a = 1; a <= steps; a++) { // over the steps
    for (int b = 0; b < NUM_LEDS; b++) { // over the whole lenght
      leds[b].r = (int)leds[b].r + ((int)new_frame[b][0] - (int)old_frame[b][0]) * a /steps;
      leds[b].g = (int)leds[b].g + ((int)new_frame[b][1] - (int)old_frame[b][1]) * a /steps;
      leds[b].b = (int)leds[b].b + ((int)new_frame[b][2] - (int)old_frame[b][2]) * a /steps;
    }
    FastSPI_LED.show(); 
    delay(wait_ms);
  }
}

@pylon

Ok, it took me some beers to understand the trick: all my "if" conditions are obsolete, because the difference turns negative itself - resulting in automatically decreasing the value in case of a smaller targetvalue (new_frame < old_frame). Beautiful math! Thanks for that, that will speed it up.

leds[b].r = (int)leds[b].r + ((int)new_frame[b][0] - (int)old_frame[b][0]) * a /steps;

But the first part after the "=" needs to be the constant same old value:

leds[b].r =  (int)old_frame[b][0] + ((int)new_frame[b][0] - (int)old_frame[b][0]) * a /steps;

We want to fade linear, not progressive. So we increase or decrease always the same starting point - the old color, we started with.

Cheers.