Speeding up LED transition

I'm trying to make a NeoPixel strip change from yellow to purple in a quick wipe with a gradient. I started frankensteining some code together and found something that works, however, the change from one color to the next is a bit slow. I wanted to speed it up by using millis() instead of the delay() that was in the code before, but nothing really changed when I added that. Is it evident in this code where I messed up? Thanks!

#include <Adafruit_NeoPixel.h>
#define LED_PIN    6
#define LED_COUNT 60
#define YELLOW 5000
#define PURPLE 10000

int period = 1000;
unsigned long time_now = 0;
unsigned long time_1 = 0;
unsigned long time_2 = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(115200);
  strip.begin();
  time_now = millis();  //initial start time
}

void loop() {
  time_now = millis();
if(millis() > time_1 + YELLOW){
        time_1 = millis();
        print_time(time_1);
        colorFade(255, 100, 0, 255); // fade into yellow
    }
   
    if(millis() > time_2 + PURPLE){
        time_2 = millis();
        print_time(time_2);
        colorFade(200, 0, 255, 255); // and then into purple
    }
}

void print_time(unsigned long time_millis){
    Serial.print("Time: ");
    Serial.print(time_millis/1000);
    Serial.print("s - ");
}

void colorFade(uint8_t r, uint8_t g, uint8_t b, uint8_t wait) {
  for(uint16_t i = 0; i < strip.numPixels(); i++) {
      uint8_t curr_r, curr_g, curr_b;
      uint32_t curr_col = strip.getPixelColor(i); // get the current colour
      curr_b = curr_col & 0xFF; curr_g = (curr_col >> 8) & 0xFF; curr_r = (curr_col >> 16) & 0xFF;  // separate into RGB components

      while ((curr_r != r) || (curr_g != g) || (curr_b != b)){  // while the curr color is not yet the target color
        if (curr_r < r) curr_r++; else if (curr_r > r) curr_r--;  // increment or decrement the old color values
        if (curr_g < g) curr_g++; else if (curr_g > g) curr_g--;
        if (curr_b < b) curr_b++; else if (curr_b > b) curr_b--;
        strip.setPixelColor(i, curr_r, curr_g, curr_b);  // set the color
        strip.show();
      }
  }
}

void colorFade(uint8_t r, uint8_t g, uint8_t b, uint8_t wait) {As you can see the original function 'colorFade() has the speed (or the wait/delay) built into it as a parameter, you seemed to have removed this already, so the maximum speed of transition with this method is already achieved. The limitation is still that every step needs to be executed and send (depending on the amount of leds this will slow the program down more), and of course that if a spectrum color changes less compared to one that changes more it will arrive at it's destination much earlier.
What you are doing within loop() is a mystery to me, but if you want a quicker fade you will have to completely re-write the program, if you just want a faster switch between colors this is less of an issue.

Thank you, that makes sense! I'm pretty new to this, so what I was trying to do in loop() with millis() is clearly not...doing anything haha.

Deva_Rishi:
if you want a quicker fade you will have to completely re-write the program, if you just want a faster switch between colors this is less of an issue.

Regarding this, I'm not 100% sure I follow. When you say a faster switch between colors, rather than a quicker fade, do you mean something like a colorWipe? What would I change in that situation?

Here is a bit of code I got running for an LED strip using the Adafruit_NeoPixel library. The code is written for an ESP32 so ignore the esp32 stuff, the principle remains the same.

There are two main tasks (functions) void fDo_LEDs( ) and void fDo_AudioReadFreq(). The last instruction in setup() starts the task void fDo_AudioReadFreq() to do a data read. At task creation the fDo_LEDs( ) stops and waits for data to become available, processes the data, triggers fDo_AudioReadFreq(), and goes back to waiting for the arrival of new data to process.

Quite speedy.

#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include <Adafruit_NeoPixel.h>
#include "AudioAnalyzer.h"
////
/* define event group and event bits */
EventGroupHandle_t eg;
#define evtDo_AudioReadFreq       ( 1 << 0 ) // 1
////
TickType_t xTicksToWait0 = 0;
////
QueueHandle_t xQ_LED_Info;
////
const int NeoPixelPin = 26;
const int LED_COUNT = 24; //total number of leds in the strip
const int NOISE = 10; // noise that you want to chop off
const int SEG = 6; // how many parts you want to separate the led strip into
// const int SerialDataBits = 115200;
const int Priority4 = 4;
const int TaskStack40K = 40000;
const int TaskCore1  = 1;
const int TaskCore0 = 0;
const int AudioSampleSize = 6;
const int Brightness = 180;
const int A_D_ConversionBits = 4096; // arduino use 1024, ESP32 use 4096
////
Analyzer Audio = Analyzer( 5, 15, 36 );//Strobe pin ->15  RST pin ->2 Analog Pin ->36
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
Adafruit_NeoPixel leds = Adafruit_NeoPixel( LED_COUNT, NeoPixelPin, NEO_GRB + NEO_KHZ800 );
////
int FreqVal[7];//create an array to store the value of different freq
////
void setup()
{
  eg = xEventGroupCreate();
  // Serial.begin( SerialDataBits );
  Audio.Init(); // start the audio analyzer
  leds.begin(); // Call this to start up the LED strip.
  clearLEDs();  // This function, defined below, de-energizes all LEDs...
  leds.show();  // ...but the LEDs don't actually update until you call this.
  ////
  xQ_LED_Info = xQueueCreate ( 1, sizeof(FreqVal) );
  //////////////////////////////////////////////////////////////////////////////////////////////
  xTaskCreatePinnedToCore( fDo_AudioReadFreq, "fDo_ AudioReadFreq", TaskStack40K, NULL, Priority4, NULL, TaskCore1 ); //assigned to core
  xTaskCreatePinnedToCore( fDo_LEDs, "fDo_ LEDs", TaskStack40K, NULL, Priority4, NULL, TaskCore0 ); //assigned to core
  xEventGroupSetBits( eg, evtDo_AudioReadFreq );
} // setup()
////
void loop() {} // void loop
////
void fDo_LEDs( void *pvParameters )
{
  int iFreqVal[7];
  int j;
  leds.setBrightness( Brightness ); //  1 = min brightness (off), 255 = max brightness.
  for (;;)
  {
    if (xQueueReceive( xQ_LED_Info, &iFreqVal,  portMAX_DELAY) == pdTRUE)
    {
      j = 0;
      //assign different values for different parts of the led strip
      for (j = 0; j < LED_COUNT; j++)
      {
        if ( (0 <= j) && (j < (LED_COUNT / SEG)) )
        {
          set(j, iFreqVal[0]); // set the color of led
        }
        else if ( ((LED_COUNT / SEG) <= j) && (j < (LED_COUNT / SEG * 2)) )
        {
          set(j, iFreqVal[1]); //orginal code
        }
        else if ( ((LED_COUNT / SEG * 2) <= j) && (j < (LED_COUNT / SEG * 3)) )
        {
          set(j, iFreqVal[2]);
        }
        else if ( ((LED_COUNT / SEG * 3) <= j) && (j < (LED_COUNT / SEG * 4)) )
        {
          set(j, iFreqVal[3]);
        }
        else if ( ((LED_COUNT / SEG * 4) <= j) && (j < (LED_COUNT / SEG * 5)) )
        {
          set(j, iFreqVal[4]);
        }
        else
        {
          set(j, iFreqVal[5]);
        }
      }
      leds.show();
    }
    xEventGroupSetBits( eg, evtDo_AudioReadFreq );
  }
  vTaskDelete( NULL );
} // void fDo_ LEDs( void *pvParameters )
////
void fDo_AudioReadFreq( void *pvParameters )
{
  // int64_t EndTime = esp_timer_get_time();
  // int64_t StartTime = esp_timer_get_time(); //gets time in uSeconds like Arduino Micros.
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDo_AudioReadFreq, pdTRUE, pdTRUE, portMAX_DELAY);
    // EndTime = esp_timer_get_time() - StartTime;
    // log_i( "TimeSpentOnTasks: %d", EndTime );
    Audio.ReadFreq(FreqVal);
    for (int i = 0; i < 7; i++)
    {
      FreqVal[i] = constrain( FreqVal[i], NOISE, A_D_ConversionBits );
      FreqVal[i] = map( FreqVal[i], NOISE, A_D_ConversionBits, 0, 255 );
      log_i( "Freq %d Value: %d", i, FreqVal[i]);//used for debugging and Freq choosing
    }
    xQueueSend( xQ_LED_Info, ( void * ) &FreqVal, xTicksToWait0 );
    // StartTime = esp_timer_get_time();
  }
  vTaskDelete( NULL );
} // fDo_ AudioReadFreq( void *pvParameters )
////
//the following function set the led color based on its position and freq value
void set(byte position, int value)
{
  // segment 0, red
  if ( (0 <= position) && (position < LED_COUNT / SEG) ) // segment 0 (bottom to top), red
  {
    if ( value == 0 )
    {
      leds.setPixelColor( position, 0, 0, 0 );
    }
    else
    {
      leds.setPixelColor( position, leds.Color( value , 0, 0) );
    }
  }
  else if ( (LED_COUNT / SEG <= position) && (position < LED_COUNT / SEG * 2) ) // segment 1 yellow
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color(0, 0, 0));
    }
    else
    {
      leds.setPixelColor(position, leds.Color( value, value, 0)); // works better to make yellow
    }
  }
  else if ( (LED_COUNT / SEG * 2 <= position) && (position < LED_COUNT / SEG * 3) ) // segment 2 pink
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color(0, 0, 0));
    }
    else
    {
      leds.setPixelColor(position, leds.Color( value, 0, value * .91) ); // pink
    }
  }
  else if ( (LED_COUNT / SEG * 3 <= position) && (position < LED_COUNT / SEG * 4) ) // seg 3, green
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else //
    {
      leds.setPixelColor( position, leds.Color( 0, value, 0) ); //
    }
  }
  else if ( (LED_COUNT / SEG * 4 <= position) && (position < LED_COUNT / SEG * 5) ) // segment 4, leds.color( R, G, B ), blue
  {
    // Serial.println( position );
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else //
    {
      leds.setPixelColor(position, leds.Color( 0, 0, value) ); // blue
    }
  }
  else // segment 5
  {
    if ( value == 0 )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0)); // only helps a little bit in turning the leds off
    }
    else
    {
      leds.setPixelColor( position, leds.Color( value, value * .3, 0) ); // orange
    }
  }
} // void set(byte position, int value)
////
void clearLEDs()
{
  for (int i = 0; i < LED_COUNT; i++)
  {
    leds.setPixelColor(i, 0);
  }
} // void clearLEDs()

The function leds.setPixelColor() takes in 3 values to make color. The value numbers quantity is also relevant to light intensity produced.

If you look up the color code of orange, rgb(255,165,0), a ratio can be had to create orange light.

I vary the intensity of orange by leds.setPixelColor( position, leds.Color( value, value * .3, 0) ); which changes the light intensity of orange, based upon value for red, a percentage of the value for green, and 0 for blue. Here you can see how I use a bit-o-math to make changing intensity pink light with leds.setPixelColor(position, leds.Color( value, 0, value * .91) );

Hope that makes sense. You can fade by using just leds.setPixelColor.

in your situation. you need your code to trigger on a much shorter time increment.

i would reccomend getting your millis timer going at something small

then use counters inside to keep track of what you are doing and to make the changes that happen less often.

this is untested so i cant see it work but this concept should help

int c;
int tick;
unsigned long last;
int mytimes [2] = {100,50};
void setup() {}
void loop() {
  if(millis()-last>100){ last=millis();
  
  // everthing below now happens 
  // once every 100 milliseconds
  
     tick++;
     if(tick>mytimes[c]){c++;tick=0;if(c>1){c=0;}}
   
     if(c==0){colorFade(255, 100, 0, 255);} // fade into yellow
     if(c==1){colorFade(200, 0, 255, 255);}// fade into purple
    
   }
 

}

Regarding this, I'm not 100% sure I follow. When you say a faster switch between colors, rather than a quicker fade, do you mean something like a colorWipe? What would I change in that situation?

Let me see if i can explain like this. every time through loop() the wait time increases up to 9ms * 255 steps is nearly 2.5secs, and when wait time is 0, the time to complete a colorfade is just a bit more than 255 times the time that it takes to send signal to 60 LEDS which probably comes down to something around 2 ms * 255 = 500ms (approximately, we can measure if that is required or we can calculate, 50us break + 60(LEDS) * 24(bits) * 1.2us (800KHz) so around 1800us i am not sure i got the calculation exactly right, something like that, that is still half a second to fade to the new color at maximum speed, and the speed limitation is caused by the need to pass through every step.

#include <Adafruit_NeoPixel.h>
#define LED_PIN    6
#define LED_COUNT 60

Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(115200);
  strip.begin();
}

void loop() {
  static uint8_t wait = 0;
  colorFade(255, 100, 0, wait); // fade into yellow
  colorFade(200, 0, 255, wait); // and then into purple
  wait++; 
  wait = wait % 10; // let the wait/delay fall back to 0 above 9ms
}

void colorFade(uint8_t r, uint8_t g, uint8_t b, uint8_t wait) {
  for (uint16_t i = 0; i < strip.numPixels(); i++) {
    uint8_t curr_r, curr_g, curr_b;
    uint32_t curr_col = strip.getPixelColor(i); // get the current colour
    curr_b = curr_col & 0xFF; 
    curr_g = (curr_col >> 8) & 0xFF; 
    curr_r = (curr_col >> 16) & 0xFF;  // separate into RGB components

    while ((curr_r != r) || (curr_g != g) || (curr_b != b)) { // while the curr color is not yet the target color
                            // in this example the biggest value change is in the Blue (0 - 255)
                            // which means that the while loop will be executed 255 times 
                            // and so will the delay
      delay(wait);
      if (curr_r < r) curr_r++; 
      else if (curr_r > r) curr_r--;  // increment or decrement the old color values
      if (curr_g < g) curr_g++; 
      else if (curr_g > g) curr_g--;
      if (curr_b < b) curr_b++; 
      else if (curr_b > b) curr_b--;
      strip.setPixelColor(i, curr_r, curr_g, curr_b);  // set the color      
      strip.show();
    }
  }
}

Btw what i think is pretty cool about this colorfade is that it fades every pixel individually so if the pixels are in a pattern as a consequence of a previous bit of code they fade to the one color individually, that is actually pretty cool.

Deva_Rishi:
Let me see if i can explain like this. every time through loop() the wait time increases up to 9ms * 255 steps is nearly 2.5secs, and when wait time is 0, the time to complete a colorfade is just a bit more than 255 times the time that it takes to send signal to 60 LEDS which probably comes down to something around 2 ms * 255 = 500ms (approximately, we can measure if that is required or we can calculate, 50us break + 60(LEDS) * 24(bits) * 1.2us (800KHz) so around 1800us i am not sure i got the calculation exactly right, something like that, that is still half a second to fade to the new color at maximum speed, and the speed limitation is caused by the need to pass through every step.

Thank you, this makes sense! I appreciate the help everyone :slight_smile:

Tools make jobs easier. Here's a tool description that should make your life much easier..

Using the marketing coding language..

void shiftPixel(byte r,byte g,byte b) {

// First shift all the pixels over by one.
for (n=numPixels-1;n>0;n--)
copy color from n-1 pixel to pixel n

// put r,g,b into pixal 0
}

Now all you have to do is figure out what color to feed into the zero end and it'll color wipe it all the way down.

Hope this help.. makes sense.

-jimlee

watermelan:
Thank you, this makes sense! I appreciate the help everyone :slight_smile:

Great, now since you are fading from 1 static color to another (all leds are the same color) what you can also do, is record the start time of the fade, record the starting color, and with the destination color and the time you want the fade to take, calculate what the color should be every time the function gets executed. Of course this is what i mean by a completely different method. Like this you can skip steps and speed up the fade beyond it's current minimum.