Simple question about arrays[] in a for loop, for RGB LED values

I’m attempting to be able to fade an RGB LED between various, named colours, using PWM (analoigWrite()) and arrays to hold the values for each RGB pin.

I had some troubles with this, getting the error [invalid types ‘int[int]’ for array subscript] until I came across this post on the forum, and the relevant answer:

PeterH:
The formal argument tab is an int. You are trying to treat it as an array of ints. An array is syntactically equivalent to a pointer to an array element, so change your declaration to void init_tableau (int *tab) and it should work.

I now have the problem of my values, in a for loop, going outside the bounds I had expected (i.e 0, 255) - possibly related to using pointers (which I don’t fully understand, but have some knowledge of).

In the code below (and the serial monitor section where the values get out of bounds), can anyone shed any light on how I should approach fixing this? Also, the overloaded for loop seems unwieldy to me, is there a better, more elegant way to approach this?

Many thanks.

int timer = 3000;

int redPin = 9;
int greenPin = 10;
int bluePin = 11;

int cyan[] = {0, 255, 255};
int purple[] = {100, 0, 255};


int colourName;

void setup() {
  Serial.begin(9600);
}

void loop() {

fadeNamedColour(cyan, purple);
//delay(timer);

}


void fadeNamedColour(int *from, int *to) {
  for (int redVal = from[0], greenVal = from[1], blueVal = from[2]; redVal < to[0], greenVal >= to[1], blueVal = to[2]; redVal++, greenVal--, blueVal++) {
    analogWrite(redPin, redVal);
    analogWrite(greenPin, greenVal);
    analogWrite(bluePin, blueVal);

    Serial.print("redVal = ");
    Serial.print(redVal);
    Serial.print("\t greenVal = ");
    Serial.print(greenVal);
    Serial.print("\t blueVal = ");
    Serial.println(blueVal);
    
    //delay(10);
  }
}


void myColour(int *colourName) {
  analogWrite(redPin, colourName[0]);
  analogWrite(greenPin, colourName[1]);
  analogWrite(bluePin, colourName[2]);
}

Serial monitor:

redVal = 15415 greenVal = -15160 blueVal = 255
redVal = 15416 greenVal = -15161 blueVal = 255
redVal = 15417 greenVal = -15162 blueVal = 255
redVal = 15418 greenVal = -15163 blueVal = 255
redVal = 15419 greenVal = -15164 blueVal = 255
redVal = 15420 greenVal = -15165 blueVal = 255
redVal = 15421 greenVal = -15166 blueVal = 255
redVal = 15422 greenVal = -15167 blueVal = 255

Also, the overloaded for loop seems unwieldy to me,

It’s also incorrect, and doesn’t do what you imagine.

for (int redVal = from[0], 
              greenVal = from[1], 
              blueVal = from[2]; 

              redVal < to[0], greenVal >= to[1], blueVal = to[2]; 

              redVal++, greenVal--, blueVal++)

All the comma operator is doing in the conditional section is doing is returning the result of right-most expression, which in this case is an assignment - if “to[2]” is non-zero, this is always true.

As to how to fix it . . . one way would be to use simple fixed-point arithmetic.
Work out the difference between “from” and “to” for each channel first, multiply each by (say) 256 and divide by the number of steps in the fade.
This gives a fractional increment per channel so a little shifting is necessary to extract the integer value from the fixed point.

You can't have multiple conditions in a for() loop using the comma operator. You will just use the first one.

OK, thanks... then my question becomes an even simpler one - 'how does one change multiple values within a for loop', or, if this is a meaningless question, and I should be approaching this in an entirely different way, how do I gradually change multiple values 'at once', considering I want to increment or decrement those values smoothly?

With fixed-point arithmetic - see reply #1

KeithRB:
You can't have multiple conditions in a for() loop using the comma operator. You will just use the first one.

You mis-spelled "last".

OK, cool - this is the first time I've heard of fixed point arithmetic... Will go away and look at that.

When you say 'shifting'

"This gives a fractional increment per channel so a little shifting is necessary to extract the integer value from the fixed point."

are we looking at using something like a 'round' arithmetic function? Or can I just 'cast' the float to an int?

Oh, and, say, uh, Cousin AWOL, I suppose it'd be the acme of foolishness to inquire if you had a hair net?

Imagine you have to step, say the red channel from 0 to 8, in 256 steps.
Now, represent your red channel in a sixteen bit integer, initialised to 0 (your start intensity)
If you repeated add 8 to the sixteen bit value, but only write the most significant eight bits to the PWM, after 32 increments of 8, your sixteen bit value will contain 32 x 8 = 256 (0x100), so 1 gets written to the red channel and the channel gets a little brighter.
After 256 steps, the sixteen bit values contains 256 x 8 = 2048 (0x800) and you write 8 to your red channel.
There, you smoothly faded from 0 to 8.

OK, thanks… I understand, just, what you’re saying above, but I’m still confused, and a way-away from implementing it.

I’m only just starting to get my head around decimal and binary values / arithmetic, but realise it’s a fundamental concept, particularly when dealing with LEDs, which I’m keen to learn - so appreciate your taking the time to help with this so far, and hope I can take a little more.

I can see how repeatedly adding 8 to 0 32 times gives me 256, (0x100, B100000000).

I am happy incrementing values in a for loop with hex or binary values:

for (int i = 0x0; i < 0x100; i++) {
  analogWrite(bluePin, i);
  delay(10);
}

for (int i = B0; i < B11111111; i++) {
  analogWrite(redPin, i);
  delay(10);
}

Bearing in mind I am using a different pin for each of the LEDs in the 4-pin RGB LED I’m using, and that analogWrite() has a range of 256 steps (0, 255), I’m not understanding:

a) how I can write 2048 (0x800) to the red pin (channel?) (as it’s far bigger than 255).
b) how I can increment this without using a for loop.
c) how it would work with multiple pins on an RGB LED (and therefore multiple for loops…?)

I’m sure I’m missing something very basic and fundamental.

OK, just found this, which looks like it’s doing what I want to do, Am I right to say it’s using the method you have given, AWOL…?

https://www.arduino.cc/en/Tutorial/ColorCrossfader

a) how I can write 2048 (0x800) to the red pin (channel?) (as it's far bigger than 255).

You can't - that's where the shift I mentioned comes in.

[quoteb) how I can increment this without using a for loop.][/quote] I didn't say you couldn't use a for loop, though if you want to use a blink without delay approach, you can't use a for loop, and you need to control your variables explicitly.

c) how it would work with multiple pins on an RGB LED (and therefore multiple for loops..?)

You reduce all the fades to the same number of steps, and use a single loop.

OK, I’m slowly getting there!

question a) how I can write 2048 (0x800) to the red pin (channel?) (as it’s far bigger than 255).

AWOL:
You can’t - that’s where the shift I mentioned comes in.

We’re talking bitwise operators >> << etc? OK, I’m new to that too, but good…

question b) how I can increment this without using a for loop?

I didn’t say you couldn’t use a for loop, though if you want to use a blink without delay approach, you can’t use a for loop, and you need to control your variables explicitly.

Blink Without Delay, state machines - another concept I need get a firm grasp of - I wondered about that (here, and with for loops in general). Would I then be using the ‘natural’ looping of the loop() method instead of a for loop, and use the millis() function and timers?

question c) how it would work with multiple pins on an RGB LED (and therefore multiple for loops…?)

You reduce all the fades to the same number of steps, and use a single loop.

Ahah! I didn’t understand that previously. I think this is the big issue here that I need to wrestle with.

Thank you, you’ve given me enough clues to start working with, I hope I can come back with some progress and probably a couple more questions. Cheers!

In general, how discouraged should I be that this seems quite over my head, considering I've come to Arduino and Computer Science from cold a couple of months ago, and am only working through the built-in examples just now...?

Or, hopefully, to put it another way - how encouraged should I be that I understand some of this, considering I've come to Arduino and Computer Science from cold a couple of months ago, and am only working through the built-in examples just now...?

:wink:

You just need to learn the language. Get a good C++ book.

mowgli88:
OK, I’m slowly getting there!

question a) how I can write 2048 (0x800) to the red pin (channel?) (as it’s far bigger than 255).
We’re talking bitwise operators >> << etc? OK, I’m new to that too, but good…

A bitwise shift left can be considered as simply a multiplication by an integer power of two, and similarly a bitwise shift right can be considered as a division by an integer power of two.

Thus, 28 is 256, so a left shift by eight places is equivalent to multiplication by 256, and a right shift by eight places is equivalent to division by 256.
So, 2048 >> 8 = 2048 / 28 = 2048 / 256 = 8.

You can actually write it as analogWrite (redPin, redValue / 256);, and the compiler will almost certainly convert this to a shift operation (because a shift is usually quicker than a division).