Need help with some C code

disappoint

Lifer
Dec 7, 2009
10,132
382
126
I have a microcontroller lighting an RGB LED strip with addressable LEDs doing a sweep back and forth similar to the old Knight Rider TV show (yeah the HOFF lol) Only this will be for holiday lighting not simulating AI automobilies.

For the forward sweep I had these statements:
Code:
      strip.setPixelColor(i, strip.Color(R, G, B)); // turn the pixel on
      strip.setPixelColor(i-1, strip.Color(R/2, G/2, B/2)); // turn the pixel on
      strip.setPixelColor(i-2, strip.Color(R/4, G/4, B/4)); // turn the pixel on
      strip.setPixelColor(i-3, strip.Color(R/8, G/8, B/8)); // turn the pixel on

Which I managed to change into a for loop, noticing that
2^3=8,
2^2=4,
2^1=2 and
2^0=1

so I changed those 4 statements into
Code:
for (int x=0; x<4; x++)  // 4 pixels with dimming trail
    {
      strip.setPixelColor(i-x, strip.Color(R/(pow(2,x)+0.5), G/(pow(2,x)+0.5), B/(pow(2,x)+0.5))); // turn the pixel on
    }
pow(2,x) returns 2 to the power of x (the 0.5 is just to correct rounding errors)

it works fine, and now it's expandable to more than 4 just by changing 1 number and of course I don't need as many statements as pixels

the problem is with going in reverse the statements look like this:

Code:
      strip.setPixelColor(i, strip.Color(R/8, G/8, B/8)); // turn the pixel on
      strip.setPixelColor(i-1, strip.Color(R/4, G/4, B/4)); // turn the pixel on
      strip.setPixelColor(i-2, strip.Color(R/2, G/2, B/2)); // turn the pixel on
      strip.setPixelColor(i-3, strip.Color(R, G, B)); // turn the pixel on

Now to change these 4 statements into a for loop, I'm not sure how to find the mathematical relationship between what is subtracted from i and what r,g and b are divided by.

The other one was obviously 2 to the power of x but this one is eluding me for some reason.

thanks AT braintrust!

Pic of the LEDs sweeping to the left. I picked red (255, 0, 0) for the pic to contrast the white table but the dynamic range of the camera isn't nearly as good as the human eye so it looks better in person. The trailing leds are dimming as they should be. The code works, just not easily expandable without copy pastaing lines of code and modding each one which would suck.

dh5ddYo.jpg



This is the whole function in case you want to see it. Not the whole program though, if you need that for some reason let me know. I'm still working on it so it's messy for now.
Code:
// Sweeps 4 pixels back and forth
void Sweep(uint16_t wait, uint8_t R, uint8_t G, uint8_t B) 
{

  for(uint16_t i=0; i<strip.numPixels(); i++) // sweep from 0 to number of Pixels
  {
/*    strip.setPixelColor(i, strip.Color(R, G, B)); // turn the pixel on
      strip.setPixelColor(i-1, strip.Color(R/2, G/2, B/2)); // turn the pixel on
      strip.setPixelColor(i-2, strip.Color(R/4, G/4, B/4)); // turn the pixel on
      strip.setPixelColor(i-3, strip.Color(R/8, G/8, B/8)); // turn the pixel on
*/
    for (int x=0; x<4; x++)  // 4 pixels with dimming trail
    {
      strip.setPixelColor(i-x, strip.Color(R/(pow(2,x)+0.5), G/(pow(2,x)+0.5), B/(pow(2,x)+0.5))); // turn the pixel on
    }
      strip.show();
      delay(wait);               //wait
      for(int d=0; d<4; d++)
        strip.setPixelColor(i-d, 0); // turn the pixels off
  }
  for(uint16_t i=strip.numPixels(); i>3; --i)  // now reverse the sweep
  {   
      strip.setPixelColor(i, strip.Color(R/8, G/8, B/8)); // turn the pixel on
      strip.setPixelColor(i-1, strip.Color(R/4, G/4, B/4)); // turn the pixel on
      strip.setPixelColor(i-2, strip.Color(R/2, G/2, B/2)); // turn the pixel on
      strip.setPixelColor(i-3, strip.Color(R, G, B)); // turn the pixel on
      strip.show();
      delay(wait);               //wait
      for(int d=0; d<4; d++)
        strip.setPixelColor(i-d, 0); // turn the pixel off
  }

}
 

disappoint

Lifer
Dec 7, 2009
10,132
382
126
Ah, so easy. Can't believe I didn't see it sooner.

All I need to do is decrement another variable as the for loop is incrementing and 2^x that variable rather than the for loop variable.

Well I can't implement it now as I have to do something else first but I'll post again if this works.
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,838
4,817
75
Just so you know, the way to do 2^x in integers is "1<<x".
 

disappoint

Lifer
Dec 7, 2009
10,132
382
126
Just so you know, the way to do 2^x in integers is "1<<x".

Thanks, didn't think to use bit shift to do the same thing.
So is bit shift faster than pow(2,x)? In other words does it use less instruction cycles? Not that I need more speed but bonus if it does. Just curious.

The code works perfectly now:

Code:
// Sweeps 4 pixels back and forth
void Sweep(uint16_t wait, uint8_t R, uint8_t G, uint8_t B) 
{

  for(uint16_t i=0; i<strip.numPixels(); i++) // sweep from 0 to number of Pixels
  {
    for (int x=0; x<4; x++)  // 4 pixels with dimming trail "moving" to the right
    {
      strip.setPixelColor(i-x, strip.Color(R/(1<<x), G/(1<<x), B/(1<<x))); // bit shift (1<<x) left = 2^x
    }
      strip.show();
      delay(wait);               //wait sets the speed of "movement"
      for(int d=0; d<4; d++)
        strip.setPixelColor(i-d, 0); // turn the pixels off
  }
  
  for(uint16_t i=strip.numPixels(); i>3; --i)  // now reverse the sweep
  {  
    for (int x=0, y=3; x<4; x++, y--)  // 4 pixels with dimming trail "moving" to the left
    {
      strip.setPixelColor(i-x, strip.Color(R/(1<<y), G/(1<<y), B/(1<<y))); // bit shift (1<<y) left = 2^y
    }

      strip.show();
      delay(wait);               //wait sets the speed of "movement"
      for(int d=0; d<4; d++)
        strip.setPixelColor(i-d, 0); // turn the pixels off
  }

}
 
Last edited:

Cogman

Lifer
Sep 19, 2000
10,286
147
106
Thanks, didn't think to use bit shift to do the same thing.
So is bit shift faster than pow(2,x)? In other words does it use less instruction cycles? Not that I need more speed but bonus if it does. Just curious.

The code works perfectly now:

Code:
// Sweeps 4 pixels back and forth
void Sweep(uint16_t wait, uint8_t R, uint8_t G, uint8_t B) 
{

  for(uint16_t i=0; i<strip.numPixels(); i++) // sweep from 0 to number of Pixels
  {
    for (int x=0; x<4; x++)  // 4 pixels with dimming trail "moving" to the right
    {
      strip.setPixelColor(i-x, strip.Color(R/(1<<x), G/(1<<x), B/(1<<x))); // bit shift (1<<x) left = 2^x
    }
      strip.show();
      delay(wait);               //wait sets the speed of "movement"
      for(int d=0; d<4; d++)
        strip.setPixelColor(i-d, 0); // turn the pixels off
  }
  
  for(uint16_t i=strip.numPixels(); i>3; --i)  // now reverse the sweep
  {  
    for (int x=0, y=3; x<4; x++, y--)  // 4 pixels with dimming trail "moving" to the left
    {
      strip.setPixelColor(i-x, strip.Color(R/(1<<y), G/(1<<y), B/(1<<y))); // bit shift (1<<y) left = 2^y
    }

      strip.show();
      delay(wait);               //wait sets the speed of "movement"
      for(int d=0; d<4; d++)
        strip.setPixelColor(i-d, 0); // turn the pixels off
  }

}

bitshifiting is on the order of adds and subtracts. In other words, it is about one of the fastest instructions out there.

Though bitshifting is integer only and it looks like you are using a mixture of ints and floats. For the best performance you should pick one or the other and avoid converting between the two (that is where performance is often lost).
 

disappoint

Lifer
Dec 7, 2009
10,132
382
126
bitshifiting is on the order of adds and subtracts. In other words, it is about one of the fastest instructions out there.

Though bitshifting is integer only and it looks like you are using a mixture of ints and floats. For the best performance you should pick one or the other and avoid converting between the two (that is where performance is often lost).

Thanks. I don't see any floats in the new code though. I took out the +0.5 I had in the old pow(2,x)+0.5 code so now it's just the bit shift operator. All the variables are ints now, combination of 8 and 16 bit ints.
 

Cogman

Lifer
Sep 19, 2000
10,286
147
106
Thanks. I don't see any floats in the new code though. I took out the +0.5 I had in the old pow(2,x)+0.5 code so now it's just the bit shift operator. All the variables are ints now, combination of 8 and 16 bit ints.

Int length doesn't matter (well, sort of). If the Uarch you are using is a 32bit or 64bit uarch then feel free to use all 32 or 64 bits of the integers. Using smaller integers won't generally result in faster performance.

The exception here is if you are using integers that are larger than the registers on your system. But most systems now-a-days are 32bit or 64bit. There aren't too many 16 or 8 bit system in play.
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,838
4,817
75
There aren't too many 16 or 8 bit system in play.
But this appears to be one of them. Based on his picture, he's using an Arduino Uno, which uses an 8-bit ATmega328 CPU.

Edit: One more thing. "R/(1<<y)" is equivalent to "(R>>y)". However, the compiler might have been smart enough to figure that out already.
 
Last edited:

disappoint

Lifer
Dec 7, 2009
10,132
382
126
But this appears to be one of them. Based on his picture, he's using an Arduino Uno, which uses an 8-bit ATmega328 CPU.

Edit: One more thing. "R/(1<<y)" is equivalent to "(R>>y)". However, the compiler might have been smart enough to figure that out already.

Yes that's correct, it's an Arduino Uno which is an 8 bit AVR. Although there is an Arduino Due with a 32 bit ARM core processor on the market. Using that would present other problems, as it's I/O pins only output 3.3V, so I would have to step that up to 5V because the LED strip uses WS2812B driver chips (built into the LEDs) and it needs to see a 5V signal on the data line.

There is another reason why I want to use unsigned 8 bit variables in some cases. The RGB values can only be from 0 to 255, having them unsigned 8 bit rolls the values back to 0 if they go over 255.

I changed R/(1<<y) to (R>>y). Thanks!
 

cabri

Diamond Member
Nov 3, 2012
3,616
1
81
another way to avoid all the "calculations" each time, is to do the calculations once and store them in an array.

then reference the array by index where you presently are doing the calculation.