Issue: hex and RGB color are not the same I'm sending colors from an

Issue: hex and RGB color are not the same

I’m sending colors from an Smartphone App to my Arduino.
The color is send as a hex value.
For example I send the hexValue = 0x123456 (1193046 as dec)
Then I light up the LEDs with that value:
leds[i] = 1193046;
Well I want to be able to save this value, so that the LEDs light up like I set it the last time.
Because each memory adress can hold one byte I save this color value as RGB value.
Color “translation” is done by:
red = (hexValue >> 16) & 255; // red
green = (hexValue >> 8) & 255; // green
blue= (hexValue) & 255; // blue

When I now use:
leds[i].setRGB(red, green, blue);
The leds turn to a different value.
Especially the darker colors are even darker when I use RGB color.
Whats going on there and how can I prevent it?

Hi- I suspect you’re seeing something related to plain old integer wrap-around in C, arising from the fact that on AVR Arduino’s default “integer” size is just 16 bits. Often intermediate results get passed through a 16-bit integer and unexpectedly truncated. For example:

unsigned int i = 40000;
unsigned long int sum = i + i;
Serial.println( sum );

You’d expect this code to print 80000, because “i” is big enough to hold “40000”, and “sum” is big enough to hold “80000”. However, if you run this on an AVR Arduino, it prints 14464. This is because the “+” operator on the second line only produces a 16-bit result ((40000 + 40000) % 65536) and then that value is stored into “sum”, and printed.

If you change line 2 (above) to say

  unsigned long int sum = (unsigned long)(i) + (unsigned long)(i);

then the code prints the expected result, because now the “+” operator knows that it’s adding two 32-bit values, and not to truncate the result to 16 bits. It’s sort of a pain in the bit.

I think in order to diagnose your specific code a little more deeply, I’d ask for two things. First, could you share a block of your code that’s having the trouble, showing us the data type (“int”, “long unsigned int”, etc) of all the relevant variables?

And second, could you instrument the code to print out the R, G, and B values that you are getting? That might help pinpoint the problem.

As one last item, you can also retrieve the RGB color values from a pixel like this:
uint8_t red, green, blue;
red = leds[i].red;
green = leds[i].green;
blue = leds[i].blue;

So share some code, and some printed-out values of what you’re seeing, and we’ll help get it all working.

Thanks for you quick reply.

Well the Code I have is really specific and I worked half an year on it, so it will be hard to get the important stuff for others.

I checked everything with a simple test code and found out, that not RGB translation is the problem.

After saving the RGB values I do a litte effect and light the LEDs up with increasing the v-Value of this color “translated” to HSV.

I checked it with the Hex value: 0x123456
translated to decimal: 1193046
translated to RGB: 18, 52, 86
translated to HSV: 149, 201, 86

You can check it with this simple arduino Code with you LEDs connected to Pin13 and shortened GNDs:

#include <FastLED.h>
#define NUM_LEDS 30
#define DATA_PIN 13
struct CRGB leds[NUM_LEDS];

void setup()
{
Serial.begin(38400);

LEDS.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);

for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i].setRGB(0, 0, 0);
}
LEDS.show();

Serial.println(“Setup complete”);
}

void loop()
{
Serial.println(“HEXcolor”);
for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i] = 0x123456;
}
LEDS.show();

delay(3000);

Serial.println(“DECcolor”);
for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i] = (unsigned long int) 1193046;
}
LEDS.show();

delay(3000);

Serial.println(“RGBcolor”);
for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i].setRGB(18, 52, 86);
}
LEDS.show();
delay(3000);

Serial.println(“HSVcolor”);
for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i] = CHSV( 149, 201, 86 );
}
LEDS.show();

delay(3000);
}

The FastLED HSV-to-RGB conversion has a built-in gamma correction of 2.0, which means that the Value scale is not linear. Instead it is adjusted to be closer to visually linear, or to put it another way, it comes out darker.

If you want, you can just perform the brightness effect adjustments in RGB space, or alternatively pre-adjust your HSV values to have a higher V. If you like, I can give you a formula for that.

It would be great if you could give me a formula to bring the HSV to the level of the RGB colors.

The reason I want it that way (an not adjust RGB to HSV) is because I my App to control the LEDs uses HexValues and everything is adjusted to that (values of sliders in the app etc.)
.

But if you also know how to to the other way then I am interessted in it as well (to adjust RGB to the HSV colors).

Earlier you wrote this:
I checked it with the Hex value: 0x123456
translated to decimal: 1193046
translated to RGB: 18, 52, 86
translated to HSV: 149, 201, 86

The first three lines are all literally identical; they’re just different ways of writing down the same numbers:
0x12 (18 decimal) for red,
0x34 (52 decimal) for green, and
0x56 (86 decimal) for blue.

However, the last line that you wrote says that you “translated” the color to HSV. And HSV color space is totally different from RGB color space.

So… how did you do that “translation”? What algorithm did you use? And what HSV color space are you translating into? There are multiple HSV color spaces, see https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors for discussion of classic “spectrum” versus “rainbow”.

In order to help you get the right formula you’re looking for, I need to know where you got the “HSV” values to start with. Please let me know if I can help clarify the question, too!

Also, just to be clear, when you say “hex values” you mean “hex RGB values”, right?

More details: in most applications, “Hue” is represented as a number of ‘degrees’ around a color wheel, 0…359. In FastLED, “Hue” is represented as a number of ‘binary steps’ around the color wheel, ranging from 0…255. So if you’re getting Hue from some outside source, you need to perform a mapping. You can do that by multiplying the Hue360 by 256/360 (0.711111) to get the Hue256. You’ll still be in a spectrum color space, but that’s OK, as you can do this:

CHSV hsv256color;
CRGB rgbcolor;
hsv2rgb_spectrum( hsv256color, rgbcolor);

The default assignment from a CHSV to a CRGB uses the hwv2rgb_rainbow method, so the hues won’t quite line up.

Again, this all comes back to: where are you getting your HSV values from? What algorithm is doing the translation from/to RGB, and what HSV color space (spectrum or rainbow or ?) is it using?

Happy to help figure it all out.

As for cancelling out the gamma correction… if you want to cancel out the gamma correction of “2.0” that happens automatically inside the HSV to RGB conversion (which is implied here)

leds[i] = hsvcolor; // implied hsv2rgb_rainbow conversion

then you need to apply a function to the V beforehand to cancel it out, like this:

hsvcolor.value = sqrt16( hsvcolor.value * 256);

Aside from some loss-of-precision in the low bits, this should cancel out most of the built-in gamma correction.

We still need to get the color space and hue range straightened out, but this may help with the gamma correction on the Value.

Ok, that were a lot of information.

Well I know that my first three lines are the same color.
I just wanted to show you that there is no mistake at receiving and using the hex value.
For example I could have made the mistake to store this value as an integer (instead of long).
To be precise in that: With hex value I do mean the RGB hex value. (for me the hex color 0xRRGGBB are directly connected to RGB colorspace - its just another notation.)

To your question about the “translating algorthm”:
I translate the RGB values in the only way that I know and as seen on wikipedia:

You can easyly check it out and convert it like…
For example here:
http://www.rapidtables.com/convert/color/rgb-to-hsv.htm

There is of course a Hue range from 0° to 359°.
But I converted this to 0-255.
Also the percentage values of the “RGB to HSV color conversion”-calculator are in a range of 0-255.

The check this you can try this calculator linked above and check it with my values given before:
Hex value: 0x123456
translated to decimal: 1193046
translated to RGB: 18, 52, 86
translated to HSV: 149, 201, 86

And now the harder part for me :slight_smile:

Well thats right, that FastLED has 2 kinds of HSV color spaces.
In the past I already did some checking in both of theese to figure out which one I prefere. The result was, that “rainbow hsv” looks better in my effects.
I do actually have a lot of different effects which are all “based” (looks better) in the rainbow (adjusted) HSV color space.
For example a rainbow fade through the hue looks much better because of better illustration of red and yellow.
But you know that as this was the reason why you created this rainbow HSV color space.

So as far as I understood, I “just” have to cancel out the gamma correction by recalculate the V-Value like:
newV = sqrt16( oldV * 256);
right?

What is that sqrt16() function exactly?
Is it a square root on 16bit-based CPU?

Because all the Arduinos I own has a 8-bit CPU and I only know about the simple sqrt() function of the ArduinoIDE.

I already tested this on my Arduino Uno (with the color given above 0x123456):

byte val = 86;
double newV = sqrt(oldV * 256); // newV is now 148.38
for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i] = CHSV( 149, 201, newV ); // I know the cast is not needed…
}
LEDS.show();

The color is better but there is still a lot of space to:
for (int i = 0 ; i < NUM_LEDS; i++ )
{
leds[i].setRGB(18, 52, 86);
}
LEDS.show();

Thank you so far for the help you gave me!

Thanks for the new info; I’ll take a more detailed look in the morning.

(Subscribing to discussion. Quite interesting!)

I’m a bit busy at work today, but I’ll try to move things along in this discussion as I can, bit by bit.

So here’s one thing: “sqrt16” is a FastLED function which takes a 16-bit integer and returns the 8-bit integer square root. It’s several times faster than the Arduino built-in floating point version (and takes up less code space as well). So if you’re going to convert to an integer result anyway (which you are here), instead of
double newV = sqrt(oldV * 256);
you can just say
uint8_t newV = sqrt16( oldV * 256);
and get smaller and faster code.

(uint8_t is the ANSI/ISO equivalent name for Arduino’s “byte” type.)

Ahh ok.

Well I already tried:

byte OldV = 86;
byte newV = (byte) sqrt(oldV * 256);
Serial.print("newV with sqrt: ");
Serial.println(newV);

newV = (byte) sqrt16(oldV * 256);
Serial.print("newV with sqrt16: ");
Serial.println(newV);

(I know typecast ist not needed …)

Both prints me out that newV is 148.
Even if the sqrt16() funktion is faster an takes less space, the result is sadly the same and the color looks the same with sqrt and with sqrt16.
I know it shouldnt be different, as your explaination was only for me to understand that sqrt16() is a great FastLED function.
I just wanted to give you some feedback.

Waiting for new instructions sir :slight_smile:

Still no idea how to solve it?

Sorry for the delays, and I will try to help out further. “Life” getting in the way a bit last few days! Still thinking about how best to solve the overall problem. Basically we can’t mix HSV color spaces (traditional spectrum vs FastLED rainbow). RGB is more universally the same.

Still thinking about this!

“Life” always comes first.

Hope everything is ok and you find a solution.
Regards!

Any news on this?
Looks like its being a bigger issue then i expected.

I’ll try to take another look today.

I continue to keep coming back to the central idea that there are THREE (or four) different color spaces being used here:

  1. the LEDs’ RGB hardware (presumably the same as FastLED RGB, and remote device RGB)

  2. The remote device HSV model, which is probably some kind of a “spectrum” model, and

  3. FastLED HSV color model (or models, actually, since one is a spectrum and one is a rainbow)

And that we can’t just freely convert from one to the other and back. FastLED provides one kind of conversion, but the other conversions are undefined.

I also keep feeling like I wish I had more of the code to understand, or rather, a clearer picture of the data flow. Eg, “Use inputs RGB values on device. DeviceRGB values converted to DeviceHSV by code on the device. DeviceHSV values then sent to Arduino via Bluetooth. (Etc)”

I’ll be around today and can probably take another look, but I have to admit I’m still not sure I’m working on the right part of the problem, and more information would help.

Talk with you later!