#include <Arduino_GFX_Library.h>
#include <XPT2046_Touchscreen.h>
#include <SPI.h>
#define TFT_BL 21
#define TFT_DC 2
#define TFT_CS 15
#define TFT_SCLK 14
#define TFT_MOSI 13
#define TFT_MISO 12
#define TFT_RST -1
#define TOUCH_CS 33
#define TOUCH_IRQ 36
#define TOUCH_MOSI 32
#define TOUCH_MISO 39
#define TOUCH_SCLK 25
#define SCREEN_W 320
#define SCREEN_H 240
#define BG_COLOR 0x000F
#define WHITE 0xFFFF
#define GREEN 0x07E0
#define RED 0xF800
#define BLACK 0x0000
Arduino_DataBus *bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, TFT_SCLK, TFT_MOSI, TFT_MISO);
Arduino_GFX *tft = new Arduino_ILI9341(bus, TFT_RST, 1, false);
SPIClass touchSPI(VSPI);
XPT2046_Touchscreen ts(TOUCH_CS, TOUCH_IRQ);
struct Car {
int x, y, w, h;
const char* label;
const char* series;
long maxWeight;
};
Car cars[6] = {
{20, 50, 120, 40, "Boxcar", "3000 - 3749", 72300},
{180, 50, 120, 40, "Gondola", "5500 - 5699", 82500},
{20, 100, 120, 40, "Flat Car", "6500 - 6544", 80000},
{180,100, 120, 40, "Tank Car", "UTLX 11000 - 11058", 65000},
{20, 150, 120, 40, "S Reefer", "32 - 81", 62000},
{180,150, 120, 40, "L Reefer", "150 - 169", 84000}
};
bool detailScreen = false;
int currentCarIndex = -1;
long currentWeight = 0;
bool overweight = false;
void drawCentreString(const char* text, int cx, int y, int size) {
tft->setTextSize(size);
int textW = strlen(text) * 6 * size;
int x = cx - textW / 2;
tft->setCursor(x, y);
tft->print(text);
}
String formatWeight(long value) {
String s = String(value);
String out = "";
int count = 0;
for (int i = s.length() - 1; i >= 0; i--) {
out = s[i] + out;
count++;
if (count == 3 && i > 0) {
out = "," + out;
count = 0;
}
}
return out + " lbs";
}
void generateWeight(int i) {
long maxW = cars[i].maxWeight;
overweight = (random(100) < 5);
if (overweight) {
currentWeight = random(maxW + 1, maxW + (maxW / 10));
}
else {
currentWeight = random(maxW * 80 / 100, maxW + 1);
}
}
void drawMainScreen() {
tft->fillScreen(BG_COLOR);
tft->setTextColor(WHITE);
drawCentreString("Scale Track", SCREEN_W / 2, 10, 3);
for (int i = 0; i < 6; i++) {
tft->drawRoundRect(
cars[i].x,
cars[i].y,
cars[i].w,
cars[i].h,
8,
WHITE
);
drawCentreString(
cars[i].label,
cars[i].x + cars[i].w / 2,
cars[i].y + 12,
2
);
}
}
void drawDetailScreen() {
Car c = cars[currentCarIndex];
tft->fillScreen(BG_COLOR);
tft->setTextColor(WHITE);
tft->fillTriangle(
10, 25,
35, 10,
35, 40,
WHITE
);
drawCentreString(c.label, SCREEN_W / 2, 20, 3);
String weightText = formatWeight(currentWeight);
if (overweight) {
tft->setTextColor(RED);
drawCentreString(
"OVERWEIGHT",
SCREEN_W / 2,
135,
2
);
}
else {
tft->setTextColor(WHITE);
tft->drawRoundRect(
35,
80,
250,
55,
8,
GREEN
);
}
drawCentreString(
weightText.c_str(),
SCREEN_W / 2,
93,
3
);
tft->setTextColor(WHITE);
tft->setTextSize(2);
tft->setCursor(25, 175);
tft->print("Series: ");
tft->print(c.series);
tft->setCursor(25, 205);
tft->print("Max Weight: ");
tft->print(formatWeight(c.maxWeight));
}
bool touchedIn(
int x,
int y,
int w,
int h,
int tx,
int ty
) {
return (
tx >= x &&
tx <= x + w &&
ty >= y &&
ty <= y + h
);
}
void getTouchXY(int &x, int &y) {
TS_Point p = ts.getPoint();
x = map(p.x, 200, 3900, 0, SCREEN_W);
y = map(p.y, 200, 3900, 0, SCREEN_H);
x = constrain(x, 0, SCREEN_W);
y = constrain(y, 0, SCREEN_H);
}
void setup() {
Serial.begin(115200);
delay(500);
randomSeed(analogRead(34));
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
tft->begin();
tft->setRotation(1);
touchSPI.begin(
TOUCH_SCLK,
TOUCH_MISO,
TOUCH_MOSI,
TOUCH_CS
);
ts.begin(touchSPI);
ts.setRotation(1);
drawMainScreen();
}
void loop() {
if (ts.touched()) {
int tx, ty;
getTouchXY(tx, ty);
if (!detailScreen) {
for (int i = 0; i < 6; i++) {
if (touchedIn(
cars[i].x,
cars[i].y,
cars[i].w,
cars[i].h,
tx,
ty
)) {
currentCarIndex = i;
generateWeight(i);
detailScreen = true;
drawDetailScreen();
delay(300);
break;
}
}
}
else {
if (touchedIn(0, 0, 60, 55, tx, ty)) {
detailScreen = false;
drawMainScreen();
delay(300);
}
}
}
delay(10);
}