#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>
#define TFT_CS 5
#define TFT_DC 2
#define TFT_RST 4
#define SCREEN_W 240
#define SCREEN_H 320
#define DISPLAY_X 10
#define DISPLAY_Y 10
#define DISPLAY_W 220
#define DISPLAY_H 50
#define KEY_COLS 3
#define KEY_ROWS 4
#define BTN_W 60
#define BTN_H 58 // fills gap so edge touches work
#define BTN_GAP_X 70
#define BTN_GAP_Y 60
#define BTN_ORIGIN_X 20
#define BTN_ORIGIN_Y 80
#define MAX_DIGITS 10
#define DEBOUNCE_MS 250
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Adafruit_FT6206 ctouch = Adafruit_FT6206();
const char* KEYS[KEY_ROWS][KEY_COLS] = {
{"1", "2", "3"},
{"4", "5", "6"},
{"7", "8", "9"},
{"C", "0", "X"}
};
String currentNumber = "";
uint32_t lastTouchMs = 0;
// ─────────────────────────────────────────────────────────────────
uint16_t keyColor(const char* label) {
if (strcmp(label, "C") == 0) return ILI9341_RED;
if (strcmp(label, "X") == 0) return 0xFD20;
return 0x2945;
}
// ─────────────────────────────────────────────────────────────────
void drawButton(int x, int y, const char* label, uint16_t color) {
tft.fillRoundRect(x, y, BTN_W, BTN_H, 8, color);
tft.drawRoundRect(x, y, BTN_W, BTN_H, 8, ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
int textW = strlen(label) * 12;
tft.setCursor(x + (BTN_W - textW) / 2,
y + (BTN_H - 16) / 2);
tft.print(label);
}
// ─────────────────────────────────────────────────────────────────
void drawKeypad() {
for (int r = 0; r < KEY_ROWS; r++) {
for (int c = 0; c < KEY_COLS; c++) {
drawButton(
BTN_ORIGIN_X + c * BTN_GAP_X,
BTN_ORIGIN_Y + r * BTN_GAP_Y,
KEYS[r][c],
keyColor(KEYS[r][c])
);
}
}
}
// ─────────────────────────────────────────────────────────────────
void updateDisplay() {
tft.fillRect(DISPLAY_X + 1, DISPLAY_Y + 1,
DISPLAY_W - 2, DISPLAY_H - 2,
ILI9341_BLACK);
tft.setTextSize(3);
tft.setTextColor(ILI9341_CYAN);
int textW = currentNumber.length() * 18;
int tx = DISPLAY_X + (DISPLAY_W - textW) / 2;
int ty = DISPLAY_Y + (DISPLAY_H - 24) / 2;
tft.setCursor(tx, ty);
tft.print(currentNumber);
}
// ─────────────────────────────────────────────────────────────────
void flashButton(int row, int col) {
int x = BTN_ORIGIN_X + col * BTN_GAP_X;
int y = BTN_ORIGIN_Y + row * BTN_GAP_Y;
tft.fillRoundRect(x, y, BTN_W, BTN_H, 8, ILI9341_WHITE);
tft.setTextSize(2);
tft.setTextColor(ILI9341_BLACK);
int textW = strlen(KEYS[row][col]) * 12;
tft.setCursor(x + (BTN_W - textW) / 2, y + (BTN_H - 16) / 2);
tft.print(KEYS[row][col]);
delay(80);
drawButton(x, y, KEYS[row][col], keyColor(KEYS[row][col]));
}
// ─────────────────────────────────────────────────────────────────
// Check touch against every button's exact pixel bounding box
// Much more reliable than math-based col/row calculation
// ─────────────────────────────────────────────────────────────────
bool getTouchedButton(int tx, int ty, int &outRow, int &outCol) {
for (int r = 0; r < KEY_ROWS; r++) {
for (int c = 0; c < KEY_COLS; c++) {
// Pixel coordinates of this button
int bx = BTN_ORIGIN_X + c * BTN_GAP_X;
int by = BTN_ORIGIN_Y + r * BTN_GAP_Y;
// Hit test
if (tx >= bx && tx <= bx + BTN_W &&
ty >= by && ty <= by + BTN_H) {
outRow = r;
outCol = c;
return true;
}
}
}
return false;
}
// ══════════════════════════════════════════════════════════════════
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
tft.begin();
tft.setRotation(0);
tft.fillScreen(ILI9341_BLACK);
if (!ctouch.begin(40)) {
Serial.println("FT6206 not found!");
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.setCursor(10, 150);
tft.print("TOUCH ERROR");
while (1) delay(100);
}
tft.drawRect(DISPLAY_X, DISPLAY_Y, DISPLAY_W, DISPLAY_H, ILI9341_CYAN);
drawKeypad();
updateDisplay();
Serial.println("Ready.");
}
// ══════════════════════════════════════════════════════════════════
void loop() {
if (!ctouch.touched()) return;
uint32_t now = millis();
if (now - lastTouchMs < DEBOUNCE_MS) return;
lastTouchMs = now;
TS_Point p = ctouch.getPoint();
// Both axes are mirrored on this module
int tx = SCREEN_W - p.x;
int ty = SCREEN_H - p.y;
Serial.printf("\nRAW x=%-4d y=%-4d → tx=%-4d ty=%-4d\n",
p.x, p.y, tx, ty);
int row, col;
if (!getTouchedButton(tx, ty, row, col)) {
Serial.println(" → no button hit");
return;
}
const char* pressed = KEYS[row][col];
Serial.printf(" → row=%d col=%d KEY=%s\n", row, col, pressed);
if (strcmp(pressed, "C") == 0) {
currentNumber = "";
}
else if (strcmp(pressed, "X") == 0) {
if (currentNumber.length() > 0)
currentNumber.remove(currentNumber.length() - 1);
}
else {
if ((int)currentNumber.length() < MAX_DIGITS)
currentNumber += pressed;
}
flashButton(row, col);
updateDisplay();
}