Touchscreen buttons - failing to find correct touch points / buttons

Apologies for my failings - simply cannot understand what I’m doing wrong here.
Got a 3.5" TFT touchscreen - ILI9486 controller I believe.
Using the MCUFRIEND_kbv library and <touchscreen.h>.

My aspirations are that the first menu will offer three buttons which in turn will lead to further screens with their own buttons.

In the attached code you will see that void setup creates a ‘loading’ page followed by a call to drawHome, the first page with buttons for three other applications.
The ‘loading’ screen works as does the call to drawHome with three buttons working in as much as they appear to link to currentpage 1, currentpage2 and currentpage3 o.k.

I show the code for currentpage 2 in the main loop. The currentpage 2 page displays fine - with all the buttons. However, I simply cannot get the buttons to work. I’m sure it has something to do with the x, y points being missing or wrong - but I fail to understand.

I’m new and struggling with coding so any help would be appreciated - I appreciate the code is verbose and probably naively done - if I can get it working I will work on it later to improve it.

Absolutely all help / pointers appreciated.

cws_ILI9486-Combo_01_04_pm.ino (13.3 KB)

First off, study the button_simple example that comes with the library.

I suggest that you look at this example.

  1. copy your PORTRAIT Calibration lines. (i.e. what you paste from the Serial Terminal)
  2. change the redled, blueled, greenled defines to suit your RGB led.
  3. run the sketch.
  4. alter the Rotation in setup(). The 320x240 “layout” should still fit in your 3.5 inch display
  5. it should draw the buttons in PORTRAIT or any other rotation and work ok.
  6. you can see that I map the Touch values according to the rotation.

button_multiple.ino:

#include <Adafruit_GFX.h>
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <TouchScreen.h>
#define MINPRESSURE 200
#define MAXPRESSURE 1000

// copy-paste results from TouchScreen_Calibr_native.ino

const int XP = 6, XM = A2, YP = A1, YM = 7; //ID=0x9341
const int TS_LEFT = 907, TS_RT = 136, TS_TOP = 942, TS_BOT = 139;

TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

int pixel_x, pixel_y;     //Touch_getXY() updates global vars
bool Touch_getXY(void)
{
    TSPoint p = ts.getPoint();
    pinMode(YP, OUTPUT);      //restore shared pins
    pinMode(XM, OUTPUT);
    bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE);
    if (pressed) {
        // most apps use Portrait or Landscape. No need for all 4 cases
        switch (tft.getRotation()) {
            case 0:
                pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width());
                pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
                break;
            case 1:
                pixel_x = map(p.y, TS_TOP, TS_BOT, 0, tft.width());
                pixel_y = map(p.x, TS_RT, TS_LEFT, 0, tft.height());
                break;
            case 2:
                pixel_x = map(p.x, TS_RT, TS_LEFT, 0, tft.width());
                pixel_y = map(p.y, TS_BOT, TS_TOP, 0, tft.height());
                break;
            case 3:
                pixel_x = map(p.y, TS_BOT, TS_TOP, 0, tft.width());
                pixel_y = map(p.y, TS_LEFT, TS_RT, 0, tft.height());
                break;
        }
    }
    return pressed;
}

//Adafruit_GFX_Button on_btn, off_btn, flickeron_btn, flickeroff_btn, led_btn, RGB_btn;
Adafruit_GFX_Button circle_btn, square_btn, triangle_btn, line_btn, led_btn, RGB_btn;
Adafruit_GFX_Button red_btn, green_btn, blue_btn, yellow_btn, purple_btn, aqua_btn, back_btn;
// easier to handle a list of button pointers
Adafruit_GFX_Button *screen0[] = {&led_btn, &RGB_btn, NULL };
Adafruit_GFX_Button *screen1[] = {&circle_btn, &triangle_btn, &square_btn, &line_btn, &back_btn, NULL };
Adafruit_GFX_Button *screen2[] = {&red_btn, &green_btn, &blue_btn, &yellow_btn, &purple_btn, &aqua_btn, &back_btn, NULL };

#define redled   10
#define greenled 11
#define blueled  12
int flag = 0, screen = 0;
//  GLOBAL VARIABLES, CONSTRUCTORS
const int LED = A5;     //which pin for LED

// INITIALISE GPIO, TOUCH, TFT, BUTTONS
void setup(void)
{
    tft.begin(tft.readID());
    tft.setRotation(1);     //LANDSCAPE
    pinMode(LED, OUTPUT);
    pinMode(redled, OUTPUT);
    pinMode(greenled, OUTPUT);
    pinMode(blueled, OUTPUT);
    circle_btn.initButton(&tft, 293, 20, 56, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "Circle", 1);
    triangle_btn.initButton(&tft, 293, 64, 56, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "Triangle", 1);
    square_btn.initButton(&tft, 293, 108, 56, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "Square", 1);
    line_btn.initButton(&tft, 293, 156, 56, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "Lines", 1);
    led_btn.initButton(&tft,  80, 120, 120, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "Shape", 2);
    RGB_btn.initButton(&tft, 240, 120, 120, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "RGB", 2);
    red_btn.initButton(&tft,  20, 220, 40, 40, TFT_WHITE, TFT_RED, TFT_BLACK, "RED", 2);
    green_btn.initButton(&tft, 64, 220, 40, 40, TFT_WHITE, TFT_GREEN, TFT_BLACK, "GRN", 2);
    blue_btn.initButton(&tft,  108, 220, 40, 40, TFT_WHITE, TFT_BLUE, TFT_BLACK, "BLU", 2);
    yellow_btn.initButton(&tft, 152, 220, 40, 40, TFT_WHITE, TFT_YELLOW, TFT_BLACK, "YEL", 2);
    purple_btn.initButton(&tft, 196, 220, 40, 40, TFT_WHITE, TFT_MAGENTA, TFT_BLACK, "PRP", 2);
    aqua_btn.initButton(&tft, 240, 220, 40, 40, TFT_WHITE, TFT_CYAN, TFT_BLACK, "AQU", 2);
    back_btn.initButton(&tft, 293, 220, 56, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "BACK", 2);
    tft.fillScreen(TFT_BLACK);
    led_btn.drawButton(false);
    RGB_btn.drawButton(false);
}

void loop(void)
{
    static uint16_t color = TFT_BLACK;
    // READ TOUCH
    if (screen == 0) {
        update_button_list(screen0);  //process all buttons
        if (led_btn.justPressed()) {
            screen = 1;
            draw_button_list(screen1);
        }
        if (RGB_btn.justPressed()) {
            screen = 2;
            draw_button_list(screen2);
        }
    }

    if (screen == 1) {
        update_button_list(screen1);  //process all buttons
        if (circle_btn.justPressed()) {
            tft.drawCircle(100, 100, 50, TFT_WHITE);
        }
        if (triangle_btn.justPressed()) {
            tft.drawTriangle(100, 50, 50, 100, 200, 200, TFT_RED);
        }
        if (square_btn.justPressed()) {
            tft.drawRect(100, 100, 100, 50, TFT_BLUE);
        }
        if (line_btn.justPressed()) {
            tft.drawLine(100, 100, 50, 150, TFT_GREEN);
        }
        if (back_btn.justPressed()) {
            screen = 0;
            draw_button_list(screen0);
        }
    }
    if (screen == 2) {
        update_button_list(screen2);  //process all buttons
        if (red_btn.justPressed()) {
            color = TFT_RED;
        }
        if (green_btn.justPressed()) {
            color = TFT_GREEN;
        }
        if (blue_btn.justPressed()) {
            color = TFT_BLUE;
        }
        if (yellow_btn.justPressed()) {
            color = TFT_YELLOW;
        }
        if (purple_btn.justPressed()) {
            color = TFT_MAGENTA;
        }
        if (aqua_btn.justPressed()) {
            color = TFT_CYAN;
        }
        tft.drawRect(50, 50, 100, 100, TFT_WHITE);
        tft.fillRect(51, 51, 98, 98, color);
        if (back_btn.justPressed()) {
            screen = 0;
            draw_button_list(screen0);
            color = TFT_BLACK;
        }
    }
}

/* update the state of a button and redraw as reqd
 *
 * main program can use isPressed(), justPressed() etc
 */
bool update_button(Adafruit_GFX_Button *b, bool down)
{
    b->press(down && b->contains(pixel_x, pixel_y));
    if (b->justReleased())
        b->drawButton(false);
    if (b->justPressed())
        b->drawButton(true);
    return down;
}

/* most screens have different sets of buttons
 * life is easier if you process whole list in one go
 */
bool update_button_list(Adafruit_GFX_Button **pb)
{
    bool down = Touch_getXY();
    for (int i = 0 ; pb[i] != NULL; i++) {
        update_button(pb[i], down);
    }
    return down;
}

void draw_button_list(Adafruit_GFX_Button **pb)
{
    tft.fillScreen(TFT_BLACK);
    for (int i = 0 ; pb[i] != NULL; i++) {
        pb[i]->drawButton(false);
    }
}

David.

David,

many thanks for the very swift response.

I did in fact use the button_simple as the foundation for my code and I thought I had a reasonable understanding of it.

I got the landscape mode working o.k. and the first 'menu' buttons fine.

I've looked at the button_multiple example and I admit I will struggle to follow it - although it appears at face value to do exactly what i am trying to achieve.

I appreciate I'm being a nuisance - but if you could explain the functions for updating and drawing the button lists (in the simplest of terms!) - I'd be eternally grateful. I'm afraid I've left it far too late in life to learn programming - the brain melts down too easily. Clearly, what is blindingly obvious and second nature to you is very complex for me as a novice.

Best

Charles

My advice is to print the sketch on paper. Then study it carefully with several cups of tea and a pencil.

Draw arrows, comments, … all over the paper.
It may take a little while to understand.

Then ask further question(s).

David.

David,

Believe me that's what I am already doing. Unfortunately, I have to refer to books of coding - things like these pointers * etc are beyond my basics.

I think I've completely screwed up my code now trying to follow your processes.

I'll plug at it for a while - but close to taking up knitting instead!

Thanks again.

Charles

Hi, to any kindly soul …

I’ve gone through various pads, pencils, Ibuprofen etc ! Think I understand buttons_multiple - certainly got it working on my tft screen.

However, more or less scrapped my sketch and tried to clone the barebones using buttons_multiple as the template - just to try proving my understanding and getting the rudiments of my ‘new’ sketch.

I’m listing the code … been over it a thousand times trying to find out why I cannot compile this -fails every time I try to compile with the error message

Arduino: 1.8.8 (Windows 10), Board: “Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)”

cws_ILI9486-Combo_01_04_barebones:24:36: error: ‘userInput_btn’ was not declared in this scope

None of my buttons in scope it appears .

Something to do with the pointers to the screen arrays??

Now getting desperate!!

#include <Adafruit_GFX.h> // Hardware-specific library
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
#include <TouchScreen.h>
#define MINPRESSURE 200
#define MAXPRESSURE 1000
const int XP = 8, XM = A2, YP = A3, YM = 9;                                     //320x480 ID=0x9486 RS, WR, CS, RST
const int TS_LEFT = 950, TS_RT = 94, TS_TOP = 898, TS_BOT = 120;                // Landscape
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
int pixel_x, pixel_y;
bool Touch_getXY(void)                                                          //Touch_getXY() updates global vars
{
  TSPoint p = ts.getPoint();
  pinMode(YP, OUTPUT);                                                          //Restore shared pins
  pinMode(XM, OUTPUT);
  bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE);
  if (pressed) {
    pixel_x = map(p.y, TS_LEFT, TS_RT, 0, tft.width());
    pixel_y = map(p.x, TS_TOP, TS_BOT, 0, tft.height());
  }
  return pressed;
}

Adafruit_GFX_Button *screen0[] = {&userInput_btn, &runSetup_btn, &runControl_btn, NULL };
Adafruit_GFX_Button *screen1[] = {&on_btn, &off_btn, &lens_btn, &sensor_btn, &fstop_btn, &leadscrew_btn, &stepper_btn, &microstep_btn, &calibration_btn, &stepDelay_btn, &flashDelay_btn, &back_btn, NULL };
Adafruit_GFX_Button *screen2[] = {&back_btn, NULL };
Adafruit_GFX_Button *screen3[] = {&back_btn, NULL };
int screen = 0;

void setup()(void)
{
  Serial.begin(9600);
  //tft.reset();
  uint16_t identifier = tft.readID();
  if (identifier == 0xEFEF) identifier = 0x9486;
  tft.begin(identifier);
  tft.setRotation(1);                   // 0 = Portrait, USB at top, 1 = Landscape USB to Left, 2 = Portrait, text opposite USB, 3 = Landscape USB to Left
  runControl_btn.initButton(&tft, 220, 240, 240, 45, WHITE, RED, BLACK, "Controls", 2);
  runSetup_btn.initButton(&tft,  220, 180, 240, 45, WHITE, RED, BLACK, "Setup", 2);
  userInput_btn.initButton(&tft,  220, 120, 240, 45, WHITE, RED, BLACK, "Inputs", 2);
  on_btn.initButton(&tft,  50, 300, 80, 30, WHITE, RED, BLACK, "BACK", 2);
  off_btn.initButton(&tft, 140, 300, 80, 30, WHITE, RED, BLACK, "HOME", 2);
  lens_btn.initButton(&tft, 175, 58, 75, 30, WHITE, CYAN, BLACK, lensString[x], 2);
  sensor_btn.initButton(&tft, 175, 106, 75, 30, WHITE, CYAN, BLACK, sensorString[x], 2);
  fstop_btn.initButton(&tft, 175, 154, 75, 30, WHITE, CYAN, BLACK, fstopString[x], 2);
  leadscrew_btn.initButton(&tft, 175, 250, 75, 30, WHITE, CYAN, BLACK, leadscrewString[x], 2);
  stepper_btn.initButton(&tft, 425, 58, 75, 30, WHITE, CYAN, BLACK, stepperString[x], 2);
  microstep_btn.initButton(&tft, 425, 106, 75, 30, WHITE, CYAN, BLACK, microstepString[x], 2);
  calibration_btn.initButton(&tft, 425, 154, 75, 30, WHITE, CYAN, BLACK, calibrationString[x], 2);
  stepDelay_btn.initButton(&tft, 425, 202, 75, 30, WHITE, CYAN, BLACK, stepDelayString[x], 2);
  flashDelay_btn.initButton(&tft, 425, 250, 75, 30, WHITE, CYAN, BLACK, flashDelayString[x], 2);
  back_btn.initButton(&tft, 293, 220, 56, 40, TFT_BLACK, TFT_WHITE, TFT_BLACK, "BACK", 2);
  tft.fillScreen(BLACK);
  userInput_btn.drawButton(false);
  runSetup_btn.drawButton(false);
  runControl_btn.drawButton(false);

}

void loop()
{
  static uint16_t color = TFT_BLACK;
  if (screen == 0) {
    update_button_list(screen0);                                                               //process all buttons
    if (userInput_btn.justPressed()) {
      screen = 1;
      draw_button_list(screen1);
    }
    if (runSetup_btn.justPressed()) {
      screen = 2;
      draw_button_list(screen2);
    }
    if (runControl_btn.justPressed()) {
      currentpage = 3;
      draw_button_list(screen3);
    }
  }

  if (screen == 1) {
    update_button_list(screen1);                                                                  //process all buttons
    if (back_btn.justPressed()) {
      screen = 0;
      draw_button_list(screen0);
    }
  }

  if (screen == 2) {
    update_button_list(screen2);                                                                  //process all buttons
    if (back_btn.justPressed()) {
      screen = 0;
      draw_button_list(screen0);
    }
  }
}

bool update_button(Adafruit_GFX_Button *b, bool down)
{
  b->press(down && b->contains(pixel_x, pixel_y));
  if (b->justReleased())
    b->drawButton(false);
  if (b->justPressed())
    b->drawButton(true);
  return down;
}

bool update_button_list(Adafruit_GFX_Button **pb)
{
  bool down = Touch_getXY();
  for (int i = 0 ; pb[i] != NULL; i++) {
    update_button(pb[i], down);
  }
  return down;
}

void draw_button_list(Adafruit_GFX_Button **pb)
{
  tft.fillScreen(TFT_BLACK);
  for (int i = 0 ; pb[i] != NULL; i++) {
    pb[i]->drawButton(false);
  }
}

You need to define the button objects e.g.

Adafruit_GFX_Button circle_btn, square_btn, triangle_btn, line_btn, led_btn, RGB_btn;
Adafruit_GFX_Button red_btn, green_btn, blue_btn, yellow_btn, purple_btn, aqua_btn, back_btn;

before defining the lists. I call them lists but they are actually pointer arrays. I suppose that I say "list" because I signify the last pointer as NULL. This is commonly what you do with conventional linked lists.

// easier to handle a list of button pointers
Adafruit_GFX_Button *screen0[] = {&led_btn, &RGB_btn, NULL };
Adafruit_GFX_Button *screen1[] = {&circle_btn, &triangle_btn, &square_btn, &line_btn, &back_btn, NULL };
Adafruit_GFX_Button *screen2[] = {&red_btn, &green_btn, &blue_btn, &yellow_btn, &purple_btn, &aqua_btn, &back_btn, NULL };

You still have to initialise each button e.g. in setup()

 userInput_btn.initButton(&tft,  220, 120, 240, 45, WHITE, RED, BLACK, "Inputs", 2);

But once you have got the buttons, pointer arrays, and initialised them, the actual program logic is simple to maintain. You just think in terms of a named button press, release, hover, ...

If you want to rearrange the button position and appearance, you alter the initButton() statements.

David.

David,

Got it - many thanks.

Pointers I struggle with - got some reading to do.

Reconstructed most of my code now and feeling much more comfortable.

Trouble is at my age I'll have forgotten how it was done by tomorrow - loads of comments / documentation required.

I'd never have been able to design things the way you did in a month of Sundays - very efficient but for the beginner quite a challenge to understand.

Your technique of drink tea, get paper and pencils - again efficient teaching method - but cruel!!

Many thanks again.

Charles