How to wire 2.8" TFT display to Arduino using custom pins?

Hi all, I am currently building a scale with two stepper motors. Right now I am only trying to get the display and touch controls working, but I am struggling a lot today. I have a CNC shield on a Arduino Mega and I use the ELEGOO 2.8" Touch Screen but using ribbon cables. That's because I cannot attach two Shields on one Arduino.

The following code is working and the display shows some buttons and text. However, the touch input does not work yet. That's what I ask for your help with. I managed to find the right library and pinout, otherwise the displays would not be working. I asked ChatGPT a bunch for advise, but it is pretty useless with this. It keeps telling me to connect physical touch pins, but they don´t exist on my TFT. This Adafruit article gives some insights on those pins, but I don't get it. Touchscreen Paint Example | 2.8" TFT Touch Shield | Adafruit Learning System

Anyway the touchscreen worked, but only when it was connected as a shield. Not with ribbon cables. Any ideas what I am doing wrong?





#include <Adafruit_GFX.h>

#include <Adafruit_TFTLCD.h>

#include <TouchScreen.h>

#include <AccelStepper.h>

#include <HX711.h>



// Color definitions

#define BLACK   0x0000

#define WHITE   0xFFFF

#define RED     0xF800

#define GREEN   0x07E0

#define BLUE    0x001F

#define CYAN    0x07FF

#define MAGENTA 0xF81F

#define YELLOW  0xFFE0



// ===================== Pin Mapping =====================

#define LCD_CS    A8   // Chip Select

#define LCD_CD    A9   // Command/Data

#define LCD_WR    A10  // Write

#define LCD_RD    A11  // Read

#define LCD_RESET A12  // Reset

// Data D0–D7 -> Mega pins 22..29



// Touchscreen (4-wire resistive) breakout wiring (paint example)

#define YP A2   // must be analog pin

#define XM A3   // must be analog pin

#define YM 9    // can be digital pin

#define XP 8    // can be digital pin

#define TS_MINX 120

#define TS_MAXX 900

#define TS_MINY 70

#define TS_MAXY 920

#define MINPRESSURE 10

#define MAXPRESSURE 1000

TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);



// Instantiate TFT TouchScreen(XP, YP, XM, YM, 300);



// Instantiate TFT

typedef uint16_t color_t;

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);



// Steppers

#define stepPinX 2

#define dirPinX  5

#define EndstopX 9

AccelStepper stepperX(AccelStepper::DRIVER, stepPinX, dirPinX);



#define stepPinY 3

#define dirPinY  4

#define EndstopY 11

AccelStepper stepperY(AccelStepper::DRIVER, stepPinY, dirPinY);



// HX711 scale

#define DOUT 32

#define CLK  33

HX711 scale;



#define RelayPin 12



// UI state

String inputText = "";

int Wunschanzahl = 0;

float WunschgewichtTotal = 0;

float GewichtEineSchraube  = 0;

bool  runPressed        = false;

bool  stopPressed       = false;

bool  WeighingComplete  = false;



// Generic button drawer

void drawButton(int x, int y, int w, int h, const char\* label, color_t bg, color_t fg) {

tft.fillRect(x, y, w, h, bg);

tft.drawRect(x, y, w, h, fg);

tft.setTextSize(2);

int16_t textW = 6 \* strlen(label) \* 2; // char width 6px \* size

int16_t textH = 8 \* 2;                 // char height 8px \* size

int16_t tx = x + (w - textW) / 2;

int16_t ty = y + (h - textH) / 2;

tft.setCursor(tx, ty);

tft.setTextColor(fg);

tft.print(label);

}



// Draw keypad & controls with adjusted sizes

void drawKeypad() {

tft.fillScreen(BLACK);

  // Numeric keys: narrower

const int numW = 40, numH = 40, numSX = 45, numSY = 45;

static const char\* keys\[4\]\[3\] = {{"1","2","3"},{"4","5","6"},{"7","8","9"},{"Del","0","OK"}};

for(int r=0; r<4; r++) {

for(int c=0; c<3; c++) {

int x = c \* numSX;

int y = r \* numSY;

drawButton(x, y, numW, numH, keys\[r\]\[c\], BLACK, WHITE);

}

}

    // Control keys: wider (moved left)

const int ctrlW = 70, ctrlH = 40;

const int ctrlX = 150;  // moved left from 180

drawButton(ctrlX,    0,   ctrlW, ctrlH, "Enter", BLACK, WHITE);

drawButton(ctrlX,   50,   ctrlW, ctrlH, "Run",   GREEN, BLACK);

drawButton(ctrlX,  100,   ctrlW, ctrlH, "Stop",  RED,   BLACK);

drawButton(ctrlX,  150,   ctrlW, ctrlH, "TARE",  CYAN,  BLACK);

}



// Update numeric displays

void updateDisplays() {

tft.fillRect(0, 200, 320, 60, BLACK);

tft.setTextSize(2);

tft.setCursor(0, 200);

tft.setTextColor(GREEN);

tft.print("Wunschzahl: "); tft.print(Wunschanzahl);

tft.setCursor(0, 220);

tft.setTextColor(CYAN);

tft.print("Eingabe: "); tft.print(inputText);

tft.setCursor(0, 240);

tft.setTextColor(YELLOW);

tft.print("g/Stk: "); tft.print(GewichtEineSchraube, 2);

}



// Live weight feedback

void updateWeightDisplay(float w) {

tft.fillRect(0, 280, 320, 20, BLACK);

tft.setTextSize(2);

tft.setCursor(0, 280);

float delta = abs(WunschgewichtTotal - w);

float tol   = WunschgewichtTotal \* 0.01;

color_t col = (delta <= tol) ? GREEN : ((delta <= GewichtEineSchraube\*3) ? YELLOW : RED);

tft.setTextColor(col);

tft.print("Gewicht: "); tft.print(w, 2);

}



// Homing routines

void HomingX() {

stepperX.setMaxSpeed(500);

stepperX.setSpeed(-500);

while(digitalRead(EndstopX)==HIGH) stepperX.runSpeed();

stepperX.stop(); delay(200);

stepperX.setCurrentPosition(0);

}

void HomingY() {

stepperY.setMaxSpeed(500);

stepperY.setSpeed(-500);

while(digitalRead(EndstopY)==HIGH) stepperY.runSpeed();

stepperY.stop(); delay(200);

stepperY.setCurrentPosition(0);

}



// Button press handler

void handleKey(const char\* label) {

if(!strcmp(label,"Del") && inputText.length()) inputText.remove(inputText.length()-1);

else if(!strcmp(label,"OK")) { Wunschanzahl=inputText.toInt(); inputText=""; }

else if(!strcmp(label,"Enter")) { scale.tare(); delay(500); WunschgewichtTotal=scale.get_units(20); GewichtEineSchraube=WunschgewichtTotal/Wunschanzahl; }

else if(!strcmp(label,"TARE")) scale.tare();

else if(!strcmp(label,"Run")) { runPressed=true; stopPressed=false; }

else if(!strcmp(label,"Stop")) { stopPressed=true; runPressed=false; }

else inputText+=label;

updateDisplays();

}



// Run sequence

void checkRunSequence() {

if(runPressed && !WeighingComplete) {

HomingX();

while(!WeighingComplete && !stopPressed) {

stepperX.setMaxSpeed(2000);

stepperX.move(10000); while(stepperX.distanceToGo()) stepperX.run(); delay(300);

stepperX.move(-10000); while(stepperX.distanceToGo()) stepperX.run(); delay(1000);

float cw=scale.get_units(20); updateWeightDisplay(cw);

float rem=WunschgewichtTotal-cw;

if(rem<=GewichtEineSchraube\*3) {

stepperX.move(8000); while(stepperX.distanceToGo()) stepperX.run();

for(int i=0;i<4;i++){ stepperX.move(500); while(stepperX.distanceToGo()) stepperX.run(); delay(400); }

}

if(abs(cw-WunschgewichtTotal)<WunschgewichtTotal\*0.01) { WeighingComplete=true; HomingY(); }

}

    runPressed=false;

}

}



void setup() {

Serial.begin(9600);

pinMode(RelayPin,OUTPUT);

pinMode(EndstopX,INPUT_PULLUP);

pinMode(EndstopY,INPUT_PULLUP);

stepperX.setAcceleration(9000); stepperX.setMaxSpeed(4500);

stepperY.setAcceleration(1000); stepperY.setMaxSpeed(3500);

tft.reset(); uint16_t id=tft.readID(); tft.begin(id); tft.setRotation(0);

drawKeypad(); updateDisplays();

scale.begin(DOUT,CLK); scale.set_scale(); scale.tare();

}



void loop() {

  TSPoint p=ts.getPoint(); pinMode(XP,OUTPUT); pinMode(YP,OUTPUT);

if(p.z>MINPRESSURE&&p.z<MAXPRESSURE) {

int x=map(p.x,TS_MINX,TS_MAXX,0,tft.width());

int y=map(p.y,TS_MINY,TS_MAXY,0,tft.height());

static const char\* keys\[4\]\[3\]={{"1","2","3"},{"4","5","6"},{"7","8","9"},{"Del","0","OK"}};

for(int r=0;r<4;r++) for(int c=0;c<3;c++){

int bx=c\*45,by=r\*45;

if(x>bx&&x<bx+40&&y>by&&y<by+40){ handleKey(keys\[r\]\[c\]); delay(300); }

}

if(x>150 && x<150+70){

if(y<40) handleKey("Enter");

else if(y<90) handleKey("Run");

else if(y<140) handleKey("Stop");

else if(y<190) handleKey("TARE");

delay(300);

}

}

float lw=scale.get_units(10); updateWeightDisplay(lw); checkRunSequence(); delay(200);

}
  • 1st confirm the continuity of each ribbon cable wire.

I spent 6 hours troubleshooting. I even checked continuity on those tiny access tabs on the connectors.

If you have the touchscreen pins correct for using the display as a shield:

#define YP A2   // must be analog pin
#define XM A3   // must be analog pin
#define YM 9    // can be digital pin
#define XP 8    // can be digital pin

Then they should be wired as follows. I'm not sure which pins you are using for the LCD data lines, and you appear to be calling LCD_RS by another name. The pins are shared with the display, which is why YP and XM have to be set back to output mode immediately after calling the ts.getPoint() function. (although you seem to be setting XP to output mode instead of XM, it should be whichever pins of the touchscreen are connected to LCD_RS and LCD_CS).

YP = same as LCD_RS
XM = same as LCD_CS
YM = same as LCD_D1
XP = same as LCD_D0

< edit > Oops, had YM and XP wrong, had trouble matching the picture to a UNO pinout.

The touch screen shares pins with the TFT display, if the display is working then the wiring is OK.

Read the library intro.

// Touchscreen (4-wire resistive) breakout wiring (paint example)
#define YP A2   // must be analog pin
#define XM A3   // must be analog pin
#define YM 9    // can be digital pin
#define XP 8    // can be digital pin

You have no connection to any of the touchscreen pins that you have defined. That's why the touchscreen doesn't work.

As @david_2018 has already pointed out, the touchscreen pins on the shield are shared between the display and the touchscreen and do double duty. You will have to redefine the touchscreen pins to be whatever you've moved the corresponding display pin to. And LCD_RS (YP) and LCD_CS (XM) must be on analog pins.

I am using A8-A12 instead of A1-A5. Thank you for your explanations. How am I supposed to know which pin is X and Y? I read the tutorials and manual many times. I never found that information.

I though you said the touchscreen worked when it was connected as a shield. If it was working at that time, then you can tell by the sketch which pins were being used for the touchscreen.