Adafruit TFT Touchscreen - Keypad for touchscreen?

Does anyone have code to use the Adafruit TFT touchscreen as a keypad for a string input? Sort of like a typing a text message, and then saving the message as a string variable.

I thought I'd check to see if there is an example already out there before I create a custom keypad with all the letters.

Any help would be appreciated. Thanks!

I made a keyboard that does that, not a keypad. These are meant to work with the ILI9341 model.

Keyboard.ino (6.62 KB)

Portrait_Keyboard.ino (7.15 KB)

1 Like

Thank you! This should help a lot. I am using the capacitive shield, but I should be able to use most everything else.

Do you have a demo of this in action or a photo?

I could take a photo for you tonight when I get home

If you type something then press RT (return) you will see it show in the Serial Monitor. (set to 115200 baud in code)

This is exactly what I am looking for. As I said earlier, I am going to use on a Capacitive touchscreen so I need to change out one of the Adafruit libraries. I am also using this on an Arduino Due.

I uploaded your sketch and changed the Adafruit library from Adafruit_STMPE610.h to Adafruit_FT6206.h. I get exactly what you show in the photos. However, I am not getting any output when I touch the screen.

I added a few Serial.print statements to show that my touches are being recognized, and they are. So the sketch is running, it's just not doing anything with my inputs. No letters appear on the screen when pressed, nor do the Shift and Special menus works.

Any ideas off the top?

You would need to fix the TouchButton function to work with your library. Perhaps its as easy as calibrating your touchscreen.

Try playing with the TS_MIN and MAX values based on the raw touch data. In the mean time I will look into your touchscreen library.

byte TouchButton(int x, int y, int w, int h)
{
  int X, Y;
  // Retrieve a point
  TS_Point p = ts.getPoint();
  Y = map(p.x, TS_MINY, TS_MAXY, tft.height(), 0);
  X = map(p.y, TS_MINX, TS_MAXX, 0, tft.width());

  return (IsWithin(X, x, x + w) & IsWithin(Y, y, y + h));
}

Yes, you're probably right. When I've written other sketches in the past, I've only used values between 0-320 for Y axis and 0-240 for X. Let me try changes those values.

Can I ask why you used the #'s you did for MIN and MAX?

I chose those values because they fit my screen. If you just print out the p.x and p.y before you put them into the map function, you can see what your screens raw touch data is. I used those values to set the min and max values.

The best way to get good results is to use a very fine pointer (don't scratch your screen) and touch the top left and bottom right corners of your display where you see the image.

You can do one axis at a time or print them either on the display or use the serial monitor. Make sure you know which axis you are looking at.

This code will run on an UNO with an Adafruit 2.8" TFT capacitive shield. Perhaps it can be adapted to your needs. All of the four screen rotations are supported.

/*
 For a capacitive touch screen.
 Creates a grid of numbered rectangles, each of
 which will trap touches and report its id.  
 Any of the four screen rotations may be used. 
*/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>

// Default values for Adafruit shield v2.
#define TFT_DC 9
#define TFT_CS 10

// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 ctp = Adafruit_FT6206();
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Assign human-readable names to some common 16-bit color values:
#define   BLACK   0x0000
#define   BLUE    0x001F
#define   RED     0xF800
#define   GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

 typedef struct {
  int left;
  int top;
  int width;
  int height; 
 }rectangle;

 // Rotations 0,2 = portrait  : 0->USB=right,upper : 2->USB=left,lower
 // Rotations 1,3 = landscape : 1->USB=left,upper  : 3->USB=right,lower
 const int rotation = 3; //(0->3)
 const int row = 3;
 const int col = 3;

 rectangle rect[col*row];
 static String btnTitle[col*row] = {"A","B","C","D","E","F","G","H","I"};
    
 void showGrid()
 {
   int left, top;   
   int l = 20;
   int t = 70;
   int w = 50;
   int h = 40;
   int hgap = 10;
   int vgap = 10;
   int id = 0;
   for(int j = 0; j < row; j++){
     for (int i = 0; i < col; i++){
       left = l + i*(w + vgap);
       top = t + j*(h + hgap);
       rect[id].left = left;
       rect[id].top = top;
       rect[id].width = w;
       rect[id].height = h;
       tft.drawRect( left, top, w, h, WHITE);
       tft.setCursor(left + 5, top +10);
       tft.print(btnTitle[id]); 
       id++;
      }
    }
 }

  void showZeroZero()
 {
  tft.setCursor(0, 0);
  tft.print("0,0");
 }
 
 void showTouchData(int xCoord, int yCoord, int id)
 {
   // Erase old data 
  tft.fillRect( 10, 30, tft.width()-10, 20, BLUE );
  tft.setCursor(10, 30);
  tft.print("x="); tft.print(xCoord);
  tft.print(" y="); tft.print(yCoord);
  tft.print(" id= "); tft.print(id); 
 }
 
void setup() {
    tft.begin();
   if (! ctp.begin(40)) {  // pass in 'sensitivity' coefficient
    Serial.println("Couldn't start FT6206 touchscreen controller");
    while (1);
    }

  tft.setRotation(rotation);
  tft.fillScreen(BLUE);
  tft.setTextColor(WHITE, BLUE);
  tft.setTextSize(2);
  showZeroZero();
  showGrid();
}

void loop() {
  uint16_t x, y;
  TS_Point p;

 if (! ctp.touched()) {  
    return;
 }
  
  delay(100);
  p = ctp.getPoint();
 
 if (rotation == 0){
  x = map(p.x, 240, 0, 0, 240);
  y = map(p.y, 320, 0, 0, 320);
 }
 
 if (rotation == 1){
 // p.x, p.y reversed //  
  x = map(p.y, 320, 0, 0, 320);
  y = map(p.x, 0, 240, 0, 240);
 }
 
 if (rotation == 2){
  x = map(p.x, 0, 240, 0, 240);
  y = map(p.y, 0, 320, 0, 320);  
 }
 
  if (rotation == 3){
 //  p.x, p.y reversed //
  x = map(p.y, 0, 320, 0, 320);
  y = map(p.x, 240, 0, 0, 240);
 }
 
  while (ctp.touched()) { 
   for(int count = 0; count < col*row; count++){  
     if(x > rect[count].left && x < ( rect[count].left + rect[count].width)){
     if( y > rect[count].top && y < ( rect[count].top + rect[count].height)){
     showTouchData( x, y, count );        
     }
    }
   } 
  }

}

This is very wasteful and lazy. What is wrong with simple subtraction?

if (rotation == 0){
x = map(p.x, 240, 0, 0, 240);
y = map(p.y, 320, 0, 0, 320);
}

if (rotation == 1){
// p.x, p.y reversed //
x = map(p.y, 320, 0, 0, 320);
y = map(p.x, 0, 240, 0, 240);
}

if (rotation == 2){ // especially this here, this deserves a slap in the face.
x = map(p.x, 0, 240, 0, 240);
y = map(p.y, 0, 320, 0, 320);
}

if (rotation == 3){
// p.x, p.y reversed //
x = map(p.y, 0, 320, 0, 320);
y = map(p.x, 240, 0, 0, 240);
}

@HazardsMind

If you have improvements to the code, please post them. Otherwise, please keep negative comments to yourself.

Perhaps its as easy as calibrating your touchscreen.

The FT6206 does not need to be 'calibrated'.

It wasn't really a negative comment as it was informative. The Arduino has limited SRAM so it would be best not to waste it on code that could be done better and allow it to perform faster.

Some improvements. One was not improved but i'm ok with it for now. I left some comments throughout the code.

/*
 For a capacitive touch screen.
 Creates a grid of numbered rectangles, each of
 which will trap touches and report its id.
 Any of the four screen rotations may be used.
*/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>

// Default values for Adafruit shield v2.
#define TFT_DC 9
#define TFT_CS 10

#define isWithin(x,a,b)((x>=a) && (x<=b))

// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 ctp = Adafruit_FT6206();
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Assign human-readable names to some common 16-bit color values:
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

typedef struct {
  int left;
  int top;
  int width;
  int height;
} rectangle;

// Rotations 0,2 = portrait  : 0->USB=right,upper : 2->USB=left,lower
// Rotations 1,3 = landscape : 1->USB=left,upper  : 3->USB=right,lower

// These don't need to be ints as they will never go beyond 255 or below 0
const byte rotation = 3; //(0->3)
const byte row = 3;
const byte col = 3;

rectangle rect[col * row];
// Strings waste memory, use char* instead also static is not needed. 
char *btnTitle[col * row] = {"A", "B", "C", "D", "E", "F", "G", "H", "I"};

void showGrid()
{
  // some of these don't need to be ints
  int left, top;
  int l = 20;
  int t = 70;
  int w = 50;
  int h = 40;
  int hgap = 10;
  int vgap = 10;
  byte id = 0;
  for (byte j = 0; j < row; j++)
  {
    for (byte i = 0; i < col; i++)
    {
      left = l + i * (w + vgap);
      top = t + j * (h + hgap);
      rect[id].left = left;
      rect[id].top = top;
      rect[id].width = w;
      rect[id].height = h;
      tft.drawRect( left, top, w, h, WHITE);
      tft.setCursor(left + 5, top + 10);
      tft.print(btnTitle[id]);
      id++;
    }
  }
}

void showZeroZero()
{
  tft.setCursor(0, 0);
  tft.print("0,0");
}

void showTouchData(int xCoord, int yCoord, int id)
{
  // Erase old data
  tft.fillRect( 10, 30, tft.width() - 10, 20, BLUE );
  tft.setCursor(10, 30);
  tft.print(F("x=")); tft.print(xCoord); // strings that will never change, should be put in the flash memory
  tft.print(F(" y=")); tft.print(yCoord);
  tft.print(F(" id= ")); tft.print(id);
}

void setup() {
  tft.begin();
  if (! ctp.begin(40)) {  // pass in 'sensitivity' coefficient
    Serial.println(F("Couldn't start FT6206 touchscreen controller"));
    while (1);
  }

  tft.setRotation(rotation);
  tft.fillScreen(BLUE);
  tft.setTextColor(WHITE, BLUE);
  tft.setTextSize(2);
  showZeroZero();
  showGrid();
}

void loop()
{
  static uint16_t x, y; // no need to constantly make these variables
  static TS_Point p; // same with this.

  if (ctp.touched()) // do the code in this block only when the screen has been touched.
  {
    delay(100); // I'm still iffy about this, but for right now I won't bother with it. However if this were a bigger sketch, you wouldn't what to have anything in your code that blocks it.
    p = ctp.getPoint();

    if (rotation == 0) 
    {
      x = 240 - p.x;
      y = 320 - p.y
    }
    else if (rotation == 1) 
    {
      //  p.y reversed
      x = 320 - p.y;
      y = p.x;
    }
    else if (rotation == 2) 
    {
      x = p.x;
      y = p.y;
    }
    else if (rotation == 3) 
    {
      //  p.x, p.y reversed
      x = p.y;
      y = 240 - p.x;
    }
    
    for (byte count = 0; count < (col * row); count++)
    {
      if ( isWithin(x, rect[count].left, (rect[count].left + rect[count].width)) && isWithin(y, rect[count].top, (rect[count].top + rect[count].height)) )
      {
        showTouchData( x, y, count );
      }
    }
  }

}

The FT6206 does not need to be 'calibrated'.

I wasn't familiar with the library when I wrote that, which is why I added this.

In the meantime I will look into your touchscreen library.

Thank you for the constructive code review. Your suggestions do result in memory savings on my UNO:

Program storage: 33% (your code) vs. 40% (my code)
Global variables: 31% (your code) vs. 35% (my code)

Also thank you for embellishing my original demo which was posted to the Adafruit forum. I barely recognize it, but am glad that someone was able to use it. I'll be anxious to see how you fare with those small keys on a capacitive screen, since the fingertip is considerably larger than the stylus commonly used on a resistive screen.

aka Asteroid

Your welcome.

Also I don't have a capacitive touch screen yet, still resistive.

If I did have a capacitive screen that small, I would make my own stylus with a metal ballpoint pen (completely empty of course) and some foil.

I need help to control a stepper motor with an Arduino microcontroller.
I'm doing projects in my company and I would like to integrate LCD 4D SYSTEMS. I can help with some example please.

nestor2828:
I need help to control a stepper motor with an Arduino microcontroller.
I'm doing projects in my company and I would like to integrate LCD 4D SYSTEMS. I can help with some example please.

Sounds like you should create a new thread in "Motors, Mechanics and Power" section of the forum rather than hijack this one.

I could help, but to support company funded projects I charge a fixed fee for an initial consultation (plus any travel expenses), that is to scope the task, then a hourly rate (working from home only) this rate being based on urgency, complexity and final product validation needs.

HazardsMind:
I chose those values because they fit my screen. If you just print out the p.x and p.y before you put them into the map function, you can see what your screens raw touch data is. I used those values to set the min and max values.

The best way to get good results is to use a very fine pointer (don't scratch your screen) and touch the top left and bottom right corners of your display where you see the image.

You can do one axis at a time or print them either on the display or use the serial monitor. Make sure you know which axis you are looking at.

I got it to work using the min/max values of 0-320 for X and 0-240 for Y. I also needed to switch one of the values in the mapping variable since the touches were opposite on the screen what they should have been.

Now it works great. Thanks again!

Hay frn i am going to make key pade for enter numerical value how can i read that enterd numarical value that i enter through key pad or any keypad demo plzzzz....

So this has been dead for a while but I just wanted to update the thread. I have been working with a Moddable Two which is an esp32 that has an integrated ILI9341 driven TFT display and FT6206 driven Capacitive touchscreen. I found this thread as I was looking for a way to input the passcode for a wifi config program I am working on. It's nothing special. Just learning how to use this hardware with Arduino as opposed to the Moddable SDK. Thanks to your keyboard code with some modification I'm well on my way. I'm posting the updated code for anyone else who's trying something similar.

//
//Thanks to Adafruit forums member Asteroid for the original sketch!
//
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>

// This is calibration data for the raw touch data to the screen coordinates

#define IsWithin(x, a, b) ((x>=a)&&(x<=b))
#define TS_MINX 0
#define TS_MINY 0
#define TS_MAXX 320
#define TS_MAXY 240

// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 ts = Adafruit_FT6206(); // for the cap touchscreen

#define TFT_CS 15 // specific to the Moddable Two
#define TFT_DC 2  //specific to the Moddable Two

//Haven't had luck with hardware SPI on the Moddable Two

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, 13, 14, 0, 12); //specific to the Moddable Two

#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#endif

const char Mobile_KB[3][13] PROGMEM = {
 {0, 13, 10, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'},
 {1, 12, 9, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'},
 {3, 10, 7, 'Z', 'X', 'C', 'V', 'B', 'N', 'M'},
};

const char Mobile_NumKeys[3][13] PROGMEM = {
 {0, 13, 10, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
 {0, 13, 10, '-', '/', ':', ';', '(', ')', '

, '&', '@', '"'},
 {5, 8, 5, '.', ',', '?', '!', '''}
};

const char Mobile_SymKeys[3][13] PROGMEM = {
 {0, 13, 10, '[', ']', '{', '}', '#', '%', '^', '*', '+', '='},
 {4, 9, 6, '_', '\', '|', '~', '<', '>'}, //4
 {5, 8, 5, '.', ',', '?', '!', '''}
};

const char textLimit = 25;
char MyBuffer[textLimit];

void MakeKB_Button(const char type[][13])
{
 tft.setTextSize(2);
 tft.setTextColor(0xffff, 0xf000);
 for (int y = 0; y < 3; y++)
 {
   int ShiftRight = 15 * pgm_read_byte(&(type[y][0]));
   for (int x = 3; x < 13; x++)
   {
     if (x >= pgm_read_byte(&(type[y][1]))) break;

drawButton(15 + (30 * (x - 3)) + ShiftRight, 100 + (30 * y), 20, 25); // this will draw the button on the screen by so many pixels
     tft.setCursor(20 + (30 * (x - 3)) + ShiftRight, 105 + (30 * y));
     tft.print(char(pgm_read_byte(&(type[y][x]))));
   }
 }
 //ShiftKey
 drawButton(15, 160, 35, 25);
 tft.setCursor(27, 168);
 tft.print('^');

//Special Characters
 drawButton(15, 190, 35, 25);
 tft.setCursor(21, 195);
 tft.print(F("SP"));

//BackSpace
 drawButton(270, 160, 35, 25);
 tft.setCursor(276, 165);
 tft.print(F("BS"));

//Return
 drawButton(270, 190, 35, 25);
 tft.setCursor(276, 195);
 tft.print(F("RT"));

//Spacebar
 drawButton(60, 190, 200, 25);
 tft.setCursor(105, 195);
 tft.print(F("SPACE BAR"));
}

void drawButton(int x, int y, int w, int h)
{
 // grey
 tft.fillRoundRect(x - 3, y + 3, w, h, 3, 0x8888); //Button Shading

// white
 tft.fillRoundRect(x, y, w, h, 3, 0xffff);// outter button color

//red
 tft.fillRoundRect(x + 1, y + 1, w - 1 * 2, h - 1 * 2, 3, 0xf800); //inner button color
}

void GetKeyPress(char * textBuffer)
{
 char key = 0;
 static bool shift = false, special = false, back = false, lastSp = false, lastSh = false;
 static char bufIndex = 0;

if (ts.touched()) // changed for cap
 {
   //ShiftKey
   if (TouchButton(15, 160, 35, 25))
   {
     shift = !shift;
     delay(200);
   }

//Special Characters
   if (TouchButton(15, 190, 35, 25))
   {
     special = !special;
     delay(200);
   }

if (special != lastSp || shift != lastSh)
   {
     if (special)
     {
       if (shift)
       {
         tft.fillScreen(ILI9341_BLUE);
         MakeKB_Button(Mobile_SymKeys);
       }
       else
       {
         tft.fillScreen(ILI9341_BLUE);
         MakeKB_Button(Mobile_NumKeys);
       }
     }
     else
     {
       tft.fillScreen(ILI9341_BLUE);
       MakeKB_Button(Mobile_KB);
       tft.setTextColor(0xffff, 0xf800);
     }

if (special)
       tft.setTextColor(0x0FF0, 0xf800);
     else
       tft.setTextColor(0xFFFF, 0xf800);

tft.setCursor(21, 195);
     tft.print(F("SP"));

if (shift)
       tft.setTextColor(0x0FF0, 0xf800);
     else
       tft.setTextColor(0xffff, 0xf800);

tft.setCursor(27, 168);
     tft.print('^');

lastSh = shift;

lastSp = special;
     lastSh = shift;
   }

for (int y = 0; y < 3; y++)
   {
     int ShiftRight;
     if (special)
     {
       if (shift)
         ShiftRight = 15 * pgm_read_byte(&(Mobile_SymKeys[y][0]));
       else
         ShiftRight = 15 * pgm_read_byte(&(Mobile_NumKeys[y][0]));
     }
     else
       ShiftRight = 15 * pgm_read_byte(&(Mobile_KB[y][0]));

for (int x = 3; x < 13; x++)
     {
       if (x >=  (special ? (shift ? pgm_read_byte(&(Mobile_SymKeys[y][1])) : pgm_read_byte(&(Mobile_NumKeys[y][1]))) : pgm_read_byte(&(Mobile_KB[y][1])) )) break;

if (TouchButton(15 + (30 * (x - 3)) + ShiftRight, 100 + (30 * y), 20, 25)) // this will draw the button on the screen by so many pixels
       {
         if (bufIndex < (textLimit - 1))
         {
           delay(200);

if (special)
           {
             if (shift)
               textBuffer[bufIndex] = pgm_read_byte(&(Mobile_SymKeys[y][x]));
             else
               textBuffer[bufIndex] = pgm_read_byte(&(Mobile_NumKeys[y][x]));
           }
           else
             textBuffer[bufIndex] = (pgm_read_byte(&(Mobile_KB[y][x])) + (shift ? 0 : ('a' - 'A')));

bufIndex++;
         }
         break;
       }
     }
   }

//Spacebar
   if (TouchButton(60, 190, 200, 25))
   {
     textBuffer[bufIndex++] = ' ';
     delay(200);
   }

//BackSpace
   if (TouchButton(270, 160, 35, 25))
   {
     if ((bufIndex) > 0)
       bufIndex--;
     textBuffer[bufIndex] = 0;
     tft.setTextColor(0, ILI9341_BLUE);
     tft.setCursor(15, 80);
     tft.print(F("                          "));
     delay(200);
   }

//Return
   if (TouchButton(270, 190, 35, 25))
   {
     Serial.println(textBuffer);
     while (bufIndex > 0)
     {
       bufIndex--;
       textBuffer[bufIndex] = 0;
     }

tft.setTextColor(0, ILI9341_BLUE);
     tft.setCursor(15, 80);
     tft.print(F("                         "));
   }
 }
 tft.setTextColor(0xffff, 0xf800);
 tft.setCursor(15, 80);
 tft.print(textBuffer);
}

void setup(void)
{
 Serial.begin(115200);
 tft.begin();
 if (!ts.begin())
   Serial.println(F("Unable to start touchscreen."));
 else
   Serial.println(F("Touchscreen started."));

tft.fillScreen(ILI9341_BLUE);
 // origin = left,top landscape (USB left upper)
 tft.setRotation(2);
 MakeKB_Button(Mobile_KB);
}

void loop()
{
 // See if there's any  touch data for us
 GetKeyPress(MyBuffer);
}

byte TouchButton(int x, int y, int w, int h)
{
 int X, Y;
 // Retrieve a point
 TS_Point p = ts.getPoint();
 Y = map(p.x, TS_MINY, TS_MAXY, tft.height(), 0);
 X = map(p.y, TS_MINX, TS_MAXX, 0, tft.width());

return (IsWithin(X, x, x + w) & IsWithin(Y, y, y + h));
}