Elegoo 2.8" TFT LCD Touch display - "justReleased" trigering always

I am developing a simple display-based controller for the bottom illuminator for a microscope. The devices are Duemilanove and a 2.8" TFT LCD Touchscreen shield from Elegoo. (Amazon UK: https://www.amazon.co.uk/dp/B01EUVJYME?psc=1&ref=ppx_yo2ov_dt_b_product_details).

The code is derived from the phonecall.ino sketch provided by Elegoo (itself derived from an earlier Adafruit example). The sketch uses Elegoo's bespoke libraries (also derived from Adafruit's):

#include <Elegoo_GFX.h>    // Core graphics library
#include <Elegoo_TFTLCD.h> // Hardware-specific library

as well as the Touchscreen library:

#include <TouchScreen.h>

The code is being developed in functional units prior to combining into the final application. The code shown at the end of this post is the latest version and is still at a 'template' stage with the main program flow largely in place. The sketch works reasonably well, detecting button presses in timely manner.

The next stage is to implement a "Press and Hold" function using millis() to distinguish short and long presses. A hardware button sketch (using two momentary push buttons) was developed using EzButton and this worked extremely well. A long press on the '+' and '-' push buttons increments in units until the next decade boundary (up and down) after which the '+' and '-' buttons increment in 10's to a maximum 100% or decrement to a minimum 0%. The aim is to port this to the TFT Touch display for similar functionality. For completeness here is the EzButton sketch:

/*
   Created by ArduinoGetStarted.com

   This example code is in the public domain

   Tutorial page: https://arduinogetstarted.com/tutorials/arduino-button-long-press-short-press
*/

#include <ezButton.h>               // includes useful libraries and de-bounce
#include <millisDelay.h>            // easy timing functions in library

const int SHORT_PRESS_TIME = 500;   // 500 milliseconds
const int LONG_PRESS_TIME  = 500;   // 500 milliseconds (no dead space between short and long)
const int REPEAT_DURATION = 150;    // default interval for repetitive keypresses
const int REPEAT_INTERVAL = 10;     // press and hold will increment / decrement in jumps of 10
const int MIN_COUNT = 0;             // min value for PWM duty cycle
const int MAX_COUNT = 255;           // max value for PWM duty cycle

// We use pins 7 & 8, ei, non-PWM pins
ezButton buttonUp(7);               // create ezButton object attached to pin 7;
ezButton buttonDown(8);             // create ezButton object attached to pin 8;
millisDelay pressDelayUp;           // create press & hold interval object
millisDelay pressDelayDown;         // create press & hold interval object

unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
long pressDuration = 0;
bool isPressing = false;
bool isLongDetected = false;
bool upButton = false;
bool downButton = false;
int stepCount = 0;
int newCount = 0;

void setup() {
  Serial.begin(9600);
  buttonUp.setDebounceTime(50);       // set debounce time to 50 milliseconds
  buttonDown.setDebounceTime(50);     // set debounce time to 50 milliseconds
  Serial.println("v1.2e");
}

void loop() {
  buttonUp.loop();                      // MUST call the loop() function first
  buttonDown.loop();                    // MUST call the loop() function first
  // check if delay has timed out for UP button
  if (pressDelayUp.justFinished()) {    // MUST go in outermost loop
    // (see "SafeString Detailed Documentation")
    // Serial.print("UP button just finished ");
    pressDelayUp.repeat();              // start delay again
    // do some stuff
    newCount += REPEAT_INTERVAL - (stepCount % REPEAT_INTERVAL); //increments to next REPEAT_INTERVAL
    stepCount = constrain (newCount, MIN_COUNT, MAX_COUNT); // limit to 8 bit range

    Serial.println (stepCount);
  }
  // check if delay has timed out for DOWN button
  else if (pressDelayDown.justFinished()) {    // MUST go in outermost loop
    // (see "SafeString Detailed Documentation")
    // Serial.print("DOWN button just finished ");
    pressDelayDown.repeat();              // start delay again
    // do some stuff
    newCount -= REPEAT_INTERVAL + (stepCount % REPEAT_INTERVAL); //decrements to next REPEAT_INTERVAL
    stepCount = constrain (newCount, MIN_COUNT, MAX_COUNT); // limit to 8 bit range

    Serial.println (stepCount);
  }

  if (buttonUp.isPressed()) {
    pressedTime = millis();
    isPressing = true;
    isLongDetected = false;
    upButton = true;
    //    Serial.println("UP button pressed");
  }
  if (buttonDown.isPressed()) {
    pressedTime = millis();
    isPressing = true;
    isLongDetected = false;
    downButton = true;
    //    Serial.println("DOWN button pressed");
  }

  if (buttonUp.isReleased()) {
    isPressing = false;
    releasedTime = millis();
    // Serial.println("Button has been released");
    pressDuration = releasedTime - pressedTime;
    // stop the delay
    pressDelayUp.stop();
    upButton = false;

    // SHORT PRESS
    if ( pressDuration < SHORT_PRESS_TIME ) {
      // Serial.print("A short press is detected");
      // Serial.print("Short Keypress  -  ");
      stepCount++;
      stepCount = constrain (stepCount , MIN_COUNT, MAX_COUNT);
      Serial.println (stepCount);
    }
  } else   if (buttonDown.isReleased()) {
    isPressing = false;
    releasedTime = millis();
    // Serial.println("Button has been released");
    pressDuration = releasedTime - pressedTime;
    // stop the delay
    pressDelayDown.stop();
    downButton = false;

    // SHORT PRESS
    if ( pressDuration < SHORT_PRESS_TIME ) {
      // Serial.print("A short press is detected");
      // Serial.print("Short Keypress  -  ");
      stepCount--;
      stepCount = constrain (stepCount , MIN_COUNT, MAX_COUNT);
      Serial.println (stepCount);
    }
  }

  // LONG PRESS
  if (isPressing == true && isLongDetected == false) {    // long press false until detected
    pressDuration = millis() - pressedTime;

    if ( pressDuration >= LONG_PRESS_TIME ) {
      // Serial.println("A long press is detected");
      // Serial.print("Long Keypress  -  ");
      isLongDetected = true;
      // initialise led delay for button hold section

      // determine which button and start appropriate delay timer
      if (upButton )
        pressDelayUp.start(REPEAT_DURATION);              // pass repetitive keystroke interval interval)
      if (downButton)
        pressDelayDown.start(REPEAT_DURATION);              // pass repetitive keystroke interval interval)
    }
  }
  // BUTTON HOLD
  else if (isPressing == true && isLongDetected == true) { // while button is still pressed
    // Serial.println("The long press continues...");
  }
}

Back to the TFT Touch.

The problem when holding down a display button is that the justReleased property repeatedly triggers (once per loop). This is fine for a quick press but hopeless when the justReleased function is to be used with a timer to detect a long press, say > 500ms.

Here is the output from the serial monitor during a long press of a preset button (Presets will also use a long press to store the displayed value in memory for later quick retrieval).

First, when no button is pressed:

********************* No button pressed example (full loop) **********
08:19:34.120 -> Button: P1 FALSE
08:19:34.120 -> Button: P2 FALSE
08:19:34.120 -> Button: P3 FALSE
08:19:34.154 -> Button: SET FALSE
08:19:34.187 -> Button: P4 FALSE
08:19:34.187 -> Button: P5 FALSE
08:19:34.221 -> Button: P6 FALSE
08:19:34.221 -> Button: ON FALSE
08:19:34.257 -> Button: + FALSE
08:19:34.291 -> Button: - FALSE
08:19:34.291 -> WHICH BUTTON LOOP: 0, button: P1
08:19:34.324 -> WHICH BUTTON LOOP: 1, button: P2
08:19:34.359 -> WHICH BUTTON LOOP: 2, button: P3
08:19:34.394 -> WHICH BUTTON LOOP: 3, button: SET
08:19:34.428 -> WHICH BUTTON LOOP: 4, button: P4
08:19:34.462 -> WHICH BUTTON LOOP: 5, button: P5
08:19:34.496 -> WHICH BUTTON LOOP: 6, button: P6
08:19:34.530 -> WHICH BUTTON LOOP: 7, button: ON
08:19:34.563 -> WHICH BUTTON LOOP: 8, button: +
08:19:34.630 -> WHICH BUTTON LOOP: 9, button: -
*************** No button pressed ends ******************

Secondly, when a button is pressed and held:

*************** Button pressed starts *******************
08:19:43.784 -> Button: P1 FALSE
08:19:43.819 -> Button: P2 FALSE
08:19:43.852 -> Button: P3 FALSE
08:19:43.852 -> Button: SET FALSE
08:19:43.885 -> Button: P4 FALSE
08:19:43.885 -> Button: P5 FALSE
08:19:43.919 -> CONTAINS x,y LOOP: 6, button: P6		<-- button [6] pressed
08:19:43.953 -> Button: P6 TRUE
08:19:43.989 -> Button: ON FALSE
08:19:43.989 -> Button: + FALSE
08:19:43.989 -> Button: - FALSE
08:19:44.023 -> WHICH BUTTON LOOP: 0, button: P1
08:19:44.056 -> WHICH BUTTON LOOP: 1, button: P2
08:19:44.090 -> WHICH BUTTON LOOP: 2, button: P3
08:19:44.125 -> WHICH BUTTON LOOP: 3, button: SET
08:19:44.158 -> WHICH BUTTON LOOP: 4, button: P4
08:19:44.192 -> WHICH BUTTON LOOP: 5, button: P5
08:19:44.226 -> WHICH BUTTON LOOP: 6, button: P6
08:19:44.261 -> IS pressed: 6							<-- button [6] change of state
08:19:44.261 -> JUST pressed: 6							<-- ditto
08:19:44.295 -> Preset button: P6						<-- code for button [6]
08:19:44.394 -> WHICH BUTTON LOOP: 7, button: ON		<-- and rest of buttons execute till end of loop
08:19:44.429 -> ON Button pressed 7 turned OFF
08:19:44.565 -> WHICH BUTTON LOOP: 8, button: +
08:19:44.666 -> + Button pressed +
08:19:44.666 -> Brightness = 1%
08:19:44.901 -> WHICH BUTTON LOOP: 9, button: -
08:19:44.969 -> - Button pressed -
08:19:45.002 -> Brightness = 0%
08:19:45.103 -> Button: P1 FALSE
08:19:45.137 -> Button: P2 FALSE
08:19:45.137 -> Button: P3 FALSE
08:19:45.172 -> Button: SET FALSE
08:19:45.172 -> Button: P4 FALSE
08:19:45.206 -> Button: P5 FALSE
08:19:45.206 -> Button: P6 FALSE
08:19:45.240 -> Button: ON FALSE
08:19:45.240 -> Button: + FALSE
08:19:45.274 -> Button: - FALSE
08:19:45.274 -> WHICH BUTTON LOOP: 0, button: P1		<-- all buttons now executing from start till end of loop
08:19:45.310 -> Preset button: P1
08:19:45.444 -> WHICH BUTTON LOOP: 1, button: P2
08:19:45.477 -> Preset button: P2
08:19:45.577 -> WHICH BUTTON LOOP: 2, button: P3
08:19:45.609 -> Preset button: P3
08:19:45.744 -> WHICH BUTTON LOOP: 3, button: SET
08:19:45.778 -> SET Button pressed 3
08:19:45.880 -> WHICH BUTTON LOOP: 4, button: P4
08:19:45.913 -> Preset button: P4
08:19:46.013 -> WHICH BUTTON LOOP: 5, button: P5
08:19:46.081 -> Preset button: P5
08:19:46.182 -> WHICH BUTTON LOOP: 6, button: P6		<<<< a release is detected for button [6]
08:19:46.216 -> Button released: P6
08:19:46.216 -> WHICH BUTTON LOOP: 7, button: ON		<-- and rest of buttons no longer execute (outside of loop???)
08:19:46.285 -> WHICH BUTTON LOOP: 8, button: +
08:19:46.319 -> WHICH BUTTON LOOP: 9, button: -
08:19:46.353 -> Button: P1 FALSE						<-- back to no button being pressed
08:19:46.353 -> Button: P2 FALSE
08:19:46.387 -> Button: P3 FALSE
08:19:46.387 -> Button: SET FALSE
08:19:46.421 -> Button: P4 FALSE
08:19:46.421 -> Button: P5 FALSE
08:19:46.455 -> Button: P6 FALSE
08:19:46.455 -> Button: ON FALSE
08:19:46.489 -> Button: + FALSE
08:19:46.489 -> Button: - FALSE
08:19:46.523 -> WHICH BUTTON LOOP: 0, button: P1
08:19:46.558 -> WHICH BUTTON LOOP: 1, button: P2
08:19:46.591 -> WHICH BUTTON LOOP: 2, button: P3
08:19:46.624 -> WHICH BUTTON LOOP: 3, button: SET
08:19:46.658 -> WHICH BUTTON LOOP: 4, button: P4
08:19:46.725 -> WHICH BUTTON LOOP: 5, button: P5
08:19:46.725 -> WHICH BUTTON LOOP: 6, button: P6
08:19:46.759 -> WHICH BUTTON LOOP: 7, button: ON
08:19:46.792 -> WHICH BUTTON LOOP: 8, button: +
08:19:46.826 -> WHICH BUTTON LOOP: 9, button: -

The detection of the release for button[6] (or any other button) is the problem: it occurs even when the button is held continuously.

I'm struggling with this and have reached a brick wall. I've looked at the Elegoo_GFX.cpp library and can't see anything obvious apart from the return of currstate and laststate - but I must confess here to being completely out of my depth having started this first project just a few weeks ago.

Here is the sketch:

# define VERSION 2.08

// version 2.05 and later replaces "if button[b]" tests with
// "case[b] && button[b].isPressed" construct

/*  Microscope illuminator project using sub-stage LED
    Display controls brightness & provides presets
    Incorporates debounce on soft keys
    PWM output drives constant current source for LED
    PWM frequency high enough to prevent flicker during photography
*/

// IMPORTANT: Elegoo_TFTLCD LIBRARY MUST BE SPECIFICALLY
// CONFIGURED FOR EITHER THE TFT SHIELD OR THE BREAKOUT BOARD.
// SEE RELEVANT COMMENTS IN Elegoo_TFTLCD.h FOR SETUP.
// Technical support:goodtft@163.com

#include <Elegoo_GFX.h>    // Core graphics library
#include <Elegoo_TFTLCD.h> // Hardware-specific library
#include <TouchScreen.h>

// When using the BREAKOUT BOARD only, use these 8 data lines to the LCD:
// For the Arduino Uno, Duemilanove, Diecimila, etc.:
//   D0 connects to digital pin 8  (these are NOT in pin order)
//   D1 connects to digital pin 9
//   D2 connects to digital pin 2
//   D3 connects to digital pin 3
//   D4 connects to digital pin 4
//   D5 connects to digital pin 5
//   D6 connects to digital pin 6
//   D7 connects to digital pin 7
// For the Arduino Mega, use digital pins 22 through 29
// (on the 2-row header at the end of the board).


// ************************ TFT LCD ************************
// The control pins for the LCD can be assigned to any digital or
// analog pins...but we'll use the analog pins as this allows us to
// double up the pins with the touch screen (see the TFT paint example).
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0
#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

// screen (TFT) coordinates (0<px<320, 0<py<240) in LANDSCAPE mode:
int px = 0;
int py = 0;

// Assign names to 16-bit color values for the TFT display:
#define TFT_BLACK       0x0000      /*   0,   0,   0 */
#define TFT_NAVY        0x000F      /*   0,   0, 128 */
#define TFT_DARKGREEN   0x03E0      /*   0, 128,   0 */
#define TFT_DARKCYAN    0x03EF      /*   0, 128, 128 */
#define TFT_MAROON      0x7800      /* 128,   0,   0 */
#define TFT_PURPLE      0x780F      /* 128,   0, 128 */
#define TFT_OLIVE       0x7BE0      /* 128, 128,   0 */
#define TFT_BLUE        0x001F      /*   0,   0, 255 */
#define TFT_GREEN       0x07E0      /*   0, 255,   0 */
#define TFT_CYAN        0x07FF      /*   0, 255, 255 */
#define TFT_RED         0xF800      /* 255,   0,   0 */
#define TFT_MAGENTA     0xF81F      /* 255,   0, 255 */
#define TFT_YELLOW      0xFFE0      /* 255, 255,   0 */
#define TFT_WHITE       0xFFFF      /* 255, 255, 255 */
#define TFT_ORANGE      0xFD20      /* 255, 165,   0 */
#define TFT_GREENYELLOW 0xAFE5      /* 173, 255,  47 */
#define TFT_PINK        0xF81F

// Grey scale colour definitions
#define TFT_GREY00       0x0841      //   1,   2,   1   #080808 very dark grey
#define TFT_GREY01       0x1082      //   2,   4,   2   #101010
#define TFT_GREY02       0x2104      //   4,   8,   4   #212121
#define TFT_GREY03       0x3186      //   6,  12,   6   #303030
#define TFT_GREY04       0x4208      //   8,  16,   8   #424242
#define TFT_GREY05       0x630c      //  12,  24,  12   #636363 middle grey
#define TFT_GREY06       0x8430      //  16,  33,  16   #878787
#define TFT_GREY07       0xa554      //  20,  42,  20   #a8a8a8
#define TFT_GREY08       0xc618      //  24,  48,  24   #c4c4c4
#define TFT_GREY09       0xf7be      //  30,  61,  30   #c4c4c4 very light grey

// Main screen parameters; prefix SCRN
#define SCRN_BACKCOLOUR TFT_GREY04

// Main display rectangle where brightness is displayed; values in pixels; prefix DISP
// the displayed brightness is between 0 and 100, 4 characters including terminating % sign
#define DISP_X 10       // top-left, x
#define DISP_Y 10       // top-left, y
#define DISP_W 230      // width (bottom-right = DISP_X + DISP_W)
#define DISP_H 125      // height (bottom-right = DISP_Y + DISP_H)
#define DISP_STATUS 26  // height of status bar panel at bottom of window
#define DISP_TXTSIZE 9
#define DISP_TXTCOLOUR TFT_GREEN

// Set position of status bar messages just inside the lower edge of the display rectangle
// Prefix STAT
// Note: status bar messages limited to 33 chars
#define STAT_X 25
#define STAT_Y 116
#define STAT_TXTSIZE 1
#define STAT_TXTCOLOUR TFT_GREY09     // very light grey
String strStatMsg = "";

// Standard buttons are presets (P1, P2, P3), SETUP, (P1, P2, P3), ON/OFF; prefix BTN
// UP/Down buttons are same width, double height
// 8 of the buttons are positioned by algorithm in two rows of four.
// UP (+) and DOWN (-) are positioned manually.
// define button positioning
#define BTN_X 40
#define BTN_Y 165
#define BTN_SPACING_X 25     // for progammatic filling
#define BTN_SPACING_Y 12     // for progammatic filling
// define button appearance
#define BTN_W 55             // width
#define BTN_H 35             // height
#define BTN_TEXTSIZE 2
#define BTN_TXTCOLOUR TFT_GREY09
#define BTN_EDGECOLOUR TFT_GREY07
bool boolBtnOn = true;
bool blnPressed = false;


// ************************ TOUCHSCREEN ************************
// Touchscreen (TS) wiring connections (physical sides X-, X+, Y- and Y+) to Arduino pins
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin

/*
   These are defaults provided by Elegoo; presumably the mean of representative sample boards.
  //Touch For New ILI9341 TP
  #define TS_MINX 120
  #define TS_MAXX 900
  #define TS_MINY 70
  #define TS_MAXY 920
*/
// New DEFS for my particular display (values from calibration)
#define TS_MINX 118   // (LEFT)
#define TS_MAXX 925   // (RIGHT)
#define TS_MINY  70   // (TOP)
#define TS_MAXY 910   // (BOTTOM)

#define TS_MINPRESS 10
#define TS_MAXPRESS 1000

// Co-ordinates for the status bar messages at the bottom of the display rectangle
#define STATUS_X 10
#define STATUS_Y 65

// variables for adjusting the brightness level
const int SHORT_PRESS_TIME = 500;   // 500 milliseconds
const int LONG_PRESS_TIME  = 500;   // 500 milliseconds (no dead space between short and long)
const int REPEAT_DURATION = 150;    // default interval for repetitive keypresses
const int REPEAT_INTERVAL = 10;     // press and hold will increment / decrement in jumps of 10
const int MIN_COUNT = 0;             // min value for PWM duty cycle
const int MAX_COUNT = 100;           // max value for PWM duty cycle ==> translate to 255

int ledLevel = 0;       // 0-->100, but translated 0-->255 for control output
int newCount = 0;
int stepCount = 0;


// ************************ SCREEN and TOUCH objects ************************
Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
// If using the shield, all control and data lines are fixed, and
// a simpler declaration can optionally be used:
// Elegoo_TFTLCD tft;

// Create the buttons; we need 10
Elegoo_GFX_Button buttons[10];
char buttonlabels[10][5] = {"P1", "P2", "P3", "SET", "P4", "P5", "P6", "ON", "+", "-"};
uint16_t buttoncolors[10] = {TFT_GREY04, TFT_GREY04, TFT_GREY04, TFT_ORANGE,
                             TFT_GREY04, TFT_GREY04, TFT_GREY04, TFT_DARKGREEN,
                             TFT_GREY04, TFT_GREY04
                            };

void setup(void) {
  Serial.begin(9600);
  Serial.println(F("TFT LCD test"));
  Serial.print("Version "); Serial.println(VERSION);

#ifdef USE_Elegoo_SHIELD_PINOUT
  Serial.println(F("Using Elegoo 2.8\" TFT Arduino Shield Pinout"));
#else
  Serial.println(F("Using Elegoo 2.8\" TFT Breakout Board Pinout"));
#endif

//  Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height());

  tft.reset();

  uint16_t identifier = tft.readID();
  if (identifier == 0x9325) {
    Serial.println(F("Found ILI9325 LCD driver"));
  } else if (identifier == 0x9328) {
    Serial.println(F("Found ILI9328 LCD driver"));
  } else if (identifier == 0x4535) {
    Serial.println(F("Found LGDP4535 LCD driver"));
  } else if (identifier == 0x7575) {
    Serial.println(F("Found HX8347G LCD driver"));
  } else if (identifier == 0x9341) {
    Serial.println(F("Found ILI9341 LCD driver"));
  } else if (identifier == 0x8357) {
    Serial.println(F("Found HX8357D LCD driver"));
  } else if (identifier == 0x0101)
  {
    identifier = 0x9341;
    Serial.println(F("Found 0x9341 LCD driver"));
  } else {
    Serial.print(F("Unknown LCD driver chip: "));
    Serial.println(identifier, HEX);
    Serial.println(F("If using the Elegoo 2.8\" TFT Arduino shield, the line:"));
    Serial.println(F("  #define USE_Elegoo_SHIELD_PINOUT"));
    Serial.println(F("should appear in the library header (Elegoo_TFT.h)."));
    Serial.println(F("If using the breakout board, it should NOT be #defined!"));
    Serial.println(F("Also if using the breakout, double-check that all wiring"));
    Serial.println(F("matches the tutorial."));
    identifier = 0x9341;
  }

  tft.begin(identifier);
  tft.setRotation(3);
  Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height());
  tft.fillScreen(SCRN_BACKCOLOUR);
  // create 'display rectangle' (template rectangle to help position "3D" lines)
  /*
      tft.drawRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_YELLOW);
      tft.drawRect(DISP_X+1, DISP_Y+1, DISP_W-2, DISP_H-2, TFT_YELLOW);
      tft.drawRect(DISP_X+2, DISP_Y+2, DISP_W-4, DISP_H-4, TFT_YELLOW);
      tft.drawRect(DISP_X + 3, DISP_Y + 3, DISP_W - 6, DISP_H - 6, TFT_YELLOW);
  */
  // left
  tft.drawFastVLine(DISP_X, DISP_Y, DISP_H, TFT_GREY02);
  tft.drawFastVLine(DISP_X + 1, DISP_Y + 1, DISP_H - 2, TFT_GREY02);
  tft.drawFastVLine(DISP_X + 2, DISP_Y + 2, DISP_H - 4 - DISP_STATUS, TFT_GREY02);
  tft.drawFastVLine(DISP_X + 3, DISP_Y + 3, DISP_H - 6 - DISP_STATUS, TFT_GREY02);

  // top
  tft.drawFastHLine(DISP_X + 1, DISP_Y, DISP_W - 1, TFT_GREY02);
  tft.drawFastHLine(DISP_X + 2, DISP_Y + 1, DISP_W - 3, TFT_GREY02);
  tft.drawFastHLine(DISP_X + 3, DISP_Y + 2, DISP_W - 5, TFT_GREY02);
  tft.drawFastHLine(DISP_X + 4, DISP_Y + 3, DISP_W - 7, TFT_GREY02);

  // right
  tft.drawFastVLine(DISP_X + DISP_W - 1, DISP_Y + 1, DISP_H, TFT_GREY06);
  tft.drawFastVLine(DISP_X + DISP_W - 2, DISP_Y + 2, DISP_H - 3, TFT_GREY06);
  tft.drawFastVLine(DISP_X + DISP_W - 3, DISP_Y + 3, DISP_H - 5 - DISP_STATUS, TFT_GREY06);
  tft.drawFastVLine(DISP_X + DISP_W - 4, DISP_Y + 4, DISP_H - 7 - DISP_STATUS, TFT_GREY06);

  // bottom
  tft.drawFastHLine(DISP_X + 1, DISP_Y + DISP_H - 1, DISP_W - 2, TFT_GREY06);
  tft.drawFastHLine(DISP_X + 2, DISP_Y + DISP_H - 2, DISP_W - 4, TFT_GREY06);
  tft.drawFastHLine(DISP_X + 3, DISP_Y + DISP_H - 3 - DISP_STATUS, DISP_W - 6, TFT_GREY06);
  tft.drawFastHLine(DISP_X + 4, DISP_Y + DISP_H - 4 - DISP_STATUS, DISP_W - 8, TFT_GREY06);

  // Draw the % sign
  tft.setCursor(DISP_X + 175, DISP_Y + 17); // vertical shift MUST be same as fnAlignRight vertical shift
  tft.setTextColor(TFT_GREEN);
  tft.setTextSize(4);
  tft.print("%");

  // Draw the I/O slope selection box and prefill with a default
  fnSlopeSelect("LIN");     // hard code a default until "SET" function implemented

  // Create buttons - "P1", "P2", "P3", "SET", "P4", "P5", "P6", "ON", "UP", "DOWN"
  // We treat the first two rows as an array (2 x 4); last two buttons are defined individually
  for (uint8_t row = 0; row < 2; row++) {
    for (uint8_t col = 0; col < 4; col++) {
      buttons[col + row * 4].initButton(&tft, BTN_X + col * (BTN_W + BTN_SPACING_X),
                                        BTN_Y + row * (BTN_H + BTN_SPACING_Y), // x, y, w, h, outline, fill, text
                                        BTN_W, BTN_H, BTN_EDGECOLOUR, buttoncolors[col + row * 4], BTN_TXTCOLOUR,
                                        buttonlabels[col + row * 4], BTN_TEXTSIZE);
      buttons[col + row * 4].drawButton();
    }
  }
  buttons[8].initButton(&tft, 280, 34, BTN_W, BTN_H * 1.4, BTN_EDGECOLOUR, buttoncolors[8], TFT_RED, buttonlabels[8], BTN_TEXTSIZE + 1);
  buttons[8].drawButton();
  buttons[9].initButton(&tft, 280, 95, BTN_W, BTN_H * 1.4, BTN_EDGECOLOUR, buttoncolors[9], TFT_BLUE, buttonlabels[9], BTN_TEXTSIZE + 1);
  buttons[9].drawButton();

  // To Do: change slope selection box into a button

  // print version on TFT screen status bar
  tft.print(fnStatusMsg("MicroLite version " + String(VERSION)));
}

// Print something in the mini status bar with either flashstring (from original Elegoo code, to be deleted)
/*
  void status(const __FlashStringHelper *msg) {
  tft.fillRect(STATUS_X, STATUS_Y, 240, 8, TFT_BLACK);
  tft.setCursor(STATUS_X, STATUS_Y);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(1);
  tft.print(msg);
  }
*/
/*
  // or charstring
  void status(char *msg) {
  tft.fillRect(STATUS_X, STATUS_Y, 240, 8, TFT_BLACK);
  tft.setCursor(STATUS_X, STATUS_Y);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(1);
  tft.print(msg);
  }
*/

void loop(void) {

  digitalWrite(13, HIGH);
  TSPoint p = ts.getPoint();
  digitalWrite(13, LOW);

  // if sharing pins, you'll need to fix the directions of the touchscreen pins
  //pinMode(XP, OUTPUT);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  //pinMode(YM, OUTPUT);

  if (p.z > TS_MINPRESS && p.z < TS_MAXPRESS) {
    // a "press" has been detected.
    // It could be the start of a press or part of a chain of fast presses during 'bounce' or a much longer press.
    // scale from 0->1023 to TFT width and height
    px = map(p.y, TS_MINY, TS_MAXY, 0, tft.width());    // assign to temp variable so as to retain p.x for next line
    py = map(p.x, TS_MINX, TS_MAXX, 0, tft.height());
    p.x = px;
    p.y = py;
    // Serial.print("p.x = "); Serial.print(p.x); Serial.print(", p.y = "); Serial.print(p.y); Serial.print("px = "); Serial.print(px); Serial.print(", py = "); Serial.print(py); Serial.print(" .. ");
  }

  // this section filters for preseses that are associated with the TFT buttons, ignoring other screen areas
  // If a button is pressed, sets the button status which is then used later
  // iterate all buttons, checking if the press belongs to a particular button
  for (uint8_t b = 0; b < 10; b++) {
    if (buttons[b].contains(p.x, p.y)) {
      //    if (buttons[b].contains(px, py)) {  // for some reason this does not work (buttons hang) - could be problem is later? (p.x & p.y implicitly referenced?)
      //      Serial.print("CONTAINS x,y LOOP: "); Serial.print(b); Serial.print(", button: "); Serial.println(buttonlabels[b]);
      buttons[b].press(true);   // tell the button it is pressed
      //      Serial.print("Button: "); Serial.print(buttonlabels[b]); Serial.println(" TRUE");
    } else {
      buttons[b].press(false);  // tell the button it is NOT pressed
      //      Serial.print("Button: "); Serial.print(buttonlabels[b]); Serial.println(" FALSE");
    }
  }

  // now we can poll each button to determine if its state has changed
  for (uint8_t b = 0; b < 10; b++) {
    //    Serial.print("WHICH BUTTON LOOP: "); Serial.print(b); Serial.print(", button: "); Serial.println(buttonlabels[b]);
    if (buttons[b].justReleased()) {
      Serial.print("Button released: "); Serial.println(buttonlabels[b]);
      //tft.print(fnStatusMsg("Button released " + String(buttonlabels[b])));
      buttons[b].drawButton();      // false == draw defaul colours defined above
      blnPressed = false;
    }

    /* TEMPORARY
       This next bit of code repeats the outcome for two different properties:
          - button[].justPressed
          - button[].isPressed
          There should be a difference - but none ever detected
    */
    /*
        if (buttons[b].isPressed()) {
          blnPressed = true;
          buttons[b].drawButton(true);  // draw inverted colours
          Serial.print("IS pressed: "); Serial.println(b);
          //tft.print(fnStatusMsg("Is pressed " + String(buttonlabels[b])));
        }
    */
    // so we continue to use justPressed:
    if (buttons[b].justPressed()) {
      buttons[b].drawButton(true);  // draw inverted colours
      // Serial.print("JUST pressed: "); Serial.println(b);
      //tft.print(fnStatusMsg("JUST pressed " + String(buttonlabels[b])));
      
      // switch always runs but case statement must have buton[b].isPressed == true to isolate which button
      switch (b) {
        // PRESET [0] BUTTON ACTIONS GO IN HERE
        case 0:
          if (buttons[b].isPressed()) {
            Serial.print("Preset button: "); Serial.println(String(buttonlabels[b]));
            //tft.print(fnStatusMsg("Preset button " + String(buttonlabels[b])));

          
          
          }
          break;
          
        case 1:
        // PRESET [1] BUTTON ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("Preset button: "); Serial.println(String(buttonlabels[b]));
            //tft.print(fnStatusMsg("Preset button " + String(buttonlabels[b])));


          }
          break;
          
        case 2:
        // PRESET [2] BUTTON ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("Preset button: "); Serial.println(String(buttonlabels[b]));
            //tft.print(fnStatusMsg("Preset button " + String(buttonlabels[b])));

       

          }
          break;

        case 3:
        // 'SET' BUTTON [3] ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            // opens a new screen to select slope, reset PRESETS, amongst other things
            Serial.print("SET Button pressed "); Serial.println(b);
            //tft.print(fnStatusMsg("SET Button " + String(buttonlabels[b])));

          }
          break;

        case 4:
        // PRESET [4] BUTTON ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("Preset button: "); Serial.println(String(buttonlabels[b]));
            //tft.print(fnStatusMsg("Preset button " + String(buttonlabels[b])));


          }
          break;
          
        case 5:
          // PRESET [5] BUTTON ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("Preset button: "); Serial.println(String(buttonlabels[b]));
            //tft.print(fnStatusMsg("Preset button " + String(buttonlabels[b])));



          }
          break;
          
        case 6:
        // PRESET [6] BUTTON ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("Preset button: "); Serial.println(String(buttonlabels[b]));
            //tft.print(fnStatusMsg("Preset button " + String(buttonlabels[b])));



          }
          break;

        case 7:
        // ON BUTTON [7] ACTIONS GO IN HERE
        // ON/OFF is a toggle action that flip-flops the two states for the LED
          if (buttons[b].isPressed()) {
            Serial.print("ON Button pressed "); Serial.print(b);
            //tft.print(fnStatusMsg("ON Button " + String(buttonlabels[b])));
            if (boolBtnOn == true) {
              boolBtnOn = false;
              Serial.println(" turned OFF");
              // swap colours etc


            } else if (boolBtnOn == false) {
              boolBtnOn = true;
              Serial.println(" turned ON");
              // restore colours etc


            }
          }
          break;

        case 8:
        // UP BUTTON [8] ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("+ Button pressed "); Serial.println(buttonlabels[b]);
            //tft.print(fnStatusMsg("+ button " + String(buttonlabels[b])));

            // Serial.print("A short press is detected");
            // Serial.print("Short Keypress  -  ");
            stepCount++;
            stepCount = constrain (stepCount , MIN_COUNT, MAX_COUNT);
            Serial.print("Brightness = "); Serial.print (stepCount); Serial.println("%");

            // This code increments in REPEAT_INTERVAL jumps. This is for the "Press & Hold" function
            //newCount += REPEAT_INTERVAL - (stepCount % REPEAT_INTERVAL); //increments to next REPEAT_INTERVAL
            //stepCount = constrain (newCount, MIN_COUNT, MAX_COUNT); // limit to 8 bit range
            fnRightAlign(stepCount);
          }

          break;

        case 9:
        // DOWN BUTTON [9] ACTIONS GO IN HERE
          if (buttons[b].isPressed()) {
            Serial.print("- Button pressed "); Serial.println(buttonlabels[b]);
            //tft.print(fnStatusMsg("+ button " + String(buttonlabels[b])));
            // Serial.print("A short press is detected");
            // Serial.print("Short Keypress  -  ");
            stepCount--;
            stepCount = constrain (stepCount , MIN_COUNT, MAX_COUNT);
            Serial.print("Brightness = "); Serial.print (stepCount); Serial.println("%");

            // This code decrements in REPEAT_INTERVAL jumps. This is for the "Press & Hold" function
            //newCount -= REPEAT_INTERVAL + (stepCount % REPEAT_INTERVAL); //decrements to next REPEAT_INTERVAL
            //stepCount = constrain (newCount, MIN_COUNT, MAX_COUNT); // limit to 8 bit range
            fnRightAlign(stepCount);
          }

          break;

          // Note: Slope selection to be added
          // Can't see any reason for adding a default

      } // end switch/case
      //      delay(50); // UI debouncing (as per original). Note to self: avoid delay(); use millis

    } // button just pressed
  }   // for num buttons
}     // loop

String fnRightAlign(int val) {         // passes numeric values as integer
  // values are in the interval 0-->100, max 3 characters
  int    space  = 0;
  String spaces = "";

  if ( val < 10 ) space = 2;
  else if ( val > 9 && 100 > val ) space = 1;
  else if ( val == 100 ) space = 0;

  //add the correct amount of spaces in front of our value
  for ( uint8_t s = 0; s < space; s++ ) spaces += F(" ");
  tft.setCursor(DISP_X + 10, DISP_Y + 17);
  tft.setTextColor(DISP_TXTCOLOUR, SCRN_BACKCOLOUR);      // leaving bac kground BLACK for the moment shows the space occuiped
  //  tft.setTextColor(DISP_TXTCOLOUR, TFT_BLACK);      // leaving bac kground BLACK for the moment shows the space occuiped
  tft.setTextSize(DISP_TXTSIZE);
  tft.print(spaces + String ( val ));
  //return value
  return spaces + String ( val );
}


// Formats and aligns the status message to centre of status bar
String fnStatusMsg(String txt) {
  // passed-in string lengths in the interval 0-->33, max 33 characters
  // function specific to the status bar line, hence absolte coordinates are used here
  tft.setTextColor(STAT_TXTCOLOUR, SCRN_BACKCOLOUR); // resets any other changes made elsewhere
  tft.setTextSize(STAT_TXTSIZE);
  // Blank-out the status bar to remove previous messages
  // Each character is 6px wide yielding max width of 6 x 33 = 198px
  tft.fillRect(STAT_X, STAT_Y, 198, 8, SCRN_BACKCOLOUR);  // clear any old text
  // Calculate new x position & set cursor
  tft.setCursor(STAT_X + (198 - txt.length() * 6) / 2, STAT_Y);
  return txt;
}

// Aligns and refreshes the slope selection box
String fnSlopeSelect(String slope) {
  // passed-in values can be == LIN, LOG, EXP, ^2, ^3
  tft.fillRect(DISP_X + 175, DISP_Y + 60, 45, 22, TFT_DARKGREEN); // rectangle LHS shift same as "%" sign horizontal shift
  tft.drawRect(DISP_X + 175, DISP_Y + 60, 45, 22, TFT_GREEN); // rectangle LHS shift same as "%" sign horizontal shift
  tft.setCursor(DISP_X + 180, DISP_Y + 64); // vertical shift MUST be same as fnAlignRight vertical shift
  tft.setTextColor(TFT_GREEN);
  tft.setTextSize(2);
  tft.print(slope);
  return slope;
}

All help resolving the problem with justReleased (the code above has uncommented just a couple of lines for the serial monitor - you can press and hold any key) towards implementing a "Press & Hold" function is appreciated.
ricm

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.