Convert HTML color code or RGB888 to RGB565

Hi,

I bought an Arduino Uno a few days ago and i'm very new to C/C++ programming. I'm trying to make a function that transforms HTML color code to RGB565 color code.

My function is called RGB888toRGB565.

For example: RGB888toRGB565(FFFFFF); should return 0xFFFF.

Or if you prefer, I'm trying to convert 24 bit color code (RGB888) to 16bit color code (RGB565).

So far i've done this:

int RGB888toRGB565 (String RGB888)
{
  int RGB565;
  String r,g,b;
  char rc[3];
  char gc[3];
  char bc[3];
  unsigned int R,G,B;

  //Separamos cada canal de color en una variable tipo String para poder usar .substring()
  r = RGB888.substring(0,2);
  g = RGB888.substring(2,4);
  b = RGB888.substring(4,6);  

  //Ahora lo convertimos a char para poder usarlo con strtoul()
  r.toCharArray(rc,3);
  R = strtoul(rc, NULL, 16);
  g.toCharArray(gc,3);
  G = strtoul(gc, NULL, 16);
  b.toCharArray(bc,3);
  B = strtoul(bc, NULL, 16);

  //Dividimos para convertir el formato RGB888 a RGB565
  R = (R / 5);
  G = (G / 6);
  B = (B / 5);

  //???
  
  return(RGB565);
}

I think the function so far it's not very hard to understand. I get the HTML/RGB888 color code, for example FF11EE and I split the color channels into variables. FF to r, 11 to g, and EE to b. Later I have to convert the String to a Char so I can use strtoul() wich will transform my ASCII hex code into real Hex values. Once we have the values in unsigned ints (R,G,B), we divide them by the bits per channel of the RGB565 format, so R/5, G/6, and B/5.

And right there i'm blocked. Now I would need to somehow put R,G and B together in a var called RGB565 and return it. It will be 16 bit long so unsigned int for RGB565 should work.

Thanks in advance

uint16_t RGB888toRGB565(const char *rgb32_str_)
{
  long rgb32=strtoul(rgb32_str_, 0, 16);
  return (rgb32>>8&0xf800)|(rgb32>>5&0x07e0)|(rgb32>>3&0x001f);
}

You are welcome (:

JarkkoL: uint16_t RGB888toRGB565(const char *rgb32_str_) {  long rgb32=strtoul(rgb32_str_, 0, 16);  return (rgb32>>8&0xf800)|(rgb32>>5&0x07e0)|(rgb32>>3&0x001f); }

You are welcome (:

Wow that's awesome compared to what I was doing lol Thanks :D

But it doesn't work 100% for me.

FFFFFF shows up like blue 000000 , FF0000, 00FF00, 0000FF, 00FFFF and 555555 shows up correctly FFFF00 shows up like black but if look very closely it's not completly black but i'm not sure

What could be the problem?

I'm sending commands to an uLCD from 4Dsystems via the serial port. I must send the colors in 2 bytes, msb:lsb. I separate the 2 bytes like this:

    Serial.write(color / 256);
    Serial.write(color % 256);

Where color is an int, it's supposed to be what your function returns.

Also sorry for my bad english.

I actually tested the conversion code with "FFFFFF" input and it gave 0xffff as the result so that's unlikely the problem. You should verify it though. Try to make the function always return 0xffff and see if you get white color. If you do, then change the function back and feed it always with "FFFFFF" to see if you still get white. This way you can locate the issue in the rough 3 areas of code

JarkkoL:
I actually tested the conversion code with “FFFFFF” input and it gave 0xffff as the result so that’s unlikely the problem. You should verify it though. Try to make the function always return 0xffff and see if you get white color. If you do, then change the function back and feed it always with “FFFFFF” to see if you still get white. This way you can locate the issue in the rough 3 areas of code

Found the issue!

My function was taking your function as an integer instead of an unsigned integer.

This is my testing code for finding the issue (fixed):

const int RED_PIN = 9;
const int GREEN_PIN = 10;
const int BLUE_PIN = 11;

void setup()
{
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
  Serial.begin(9600);
  delay(500);
  InitDisplay();
  LandscapeMode();
  EraseScreen();
}



void loop()
{
  ChangeBackgroundColor(0xFF,0xFF);
  delay(2000);
  ChangeBackgroundColorNew(RGB888toRGB565("FFFFFF"));
  delay(2000);
}



uint16_t RGB888toRGB565(const char *rgb32_str_)
{
  long rgb32=strtoul(rgb32_str_, 0, 16);
  return (rgb32>>8&0xf800)|(rgb32>>5&0x07e0)|(rgb32>>3&0x001f);
}


void ChangeBackgroundColorNew(unsigned int color) //Here was the issue, just added 'unsigned' to the parameter
{
  do
  {
    Serial.write(0x42);
    Serial.write(color / 256);
    Serial.write(color % 256);
  } while(uLCD()!=0x06);
}


void ChangeBackgroundColor(int color1, int color2)
{
  do
  {
    Serial.write(0x42);
    Serial.write(color1);
    Serial.write(color2);
  } while(uLCD()!=0x06);
}


void LandscapeMode()
{
  do
  {
    Serial.write(0x59);
    Serial.write(0x04);
    Serial.write(0x02);
  } while(uLCD()!=0x06);
}


void EraseScreen()
{
  do
  {
    Serial.write(0x45);
  } while(uLCD()!=0x06);
}


void InitDisplay()
{
  do
  {
    Serial.write(0x55);
  } while(uLCD()!=0x06);
}


int uLCD()
{
    int reply = 0x00;
    int timeout = 0;
    while(Serial.available() == 0 && timeout < 10000)
    {
      timeout++;
      SetLED(0x00);
      delayMicroseconds(500);
    }
    reply = Serial.read();
    SetLED(reply);
    Serial.flush();
    timeout = 0;
    return(reply);
}


void SetLED (int reply)
{    
  if (reply == 0x15) {
    digitalWrite(RED_PIN, HIGH);
    delay(100);
    digitalWrite(RED_PIN, LOW);
  } else if (reply == 0x06) {
    digitalWrite(GREEN_PIN, HIGH);    
    delayMicroseconds(25);
    digitalWrite(GREEN_PIN, LOW);
  } else {
    digitalWrite(BLUE_PIN, HIGH);
    digitalWrite(BLUE_PIN, LOW);
  }
}

Thanks! It’s much easier for me to work with HTML color codes, at least it has separate color channels.

You could also try this:

uint16_t RGB888toRGB565(const char *rgb32_str_)
{
  typedef union {
    uint16_t integer;
    struct{
      uint8_t low;
      uint8_t high;
    };
  } Byter;
  
  byte red;
  byte green;
  Byter rgb16;
                          
  green = hexToNibble(rgb32_str_[2])<<4;
  green |= hexToNibble(rgb32_str_[3])&0xC;
  rgb16.low = hexToNibble(rgb32_str_[4])<<4;
  rgb16.low |= hexToNibble(rgb32_str_[5])&0x8;
  rgb16.high = hexToNibble(rgb32_str_[0])<<4;
  rgb16.high |= hexToNibble(rgb32_str_[1])&0x8;
  rgb16.low >>= 3;
  rgb16.integer |= (green << 3);
  
  return rgb16.integer;
}
inline byte hexToNibble(char hex) {
  if (hex & _BV(6)){
    hex += 9;
  }
  return hex;
}

While it looks a bit more complex, it is a lot faster and smaller in final code size than the other as it avoids the strtoul function. This version of the function takes up 92bytes of flash and takes about 4us to run. This compared to the 1kB that the strtoul function takes up.

Sorry, did a quick reply in my previous post

code is:

unsigned int convertRGB(unsigned long rgb){ //convert 24 bit RGB to 16bit 5:6:5 RGB

  return(((rgb&0xf80000)>>8)|((rgb&0xfc00)>>5)|((rgb&0xf8)>>3));
}