Using Globals or Passing Parameters to a Function

This is a question about best practice when using functions to repeat tasks. The attached sketch has the full code (less the UTFT libraries).

I am trying to create a menu handler for the 3.2" TFT Touch Screen using Henning Karlsen’s UTFT library. I need to pre-define colours for menus with each menu “button” having:

  • a background colour
  • a border colour
  • a text colour
  • a text background colour (= background colour)

I have a struct to hold the RGB values:

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

and a further struct to hold the menu colours as defined above:

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

I can now define various menu colours using the BtnCol struct as shown in this snippet:

    BtnCol vMain = {{0, 32, 0},{34, 139, 34},{144, 238, 144}};    // main (vertical) menu
    BtnCol hSubs = {{12, 78, 28},{56, 144, 64},{144, 238, 144}};  // subsidiary (horizontal) menu
    // etc....

I can create an array of menu items:

    // Up to 6 vertical menu items each of 1 to 9 characters
    const char* vMenuTxt [][6] = {
        {"Date-Time", "Co-ords", "Solar", "Equations", "Setup", "RESET"},  // 6 items
        {"Set Date", "Set Time", "Time Zone", "", "", "BACK"}
        // etc...
    };

…and to each of those items I can assign a colour from the previously-defined Button Colour struct:

    // assign appropriate menu colours to each of the above buttons
    BtnCol vMenu [][6] = {
        {{vMain}, {vMain}, {vMain}, {vMain}, {vMain}, {aWarn}},
        {{vMain}, {vMain}, {vMain}, {aGrey}, {aGrey}, {aSpec}}
    };

Menus are drawn using a generalised function:

void vDrawMenu (int menu, int vMOX, int vMOY, int vMP, int vBH, int vBW, int FW, int FH) {
    myGLCD.drawBitmap (1, 54, 78, 22, bmenu[menu]);
    for (int i=0; i<6; i++) { 
        myGLCD.setBackColor(vMenu[menu][i].fill.r,vMenu[menu][i].fill.g,vMenu[menu][i].fill.b);   // Text Background, index 0
        myGLCD.setColor(vMenu[menu][i].fill.r,vMenu[menu][i].fill.g,vMenu[menu][i].fill.b);       // Button Fill, index 0
        myGLCD.fillRoundRect(vMOX, vMOY + i * vMP, vBW, vMOY + i * vMP + vBH);
   
        myGLCD.setColor(vMenu[menu][i].border.r,vMenu[menu][i].border.g,vMenu[menu][i].border.b); // Button Border, index 1
        myGLCD.drawRoundRect(vMOX, vMOY + i * vMP, vBW, vMOY + i * vMP + vBH);
   
        myGLCD.setColor(vMenu[menu][i].text.r,vMenu[menu][i].text.g,vMenu[menu][i].text.b);       // Text Colour, index 2
        int xpos = vMOX + vBW/2 - strlen(vMenuTxt[menu][i]) * FW/2;
        int vTextOffset = (vBH-FH)/2 + 3;
        myGLCD.print(vMenuTxt[menu][i], vMOX + xpos, vMOY + vTextOffset + i * vMP);
    }
}

All of the function arguments are positional. None of them are to do with the colour definitions. The specification of colours via the arrays previously defined at global level is handled by statements such as:

myGLCD.setColor(vMenu[menu][i].text.r,vMenu[menu][i].text.g,vMenu[menu][i].text.b);

This means that whilst positional information is passed via parameters, colour information is picked up directly from the global values.

I have read that globals ought not to be used this way so the question is:

Given that eventually I want to reduce the above function (as well as others) to a library, is the above way acceptable or are there better ways of organising this code? Or is it just down to personal preference?

(At the moment the button size and position parameters as well as the colours are defined in the sketch. Eventually they will be hived off to a separate file of user-definable values).

Thanks,
Ric

touch_interface_v15.ino (13 KB)

TFT.h (339 Bytes)

Well global state is usually a mistake - as soon as you want more than one touchscreen you'll see why (!)

But you don't want to clutter up each call with such parameters.

So the answer is to provide a class for handling the menus, and the parameters then become instance-variables in the class. (Or to use C++'s nomenclature "private member variables" I think)

MarkT: So the answer is to provide a class for handling the menus, and the parameters then become instance-variables in the class.

I am new to C++ and unfamiliar with classes but am reading up on it. I'm looking at the "Writing a library for the Arduino" tutorial and delving into my C++ book. I can see already that a class is an object and that I can create instances of it. I can already spot the candidates for the member functions or methods and shall do the same for the variables. Not sure yet about constructors but... that's something I can research.

Thanks for the suggestion, Ric

Actually you're kind of onto the constructors too you just don't know it.

The reason you are declaring those things at global scope now is so you can do it at the top of the sketch one time and have it exist throughout the code. That makes obvious sense.

So that's exactly what you would need the constructors for. Say you had two screens and the issue brought up by MarkT. You would need a class and two instances, one for each screen. But you still need to set the actual color codes, the RGB values, that you want to use for each screen and you would like to do that early in the code just like you are doing now. So that would be something that would go into the constructor. The constructor function would take the RGB values as parameters in some certain order and would assign them to the private member variables in their particular instance of the class.

Hi,

Thanks for the heads up on the class constructor. I am finding the Jesse Liberty book very useful. If I understand correctly, the class would not pre-define those colours but provide the "placeholders" (not sure that's the correct term) for those values when an instance of the class is created.

As an aside I am creating some simple classes and trying them out to get the feel of how they work. There are other issues I will need to think about (such as using pointers instead of passing all the values as parameters) but I think I need to learn to walk first before I try running...

Thanks again, Ric