Update touch, add Button class

Touch reliability improved by using pressure and double sampling. Added
Adafruit compatible Button class. Added touchscreen on/off and keypad
examples.
This commit is contained in:
Bodmer
2017-10-23 01:59:37 +01:00
parent d484a56574
commit e4ea506b65
7 changed files with 1069 additions and 43 deletions

View File

@@ -1179,6 +1179,15 @@ uint8_t TFT_eSPI::getRotation(void)
return rotation; 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 ** 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; int16_t sumX = 0;
uint8_t padding = 1, baseline = 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 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 #ifdef LOAD_GFXFF
if (font == 1) { if (font == 1) {
@@ -3643,14 +3652,15 @@ void spiWriteBlock(uint16_t color, uint32_t repeat)
} }
#endif #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 // https://github.com/maxpautsch
// Define TOUCH_CS is the user setup file to enable this code // 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 #ifdef TOUCH_CS
/*************************************************************************************** /***************************************************************************************
** Function name: getTouchRaw ** 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){ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){
uint16_t tmp; uint16_t tmp;
@@ -3671,13 +3681,6 @@ uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){
tmp = tmp <<5; tmp = tmp <<5;
tmp |= 0x1f & (SPI.transfer(0)>>3); tmp |= 0x1f & (SPI.transfer(0)>>3);
if(tmp == 0 || tmp == 0x3ff){
T_CS_H;
SPI.setFrequency(SPI_FREQUENCY);
spi_end();
return false;
}
*x = tmp; *x = tmp;
// Start bit + XP sample request for y position // 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.setFrequency(SPI_FREQUENCY);
spi_end(); spi_end();
if(tmp == 0 || tmp == 0x3ff){ return true;
return false;
} }
/***************************************************************************************
** 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; return true;
} }
/*************************************************************************************** /***************************************************************************************
** Function name: getTouch ** Function name: getTouch
** Description: read callibrated position of touchpad if pressed. Return false if not pressed. ** 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 TFT_eSPI::getTouch(uint16_t *x, uint16_t *y){
uint8_t retVal;
uint16_t x_tmp, y_tmp; uint16_t x_tmp, y_tmp;
retVal = getTouchRaw(&x_tmp,&y_tmp); if(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD)) return false;
if(!retVal)
return retVal;
if(!touchCalibration_rotate){ if(!touchCalibration_rotate){
*x=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; *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; *y = _height - *y;
} }
return retVal; return true;
} }
/*************************************************************************************** /***************************************************************************************
** Function name: calibrateTouch ** Function name: calibrateTouch
** Description: generates calibration parameters for touchscreen. ** 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}; int16_t values[] = {0,0,0,0,0,0,0,0};
uint16_t x_tmp, y_tmp; 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); if(i>0) delay(1000);
for(uint8_t j= 0; j<8; j++){ 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 ] += x_tmp;
values[i*2+1] += y_tmp; values[i*2+1] += y_tmp;
} }
@@ -3847,8 +3915,141 @@ void TFT_eSPI::setTouch(uint16_t *parameters){
touchCalibration_invert_y = parameters[4] & 0x04; 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 #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 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 those portions is that users have fun! Most of the effort has been spent on

View File

@@ -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 // 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); 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), uint16_t fontsLoaded(void),
color565(uint8_t r, uint8_t g, uint8_t b); color565(uint8_t r, uint8_t g, uint8_t b);
@@ -427,12 +428,13 @@ class TFT_eSPI : public Print {
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 // These are associated with the Touch Screen handlers
uint8_t getTouch(uint16_t *x, uint16_t *y);
uint8_t getTouchRaw(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); 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); void setTouch(uint16_t *data);
#endif
virtual size_t write(uint8_t); virtual size_t write(uint8_t);
@@ -452,10 +454,10 @@ inline void spi_end() __attribute__((always_inline));
uint32_t lastColor = 0xFFFF; 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; uint16_t touchCalibration_x0, touchCalibration_x1, touchCalibration_y0, touchCalibration_y1;
uint8_t touchCalibration_rotate, touchCalibration_invert_x, touchCalibration_invert_y; uint8_t touchCalibration_rotate, touchCalibration_invert_x, touchCalibration_invert_y;
#endif
protected: 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 #endif
/*************************************************** /***************************************************

View File

@@ -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 an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) // 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 1000000
// #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 5000000
@@ -172,6 +174,10 @@
// supported. Tranaction support is required if other SPI devices are connected. // supported. Tranaction support is required if other SPI devices are connected.
// When commented out the code size will be smaller and sketches will // When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it! // 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 // 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 // #define SUPPORT_TRANSACTIONS

View File

@@ -76,12 +76,13 @@
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR SETUP ###### // ###### 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_CS PIN_D8 // Chip select control pin D8
#define TFT_DC PIN_D3 // Data Command control pin #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 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 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 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 //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only

View File

@@ -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 <SPI.h>
#include <TFT_eSPI.h> // 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);
}
//------------------------------------------------------------------------------------------

View File

@@ -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 <SPI.h>
#include <TFT_eSPI.h> // 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);
}
//------------------------------------------------------------------------------------------

View File

@@ -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 <SPI.h>
#include <TFT_eSPI.h> // 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;
}