Creating a struct of RGB Values

I am writing a menu handler comprising a number of "buttons" using the UTFT library from here:

http://henningkarlsen.com/electronics/library.php?id=52

The aim is to simplify the creation of a number of menus. For the moment I need only consider the main menu structure which is represented by a single vertical column of "buttons".

This question addresses only the colour information required to display each "button".

Each button requires a number of colour values:

  • a fill colour
  • a border colour
  • a text colour

the background colour for the button fill is always the same as the background colour for the text, so only three parameters are required to define the colour of each button.

The first thing is to define a simple rgb struct in the header file:

#ifndef TFT_h
#define TFT_h

#include <Arduino.h>
// Each triad represents a single RGB colour
struct RGB {
    byte r;
    byte g;
    byte b;
};
#endif

the required rgb values for each colour required to build a button can be defined in a series of arrays:

    RGB vMenuFillCol[7]   = {{0, 32, 0},{12, 56, 28},{0,132,102},{0, 0, 0},{102,102,82},{250,250,250},{148,32,0}};  
    RGB vMenuBorderCol[7] = {{34, 139, 34},{56, 128, 64},{0,164,122},{32, 32,32},{132,132,102},{255,255,255},{164,36,12}};
    RGB vMenuTextCol[7]   = {{144, 238, 144},{144, 238, 144},{144,248,153},{144, 144, 126},{32,32,28},{0,0,0},{255,255,255}};

These 3 arrays define a set of different colours that can be assigned to each button

The text for each button (there are 6 vertical buttons always) can be defined by an array:

    char* MenuTxt [][6] = {
        {"Date-Time", "Co-ords", "Solar", "Equations", "Setup", "RESET"},  // up to 6 items for the main menu
        {"Set Date", "Set Time", "Time Zone", "", "", "BACK"}
    };

For the first main (vertical) menu the colour of each button can be defined using an array of rgb values:

    RGB MenuCol [3][6] = {
        {{vMenuFillCol[0]}, {vMenuFillCol[1]}, {vMenuFillCol[2]}, {vMenuFillCol[3]}, {vMenuFillCol[4]}, {vMenuFillCol[5]}},  // up to 6 items or greyed-out
        {{vMenuBorderCol[0]}, {vMenuBorderCol[1]}, {vMenuBorderCol[2]}, {vMenuBorderCol[3]}, {vMenuBorderCol[4]}, {vMenuBorderCol[5]}},
        {{vMenuTextCol[0]}, {vMenuTextCol[1]}, {vMenuTextCol[2]}, {vMenuTextCol[3]}, {vMenuTextCol[4]}, {vMenuTextCol[5]}}
    };

The above array defines the colours for the first set of menu buttons only (creating arrays for each set of menus (each comprising column of vertical buttons) is a problem to be solved later). For testing, most of the available colours (there are 7) are assigned to the 6 menu buttons. This bizarre arrangement of colours would never happen in practice, only in test....

I can define a menu function as follows:

void vDrawMenu (int menu, int vMOX, int vMOY, int vMP, int vBH, int vBW) {
    for (int i=0; i<6; i++) { 
        myGLCD.setBackColor(MenuCol[0][i].r,MenuCol[0][i].g,MenuCol[0][i].b); // Text Background, index 0
        myGLCD.setColor(MenuCol[0][i].r,MenuCol[0][i].g,MenuCol[0][i].b);       // Button Fill, index 0
        myGLCD.fillRoundRect(vMOX, vMOY + i * vMP, vBW, vMOY + i * vMP + vBH);
        myGLCD.setColor(MenuCol[1][i].r,MenuCol[1][i].g,MenuCol[1][i].b);       // Button Border, index 1
        myGLCD.drawRoundRect(vMOX, vMOY + i * vMP, vBW, vMOY + i * vMP + vBH);
        myGLCD.setColor(MenuCol[2][i].r,MenuCol[2][i].g,MenuCol[2][i].b);       // Text Colour, index 2
        int xpos = vMOX + vBW/2 - strlen(MenuTxt[menu][i]) * MenuFontWidth/2;
        int vMenuTextOffsetY = (vBH-MenuFontHeight)/2 + 3;
        myGLCD.print(MenuTxt[menu][i], vMOX + xpos, vMOY + vMenuTextOffsetY + i * vMP); // 8 = v offset to centre vertically
    }
}

The function can be called as follows:

    vDrawMenu (0, vMenuOrigX,vMenuOrigY,vMenuPitch,vBtnHeight,vBtnWidth);

No colour information is passed by the function call, only positional information. Colour information is available via globals.

So far so good. It works and although somewhat clumsy )and probably at this stage far from good practice) it delivers the correct colour values for drawing each button. The full code is attached for those that wish to run it (but it requires a 3.2" TFT Touch screen to display the results, hence my extracting the key bits of the code to explain what I have been doing).

The problem

I am new to C++ and feeling my way into it. But this is an initial stab at a solution as well as an opportunity to learn.

Problems started when I tried to create another block of colours for the next menu (in this example it will be the menu starting:

{"Set Date", "Set Time", "Time Zone", "", "", "BACK"}

I first of all tried expanding the MenuCol array to a 3D array. I was never able to address the the 3D array correctly and colours were presented seemingly at random, even to the point of presenting colours that were to do with positional information. Clearly I was adressing the array incorrectly and visting areas of memory outside the new 3D array. I decided to put that aside...

Reading up on the inadvisability of creating 3D arrays in C++ I began to think there would be two better solutions:

  • using vectors
  • using a struct to hold the button information

Being new to C++ I realised a lot of learning would be required to implement vectors. Even then I was not sure it would be a good fit towards creating a generalised menu handler.

I opted to try the Struct approach.

I changed the header file as follows:

#ifndef TFT_h
#define TFT_h

#include <Arduino.h>
// Each triad represents a single RGB colour
struct RGB {
    byte r;
    byte g;
    byte b;
};

// Each triad of RGB colours represents a single fill, border and text colour respectively
struct BtnCol {
	struct RGB fill;
	struct RGB border;
	struct RGB text;
};
#endif

struct BtnCol should now hold the triad of RGB values required to define each button. I also hoped it would result in a more readable display of each colour.

The next step is to create such a triad of rgb values:

    BtnCol main = {{0, 32, 0},{34, 139, 34},{144, 238, 144}};    // main (vertical) menu

This creates a compile error whcih I have been unable to resolve:

c:/program-arduino/arduino-1.0.1/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../../avr/lib/avr6/crtm2560.o:(.init9+0x0): warning: internal error: out of range error

There may be other (better) ways to find a solution to assigning rgb values to each of the three elements that define a button's colour values and I will be interested to hear about those. The immediate question is: Why do I get the out of range error and how should I go about assigning values to what is, essentially, a struct of structs?

I hope I have made everything clear.

Thanks,
Ric

Note <ITDB02_Touch.h> is not needed in this example and can be commented-out.

touch_interface_v13.ino (13.9 KB)

TFT.h (339 Bytes)

UTFT.h (6.1 KB)

UTFT.cpp (58.3 KB)

I'm really not sure, but maybe rename "main" to something else?

If you try this code on this website http://codepad.org :

struct RGB {
 uint8_t r;
 uint8_t g;
 uint8_t b;
};

struct BtnCol {
 RGB fill;
 RGB border;
 RGB text;
};

BtnCol main_menu = {{0, 32, 0},{34, 139, 34},{144, 238, 144}};

int main()
{
 printf("%d", (int) main_menu.fill.g);
}

It's working perfectly.

Yes, it works....

Clearly "main" is a reserved word in C++ and I had used it to reference something completely different. I keep forgetting I am working in C++ as well as the Arduino environment.

Now that this error has been corrected I still need to work out how to dereference the struct to derive the individual rgb values. Suggestions?

Thanks for your help guix.
Ric

Sorry for late reply, I didn't see your edit.

ricm:
I still need to work out how to dereference the struct to derive the individual rgb values. Suggestions?

I don't understand what you mean, explain better, with pseudo code for example :slight_smile:

I still need to work out how to dereference the struct to derive the individual rgb values. Suggestions?

Do you mean like this?

BtnCol main_menu = {{0, 32, 0},{34, 139, 34},{144, 238, 144}};

//... Use main_menu somewhere
analogWrite( 3, main_menu.fill.r );

If you need a 32-bit colour, define your struct as follows.

struct RGB {

 operator uint32_t&(){ return *( uint32_t* ) this; }

 uint8_t a;
 uint8_t r;
 uint8_t g;
 uint8_t b;
};

do you really need 24 bits per color?

byte color = B11111100

is a 8 bit RGB

3 red
3 green
2 blue

8 bits per pixel gets you 256 colors and is much easier to deal with as each color is a handy to deal with single byte

Osgeld:
do you really need 24 bits per color?

For my purposes, no. 8 bit would indeed be god enough because only solid blocks/lines of colour are used and a 256 palette is probably going to look good anyway. The 3.2" TFT defaults to 24 bit colour but colour depth can be specified in the init section of the library.

Thank you for the suggestion.

Ric

guix:

struct RGB {

uint8_t r;
uint8_t g;
uint8_t b;
};

On revisiting this question I realise I had overlooked your use of uint8_t instead of byte. What was your reason for this?

Thanks,
Ric

pYro_65:
Do you mean like this?

BtnCol ..... etc

analogWrite( 3, main_menu.fill.r );

Yes, this is the bit:

main_menu.fill.r

Thanks!

ricm:
On revisiting this question I realise I had overlooked your use of uint8_t instead of byte. What was your reason for this?

It's the same thing, but with "byte" doesn't compile on codepad.org :slight_smile:

ricm:
Yes, this is the bit: main_menu.fill.r

What is your problem, if you still have one?

guix:
What is your problem, if you still have one?

None. Apologies for not making that clear. I was pointing out that the syntax I was looking for was of the form "main_menu.fill.r"