diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 8fc7fc9..5fea5b9 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1179,6 +1179,15 @@ uint8_t TFT_eSPI::getRotation(void) return rotation; } +/*************************************************************************************** +** Function name: getTextDatum +** Description: Return the text datum value (as used by setTextDatum()) +***************************************************************************************/ +uint8_t TFT_eSPI::getTextDatum(void) +{ + return textdatum; +} + /*************************************************************************************** ** Function name: width @@ -3151,7 +3160,7 @@ int16_t TFT_eSPI::drawString(const char *string, int poX, int poY, int font) int16_t sumX = 0; uint8_t padding = 1, baseline = 0; uint16_t cwidth = textWidth(string, font); // Find the pixel width of the string in the font - uint16_t cheight = 8; + uint16_t cheight = 8 * textsize; #ifdef LOAD_GFXFF if (font == 1) { @@ -3643,14 +3652,15 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #endif -// The following touch screen support code added by maxpautsch 1/10/17 +// The following touch screen support code by maxpautsch was merged 1/10/17 // https://github.com/maxpautsch // Define TOUCH_CS is the user setup file to enable this code -// An example is provided is 480x320 folder +// A demo is provided in examples Generic folder +// Additions by Bodmer to double sample and use Z value to improve detection reliability #ifdef TOUCH_CS /*************************************************************************************** ** Function name: getTouchRaw -** Description: read raw position of touchpad if pressed. Return false if not pressed. +** Description: read raw touch position. Return false if not pressed. ***************************************************************************************/ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ uint16_t tmp; @@ -3671,13 +3681,6 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ tmp = tmp <<5; tmp |= 0x1f & (SPI.transfer(0)>>3); - if(tmp == 0 || tmp == 0x3ff){ - T_CS_H; - SPI.setFrequency(SPI_FREQUENCY); - spi_end(); - return false; - } - *x = tmp; // Start bit + XP sample request for y position @@ -3692,23 +3695,87 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ SPI.setFrequency(SPI_FREQUENCY); spi_end(); - if(tmp == 0 || tmp == 0x3ff){ - return false; - } return true; } /*************************************************************************************** -** Function name: getTouch -** Description: read callibrated position of touchpad if pressed. Return false if not pressed. +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. ***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + CS_H; + T_CS_L; + +#ifdef SPI_HAS_TRANSACTION + #ifdef SUPPORT_TRANSACTIONS + if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} + #endif +#endif + + SPI.setFrequency(SPI_TOUCH_FREQUENCY); + + // Z sample request + uint16_t tz = 0xFFF; + SPI.transfer(0xb1); + tz += SPI.transfer16(0xc1) >> 3; + tz -= SPI.transfer16(0x91) >> 3; + + T_CS_H; + SPI.setFrequency(SPI_FREQUENCY); + spi_end(); + + return tz; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +#define _RAWERR 10 // Deadband in position samples +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2; + + + // uint16_t z_tmp = getTouchRawZ(); // Save value for debug message + // Debug messages used for tuning + // Serial.print("Z = ");Serial.println(z_tmp); + + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small debounce delay to the next sample + getTouchRaw(&x_tmp,&y_tmp); + + // z_tmp = getTouchRawZ(); + // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp); + // Serial.print(", Z = ");Serial.println(z_tmp); + + delay(2); // Small debounce delay to the next sample + if (getTouchRawZ() <= threshold) return false; + + delay(2); // Small debounce delay to the next sample + getTouchRaw(&x_tmp2,&y_tmp2); + + // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2); + // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2)); + + if (abs(x_tmp - x_tmp2) > _RAWERR) return false; + if (abs(y_tmp - y_tmp2) > _RAWERR) return false; + + *x = x_tmp; + *y = y_tmp; + + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +#define Z_THRESHOLD 0x200 // Touch pressure threshold for validating touches uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ - uint8_t retVal; uint16_t x_tmp, y_tmp; - retVal = getTouchRaw(&x_tmp,&y_tmp); - if(!retVal) - return retVal; + if(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD)) return false; if(!touchCalibration_rotate){ *x=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; @@ -3726,14 +3793,14 @@ uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ *y = _height - *y; } - return retVal; + return true; } /*************************************************************************************** ** Function name: calibrateTouch ** Description: generates calibration parameters for touchscreen. ***************************************************************************************/ -uint8_t TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ int16_t values[] = {0,0,0,0,0,0,0,0}; uint16_t x_tmp, y_tmp; @@ -3774,7 +3841,8 @@ uint8_t TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32 if(i>0) delay(1000); for(uint8_t j= 0; j<8; j++){ - while(!getTouchRaw(&x_tmp, &y_tmp)) delay(100); + // Use a lower detect threshold as corners tend to be less sensitive + while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2)) delay(10); values[i*2 ] += x_tmp; values[i*2+1] += y_tmp; } @@ -3847,8 +3915,141 @@ void TFT_eSPI::setTouch(uint16_t *parameters){ touchCalibration_invert_y = parameters[4] & 0x04; } +#else // TOUCH CS is not defined so generate dummy functions that do nothing + +/*************************************************************************************** +** Function name: getTouchRaw +** Description: read raw touch position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){ + return true; +} + +/*************************************************************************************** +** Function name: getTouchRawZ +** Description: read raw pressure on touchpad and return Z value. +***************************************************************************************/ +uint16_t TFT_eSPI::getTouchRawZ(void){ + return true; +} + +/*************************************************************************************** +** Function name: validTouch +** Description: read validated position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){ + return true; +} + +/*************************************************************************************** +** Function name: getTouch +** Description: read callibrated position. Return false if not pressed. +***************************************************************************************/ +uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){ + return true; +} + +/*************************************************************************************** +** Function name: calibrateTouch +** Description: generates calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_bg, uint32_t color_fg, uint8_t size){ +} + + +/*************************************************************************************** +** Function name: setTouch +** Description: imports calibration parameters for touchscreen. +***************************************************************************************/ +void TFT_eSPI::setTouch(uint16_t *parameters){ +} + #endif // TOUCH_CS + + +/*************************************************************************************** +** Code for the GFX button UI element +** Grabbed from Adafruit_GFX library and enhanced to handle any label font +***************************************************************************************/ +TFT_eSPI_Button::TFT_eSPI_Button(void) { + _gfx = 0; +} + +// Classic initButton() function: pass center & size +void TFT_eSPI_Button::initButton( + TFT_eSPI *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, + textcolor, label, textsize); +} + +// Newer function instead accepts upper-left corner & size +void TFT_eSPI_Button::initButtonUL( + TFT_eSPI *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize) +{ + _x1 = x1; + _y1 = y1; + _w = w; + _h = h; + _outlinecolor = outline; + _fillcolor = fill; + _textcolor = textcolor; + _textsize = textsize; + _gfx = gfx; + strncpy(_label, label, 9); +} + +void TFT_eSPI_Button::drawButton(boolean inverted) { + uint16_t fill, outline, text; + + if(!inverted) { + fill = _fillcolor; + outline = _outlinecolor; + text = _textcolor; + } else { + fill = _textcolor; + outline = _outlinecolor; + text = _fillcolor; + } + + uint8_t r = min(_w, _h) / 4; // Corner radius + _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); + _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); + + _gfx->setTextColor(text); + _gfx->setTextSize(_textsize); + + static byte tempdatum = _gfx->getTextDatum(); + _gfx->setTextDatum(MC_DATUM); + _gfx->drawString(_label, _x1 + (_w/2), _y1 + (_h/2)); + _gfx->setTextDatum(tempdatum); +} + +boolean TFT_eSPI_Button::contains(int16_t x, int16_t y) { + return ((x >= _x1) && (x < (_x1 + _w)) && + (y >= _y1) && (y < (_y1 + _h))); +} + +void TFT_eSPI_Button::press(boolean p) { + laststate = currstate; + currstate = p; +} + +boolean TFT_eSPI_Button::isPressed() { return currstate; } +boolean TFT_eSPI_Button::justPressed() { return (currstate && !laststate); } +boolean TFT_eSPI_Button::justReleased() { return (!currstate && laststate); } + + + + + + /*************************************************** The majority of code in this file is "FunWare", the only condition of use of those portions is that users have fun! Most of the effort has been spent on diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 92d0401..5d174fd 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -393,7 +393,8 @@ class TFT_eSPI : public Print { // Set w and h to 1 to read 1 pixel's colour. The data buffer must be at least w * h * 3 bytes void readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data); - uint8_t getRotation(void); + uint8_t getRotation(void), + getTextDatum(void); uint16_t fontsLoaded(void), color565(uint8_t r, uint8_t g, uint8_t b); @@ -425,37 +426,38 @@ class TFT_eSPI : public Print { textWidth(const String& string), fontHeight(int16_t font); - void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); + void setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); -#ifdef TOUCH_CS - uint8_t getTouch(uint16_t *x, uint16_t *y); - uint8_t getTouchRaw(uint16_t *x, uint16_t *y); - uint8_t calibrateTouch(uint16_t *data, uint32_t color_bg, uint32_t color_fg, uint8_t size); - void setTouch(uint16_t *data); -#endif + // These are associated with the Touch Screen handlers + uint8_t getTouchRaw(uint16_t *x, uint16_t *y); + uint16_t getTouchRawZ(void); + uint8_t getTouch(uint16_t *x, uint16_t *y); + + void calibrateTouch(uint16_t *data, uint32_t color_fg, uint32_t color_bg, uint8_t size); + void setTouch(uint16_t *data); virtual size_t write(uint8_t); private: -inline void spi_begin() __attribute__((always_inline)); -inline void spi_end() __attribute__((always_inline)); + inline void spi_begin() __attribute__((always_inline)); + inline void spi_end() __attribute__((always_inline)); - void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); + void readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); uint8_t tabcolor, colstart = 0, rowstart = 0; // some ST7735 displays need this changed volatile uint32_t *dcport, *csport;//, *mosiport, *clkport, *rsport; - uint32_t cspinmask, dcpinmask, wrpinmask;//, mosipinmask, clkpinmask; + uint32_t cspinmask, dcpinmask, wrpinmask;//, mosipinmask, clkpinmask; uint32_t lastColor = 0xFFFF; -#ifdef TOUCH_CS + // These are associated with the Touch Screen handlers + uint8_t validTouch(uint16_t *x, uint16_t *y, uint16_t threshold); uint16_t touchCalibration_x0, touchCalibration_x1, touchCalibration_y0, touchCalibration_y1; uint8_t touchCalibration_rotate, touchCalibration_invert_x, touchCalibration_invert_y; -#endif protected: @@ -482,6 +484,45 @@ inline void spi_end() __attribute__((always_inline)); }; +/*************************************************************************************** +// The following button class has been ported over from the Adafruit_GFX library so +// should be compatible. +// A slightly different implementation in this TFT_eSPI library allows the button +// legends to be in any font +***************************************************************************************/ + +class TFT_eSPI_Button { + + public: + TFT_eSPI_Button(void); + // "Classic" initButton() uses center & size + void initButton(TFT_eSPI *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + + // New/alt initButton() uses upper-left corner & size + void initButtonUL(TFT_eSPI *gfx, int16_t x1, int16_t y1, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize); + void drawButton(boolean inverted = false); + boolean contains(int16_t x, int16_t y); + + void press(boolean p); + boolean isPressed(); + boolean justPressed(); + boolean justReleased(); + + private: + TFT_eSPI *_gfx; + int16_t _x1, _y1; // Coordinates of top-left corner + uint16_t _w, _h; + uint8_t _textsize; + uint16_t _outlinecolor, _fillcolor, _textcolor; + char _label[10]; + + boolean currstate, laststate; +}; + #endif /*************************************************** diff --git a/User_Setup.h b/User_Setup.h index 62e6ff0..8fe0a82 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -152,10 +152,12 @@ // // ################################################################################## -// Define the SPI clock frequency +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) -// With an ILI9163 display TBD MHz works OK, +// With an ILI9163 display 27 MHz works OK. +// The RPi typically only works at 20MHz maximum. // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 @@ -172,6 +174,10 @@ // supported. Tranaction support is required if other SPI devices are connected. // When commented out the code size will be smaller and sketches will // run slightly faster, so leave it commented out unless you need it! + // Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has noo effect + // #define SUPPORT_TRANSACTIONS diff --git a/User_Setups/Setup10_RPi_touch_ILI9486.h b/User_Setups/Setup10_RPi_touch_ILI9486.h index b572e0f..34e23c1 100644 --- a/User_Setups/Setup10_RPi_touch_ILI9486.h +++ b/User_Setups/Setup10_RPi_touch_ILI9486.h @@ -76,12 +76,13 @@ // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### -// ModeMCU +// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 #define TFT_DC PIN_D3 // Data Command control pin -//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) -#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TOUCH_CS D4 // Chip select pin (T_CS) of touch screen controller XPT2046 +#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +#define TOUCH_CS PIN_D1 // Chip select pin (T_CS) of touch screen, also define SPI frequency in Section 5 below //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only diff --git a/examples/320 x 240/Keypad_240x320/Keypad_240x320.ino b/examples/320 x 240/Keypad_240x320/Keypad_240x320.ino new file mode 100644 index 0000000..a6c503f --- /dev/null +++ b/examples/320 x 240/Keypad_240x320/Keypad_240x320.ino @@ -0,0 +1,284 @@ +/* + The TFT_eSPI library incorporates an Adafruit_GFX compatible + button handling class, this sketch is based on the Arduin-o-phone + example. + + This example diplays a keypad where numbers can be entered and + send to the Serial Monitor window. + + The sketch has been tested on the ESP8266 (which supports SPIFFS) + + The minimum screen size is 320 x 240 as that is the keypad size. +*/ + +// The SPIFFS (FLASH filing system) is used to hold touch screen +// calibration data + +#include "FS.h" + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// This is the file name used to store the calibration data +// You can change this to create new calibration files. +// The SPIFFS file name must start with "/". +#define CALIBRATION_FILE "/TouchCalData1" + +// Set REPEAT_CAL to true instead of false to run calibration +// again, otherwise it will only be done once. +// Repeat calibration if you change the screen rotation. +#define REPEAT_CAL false + +// Keypad start position, key sizes and spacing +#define KEY_X 40 // Centre of key +#define KEY_Y 96 +#define KEY_W 62 // Width and height +#define KEY_H 30 +#define KEY_SPACING_X 18 // X and Y gap +#define KEY_SPACING_Y 20 +#define KEY_TEXTSIZE 1 // Font size multiplier + +// Using two fonts since numbers are nice when bold +#define LABEL1_FONT &FreeSansOblique12pt7b // Key label font 1 +#define LABEL2_FONT &FreeSansBold12pt7b // Key label font 2 + +// Numeric display box size and location +#define DISP_X 1 +#define DISP_Y 10 +#define DISP_W 238 +#define DISP_H 50 +#define DISP_TSIZE 3 +#define DISP_TCOLOR TFT_CYAN + +// Number length, buffer for storing it and character index +#define NUM_LEN 12 +char numberBuffer[NUM_LEN + 1] = ""; +uint8_t numberIndex = 0; + +// We have a status line for messages +#define STATUS_X 120 // Centred on this +#define STATUS_Y 65 + +// Create 15 keys for the keypad +char keyLabel[15][5] = {"New", "Del", "Send", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "#" }; +uint16_t keyColor[15] = {TFT_RED, TFT_DARKGREY, TFT_DARKGREEN, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE + }; + +// Invoke the TFT_eSPI button class and create all the button objects +TFT_eSPI_Button key[15]; + +//------------------------------------------------------------------------------------------ + +void setup() { + // Use serial port + Serial.begin(9600); + + // Initialise the TFT screen + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(0); + + // Calibrate the touch screen and retrieve the scaling factors + touch_calibrate(); + + // Clear the screen + tft.fillScreen(TFT_BLACK); + + // Draw keypad background + tft.fillRect(0, 0, 240, 320, TFT_DARKGREY); + + // Draw number display area and frame + tft.fillRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_BLACK); + tft.drawRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_WHITE); + + // Draw keypad + drawKeypad(); +} + +//------------------------------------------------------------------------------------------ + +void loop(void) { + uint16_t t_x = 0, t_y = 0; // To store the touch coordinates + + // Pressed will be set true is there is a valid touch on the screen + boolean pressed = tft.getTouch(&t_x, &t_y); + + // / Check if any key coordinate boxes contain the touch coordinates + for (uint8_t b = 0; b < 15; b++) { + if (pressed && key[b].contains(t_x, t_y)) { + key[b].press(true); // tell the button it is pressed + } else { + key[b].press(false); // tell the button it is NOT pressed + } + } + + // Check if any key has changed state + for (uint8_t b = 0; b < 15; b++) { + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + if (key[b].justReleased()) key[b].drawButton(); // draw normal + + if (key[b].justPressed()) { + key[b].drawButton(true); // draw invert + + // if a numberpad button, append the relevant # to the numberBuffer + if (b >= 3) { + if (numberIndex < NUM_LEN) { + numberBuffer[numberIndex] = keyLabel[b][0]; + numberIndex++; + numberBuffer[numberIndex] = 0; // zero terminate + } + status(""); // Clear the old status + } + + // Del button, so delete last char + if (b == 1) { + numberBuffer[numberIndex] = 0; + if (numberIndex > 0) { + numberIndex--; + numberBuffer[numberIndex] = 0;//' '; + } + status(""); // Clear the old status + } + + if (b == 2) { + status("Sent value to serial port"); + Serial.println(numberBuffer); + } + // we dont really check that the text field makes sense + // just try to call + if (b == 0) { + status("Value cleared"); + numberIndex = 0; // Reset index to 0 + numberBuffer[numberIndex] = 0; // Place null in buffer + } + + // Update the number display field + tft.setTextDatum(TL_DATUM); // Use top left corner as text coord datum + tft.setFreeFont(&FreeSans18pt7b); // Choose a nicefont that fits box + tft.setTextColor(DISP_TCOLOR); // Set the font colour + + // Draw the string, the value returned is the width in pixels + int xwidth = tft.drawString(numberBuffer, DISP_X + 4, DISP_Y + 12); + + // Now cover up the rest of the line up by drawing a black rectangle. No flicker this way + // but it will not work with italic or oblique fonts due to character overlap. + tft.fillRect(DISP_X + 4 + xwidth, DISP_Y + 1, DISP_W - xwidth - 5, DISP_H - 2, TFT_BLACK); + + delay(10); // UI debouncing + } + } +} + +//------------------------------------------------------------------------------------------ + +void drawKeypad() +{ + // Draw the keys + for (uint8_t row = 0; row < 5; row++) { + for (uint8_t col = 0; col < 3; col++) { + uint8_t b = col + row * 3; + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), + KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text + KEY_W, KEY_H, TFT_WHITE, keyColor[b], TFT_WHITE, + keyLabel[b], KEY_TEXTSIZE); + key[b].drawButton(); + } + } +} + +//------------------------------------------------------------------------------------------ + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!SPIFFS.begin()) { + Serial.println("Formating file system"); + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists and size is correct + if (SPIFFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + SPIFFS.remove(CALIBRATION_FILE); + } + else + { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} + +//------------------------------------------------------------------------------------------ + +// Print something in the mini status bar +void status(const char *msg) { + tft.setTextPadding(240); + //tft.setCursor(STATUS_X, STATUS_Y); + tft.setTextColor(TFT_WHITE, TFT_DARKGREY); + tft.setTextFont(0); + tft.setTextDatum(TC_DATUM); + tft.setTextSize(1); + tft.drawString(msg, STATUS_X, STATUS_Y); +} + +//------------------------------------------------------------------------------------------ + diff --git a/examples/480 x 320/Keypad_480x320/Keypad_480x320.ino b/examples/480 x 320/Keypad_480x320/Keypad_480x320.ino new file mode 100644 index 0000000..427cb6a --- /dev/null +++ b/examples/480 x 320/Keypad_480x320/Keypad_480x320.ino @@ -0,0 +1,287 @@ +/* + The TFT_eSPI library incorporates an Adafruit_GFX compatible + button handling class, this sketch is based on the Arduin-o-phone + example. + + This example diplays a keypad where numbers can be entered and + send to the Serial Monitor window. + + The sketch has been tested on the ESP8266 (which supports SPIFFS) + + The minimum screen size is 320 x 240 as that is the keypad size. + + TOUCH_CS and SPI_TOUCH_FREQUENCY must be defined in the User_Setup.h file + for the touch functions to do anything. +*/ + +// The SPIFFS (FLASH filing system) is used to hold touch screen +// calibration data + +#include "FS.h" + +#include +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// This is the file name used to store the calibration data +// You can change this to create new calibration files. +// The SPIFFS file name must start with "/". +#define CALIBRATION_FILE "/TouchCalData2" + +// Set REPEAT_CAL to true instead of false to run calibration +// again, otherwise it will only be done once. +// Repeat calibration if you change the screen rotation. +#define REPEAT_CAL false + +// Keypad start position, key sizes and spacing +#define KEY_X 40 // Centre of key +#define KEY_Y 96 +#define KEY_W 62 // Width and height +#define KEY_H 30 +#define KEY_SPACING_X 18 // X and Y gap +#define KEY_SPACING_Y 20 +#define KEY_TEXTSIZE 1 // Font size multiplier + +// Using two fonts since numbers are nice when bold +#define LABEL1_FONT &FreeSansOblique12pt7b // Key label font 1 +#define LABEL2_FONT &FreeSansBold12pt7b // Key label font 2 + +// Numeric display box size and location +#define DISP_X 1 +#define DISP_Y 10 +#define DISP_W 238 +#define DISP_H 50 +#define DISP_TSIZE 3 +#define DISP_TCOLOR TFT_CYAN + +// Number length, buffer for storing it and character index +#define NUM_LEN 12 +char numberBuffer[NUM_LEN + 1] = ""; +uint8_t numberIndex = 0; + +// We have a status line for messages +#define STATUS_X 120 // Centred on this +#define STATUS_Y 65 + +// Create 15 keys for the keypad +char keyLabel[15][5] = {"New", "Del", "Send", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "0", "#" }; +uint16_t keyColor[15] = {TFT_RED, TFT_DARKGREY, TFT_DARKGREEN, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE, + TFT_BLUE, TFT_BLUE, TFT_BLUE + }; + +// Invoke the TFT_eSPI button class and create all the button objects +TFT_eSPI_Button key[15]; + +//------------------------------------------------------------------------------------------ + +void setup() { + // Use serial port + Serial.begin(9600); + + // Initialise the TFT screen + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(1); + + // Calibrate the touch screen and retrieve the scaling factors + touch_calibrate(); + + // Clear the screen + tft.fillScreen(TFT_BLACK); + + // Draw keypad background + tft.fillRect(0, 0, 240, 320, TFT_DARKGREY); + + // Draw number display area and frame + tft.fillRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_BLACK); + tft.drawRect(DISP_X, DISP_Y, DISP_W, DISP_H, TFT_WHITE); + + // Draw keypad + drawKeypad(); +} + +//------------------------------------------------------------------------------------------ + +void loop(void) { + uint16_t t_x = 0, t_y = 0; // To store the touch coordinates + + // Pressed will be set true is there is a valid touch on the screen + boolean pressed = tft.getTouch(&t_x, &t_y); + + // / Check if any key coordinate boxes contain the touch coordinates + for (uint8_t b = 0; b < 15; b++) { + if (pressed && key[b].contains(t_x, t_y)) { + key[b].press(true); // tell the button it is pressed + } else { + key[b].press(false); // tell the button it is NOT pressed + } + } + + // Check if any key has changed state + for (uint8_t b = 0; b < 15; b++) { + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + if (key[b].justReleased()) key[b].drawButton(); // draw normal + + if (key[b].justPressed()) { + key[b].drawButton(true); // draw invert + + // if a numberpad button, append the relevant # to the numberBuffer + if (b >= 3) { + if (numberIndex < NUM_LEN) { + numberBuffer[numberIndex] = keyLabel[b][0]; + numberIndex++; + numberBuffer[numberIndex] = 0; // zero terminate + } + status(""); // Clear the old status + } + + // Del button, so delete last char + if (b == 1) { + numberBuffer[numberIndex] = 0; + if (numberIndex > 0) { + numberIndex--; + numberBuffer[numberIndex] = 0;//' '; + } + status(""); // Clear the old status + } + + if (b == 2) { + status("Sent value to serial port"); + Serial.println(numberBuffer); + } + // we dont really check that the text field makes sense + // just try to call + if (b == 0) { + status("Value cleared"); + numberIndex = 0; // Reset index to 0 + numberBuffer[numberIndex] = 0; // Place null in buffer + } + + // Update the number display field + tft.setTextDatum(TL_DATUM); // Use top left corner as text coord datum + tft.setFreeFont(&FreeSans18pt7b); // Choose a nicefont that fits box + tft.setTextColor(DISP_TCOLOR); // Set the font colour + + // Draw the string, the value returned is the width in pixels + int xwidth = tft.drawString(numberBuffer, DISP_X + 4, DISP_Y + 12); + + // Now cover up the rest of the line up by drawing a black rectangle. No flicker this way + // but it will not work with italic or oblique fonts due to character overlap. + tft.fillRect(DISP_X + 4 + xwidth, DISP_Y + 1, DISP_W - xwidth - 5, DISP_H - 2, TFT_BLACK); + + delay(10); // UI debouncing + } + } +} + +//------------------------------------------------------------------------------------------ + +void drawKeypad() +{ + // Draw the keys + for (uint8_t row = 0; row < 5; row++) { + for (uint8_t col = 0; col < 3; col++) { + uint8_t b = col + row * 3; + + if (b < 3) tft.setFreeFont(LABEL1_FONT); + else tft.setFreeFont(LABEL2_FONT); + + key[b].initButton(&tft, KEY_X + col * (KEY_W + KEY_SPACING_X), + KEY_Y + row * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text + KEY_W, KEY_H, TFT_WHITE, keyColor[b], TFT_WHITE, + keyLabel[b], KEY_TEXTSIZE); + key[b].drawButton(); + } + } +} + +//------------------------------------------------------------------------------------------ + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!SPIFFS.begin()) { + Serial.println("Formating file system"); + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists and size is correct + if (SPIFFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + SPIFFS.remove(CALIBRATION_FILE); + } + else + { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} + +//------------------------------------------------------------------------------------------ + +// Print something in the mini status bar +void status(const char *msg) { + tft.setTextPadding(240); + //tft.setCursor(STATUS_X, STATUS_Y); + tft.setTextColor(TFT_WHITE, TFT_DARKGREY); + tft.setTextFont(0); + tft.setTextDatum(TC_DATUM); + tft.setTextSize(1); + tft.drawString(msg, STATUS_X, STATUS_Y); +} + +//------------------------------------------------------------------------------------------ + diff --git a/examples/Generic/On_Off_Button/On_Off_Button.ino b/examples/Generic/On_Off_Button/On_Off_Button.ino new file mode 100644 index 0000000..035cf6e --- /dev/null +++ b/examples/Generic/On_Off_Button/On_Off_Button.ino @@ -0,0 +1,206 @@ +// Example of drawing a graphical "switch" and using +// the touch screen to change it's state. + +// This sketch does not use the libraries button drawing +// and handling functions. + +// Based on Adafruit_GFX library onoffbutton example. + +// Touch handling for XPT2046 based screens is handled by +// the TFT_eSPI library. + +// Calibration data is stored in SPIFFS so we need to include it +#include "FS.h" + +#include + +#include // Hardware-specific library + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +// This is the file name used to store the touch coordinate +// calibration data. Cahnge the name to start a new calibration. +#define CALIBRATION_FILE "/TouchCalData3" + +// Set REPEAT_CAL to true instead of false to run calibration +// again, otherwise it will only be done once. +// Repeat calibration if you change the screen rotation. +#define REPEAT_CAL false + +boolean SwitchOn = false; + +// Comment out to stop drawing black spots +#define BLACK_SPOT + +// Switch position and size +#define FRAME_X 100 +#define FRAME_Y 64 +#define FRAME_W 120 +#define FRAME_H 50 + +// Red zone size +#define REDBUTTON_X FRAME_X +#define REDBUTTON_Y FRAME_Y +#define REDBUTTON_W (FRAME_W/2) +#define REDBUTTON_H FRAME_H + +// Green zone size +#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W) +#define GREENBUTTON_Y FRAME_Y +#define GREENBUTTON_W (FRAME_W/2) +#define GREENBUTTON_H FRAME_H + +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ +void setup(void) +{ + Serial.begin(9600); + tft.init(); + + // Set the rotation before we calibrate + tft.setRotation(1); + + // call screen calibration + touch_calibrate(); + + // clear screen + tft.fillScreen(TFT_BLUE); + + // Draw button (this example does not use library Button class) + redBtn(); +} +//------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------ +void loop() +{ + uint16_t x, y; + + // See if there's any touch data for us + if (tft.getTouch(&x, &y)) + { + // Draw a block spot to show where touch was calculated to be + #ifdef BLACK_SPOT + tft.fillCircle(x, y, 2, TFT_BLACK); + #endif + + if (SwitchOn) + { + if ((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) { + if ((y > REDBUTTON_Y) && (y <= (REDBUTTON_Y + REDBUTTON_H))) { + Serial.println("Red btn hit"); + redBtn(); + } + } + } + else //Record is off (SwitchOn == false) + { + if ((x > GREENBUTTON_X) && (x < (GREENBUTTON_X + GREENBUTTON_W))) { + if ((y > GREENBUTTON_Y) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) { + Serial.println("Green btn hit"); + greenBtn(); + } + } + } + + Serial.println(SwitchOn); + + } +} +//------------------------------------------------------------------------------------------ + +void touch_calibrate() +{ + uint16_t calData[5]; + uint8_t calDataOK = 0; + + // check file system exists + if (!SPIFFS.begin()) { + Serial.println("Formating file system"); + SPIFFS.format(); + SPIFFS.begin(); + } + + // check if calibration file exists and size is correct + if (SPIFFS.exists(CALIBRATION_FILE)) { + if (REPEAT_CAL) + { + // Delete if we want to re-calibrate + SPIFFS.remove(CALIBRATION_FILE); + } + else + { + File f = SPIFFS.open(CALIBRATION_FILE, "r"); + if (f) { + if (f.readBytes((char *)calData, 14) == 14) + calDataOK = 1; + f.close(); + } + } + } + + if (calDataOK && !REPEAT_CAL) { + // calibration data valid + tft.setTouch(calData); + } else { + // data not valid so recalibrate + tft.fillScreen(TFT_BLACK); + tft.setCursor(20, 0); + tft.setTextFont(2); + tft.setTextSize(1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + tft.println("Touch corners as indicated"); + + tft.setTextFont(1); + tft.println(); + + if (REPEAT_CAL) { + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.println("Set REPEAT_CAL to false to stop this running again!"); + } + + tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); + + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.println("Calibration complete!"); + + // store data + File f = SPIFFS.open(CALIBRATION_FILE, "w"); + if (f) { + f.write((const unsigned char *)calData, 14); + f.close(); + } + } +} + +void drawFrame() +{ + tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, TFT_BLACK); +} + +// Draw a red button +void redBtn() +{ + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_RED); + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_DARKGREY); + drawFrame(); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(2); + tft.setTextDatum(MC_DATUM); + tft.drawString("ON", GREENBUTTON_X + (GREENBUTTON_W / 2), GREENBUTTON_Y + (GREENBUTTON_H / 2)); + SwitchOn = false; +} + +// Draw a green button +void greenBtn() +{ + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_GREEN); + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_DARKGREY); + drawFrame(); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(2); + tft.setTextDatum(MC_DATUM); + tft.drawString("OFF", REDBUTTON_X + (REDBUTTON_W / 2) + 1, REDBUTTON_Y + (REDBUTTON_H / 2)); + SwitchOn = true; +} +