the config and pins are not edited, as the cheap yellow display 2.8 comes in presoldered. The sketch is:
#include <TFT_eSPI.h>
#include <SPI.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
#include <esp_system.h>
#include <esp_timer.h>
#include <esp_adc_cal.h>
#include <XPT2046_Touchscreen.h>
// TFT_eSPI configuration for Cheap Yellow Display
#define TFT_MISO -1 // Not used
#define TFT_MOSI 23 // IO23
#define TFT_SCLK 18 // IO18
#define TFT_CS 5 // IO5
#define TFT_DC 2 // IO2
#define TFT_RST 4 // IO4
#define TFT_BL 15 // IO27 (Backlight control for CYD)
// Touch configuration
#define TOUCH_CS 21 // IO21 (XPT2046 touch controller)
// Display instance
TFT_eSPI tft = TFT_eSPI();
XPT2046_Touchscreen ts(TOUCH_CS);
// System information
struct SystemInfo {
uint32_t freeHeap;
uint32_t totalHeap;
float cpuTemp;
String currentTime;
String currentDate;
uint32_t sessionNumber;
String wifiSSID;
int32_t wifiRSSI;
uint32_t wifiSpeed;
uint32_t uptime;
} sysInfo;
// Boot sequence state
enum BootState {
LOGO_DISPLAY,
TITLE_DISPLAY,
SYSTEM_INFO_DISPLAY,
WIFI_CONNECTING,
READY
};
BootState bootState = LOGO_DISPLAY;
unsigned long bootStartTime = 0;
const int BOOT_DELAY = 2000; // 2 seconds per screen
// WiFi network structure
struct WiFiNetwork {
String ssid;
int32_t rssi;
uint8_t encryptionType;
};
// WiFi configuration
const char* defaultSSID = "Virtual Death Spot";
const char* defaultPassword = "Vita101Vita";
std::vector<WiFiNetwork> availableNetworks;
int selectedNetworkIndex = 0;
bool isSelectingNetwork = false;
// Display colors
#define BACKGROUND_COLOR TFT_BLACK
#define TEXT_COLOR TFT_WHITE
#define HIGHLIGHT_COLOR TFT_YELLOW
// Touch screen calibration
#define TOUCH_CALIBRATION_X 0
#define TOUCH_CALIBRATION_Y 0
#define TOUCH_CALIBRATION_X1 240 // Changed from 320 to match display width
#define TOUCH_CALIBRATION_Y1 320 // Changed from 240 to match display height
// System state
enum SystemState {
BOOTING,
WIFI_SELECTION,
SYSTEM_INFO,
CHAT_MODE
};
SystemState currentState = BOOTING;
// Chat interface variables
String inputBuffer = "";
String responseBuffer = "";
bool isWaitingForResponse = false;
int cursorY = 50;
const int MAX_LINES = 8;
const int LINE_HEIGHT = 25;
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Initialize display with more detailed sequence
Serial.println("Initializing display...");
// Hardware reset
pinMode(TFT_RST, OUTPUT);
digitalWrite(TFT_RST, HIGH);
delay(100);
digitalWrite(TFT_RST, LOW);
delay(100);
digitalWrite(TFT_RST, HIGH);
delay(100);
// Initialize display
tft.init();
delay(100);
// Software reset
Serial.println("Resetting display...");
tft.writecommand(ST7789_SWRESET);
delay(150);
// Exit sleep mode
Serial.println("Exiting sleep mode...");
tft.writecommand(ST7789_SLPOUT);
delay(100);
// Set color mode to 16-bit per pixel (0x05 = 16-bit color)
tft.writecommand(ST7789_COLMOD);
tft.writedata(0x06); // Changed from 0x05 to 0x06 for 18-bit color
delay(10);
// Set display orientation (0x00 = normal, 0x60 = rotated)
tft.writecommand(ST7789_MADCTL);
tft.writedata(0x00); // Changed back to 0x00 for normal orientation
delay(10);
// Set display parameters
Serial.println("Setting display parameters...");
// Frame rate control
tft.writecommand(ST7789_FRMCTR1);
tft.writedata(0x01);
tft.writedata(0x2C);
tft.writedata(0x2D);
delay(10);
tft.writecommand(ST7789_FRMCTR2);
tft.writedata(0x01);
tft.writedata(0x2C);
tft.writedata(0x2D);
delay(10);
// Display inversion control
tft.writecommand(ST7789_INVCTR);
tft.writedata(0x07);
delay(10);
// Power control
tft.writecommand(ST7789_PWCTR1);
tft.writedata(0xA2);
tft.writedata(0x02);
tft.writedata(0x84);
delay(10);
tft.writecommand(ST7789_PWCTR2);
tft.writedata(0xC5);
delay(10);
tft.writecommand(ST7789_PWCTR3);
tft.writedata(0x0A);
tft.writedata(0x00);
delay(10);
tft.writecommand(ST7789_PWCTR4);
tft.writedata(0x8A);
tft.writedata(0x2A);
delay(10);
tft.writecommand(ST7789_PWCTR5);
tft.writedata(0x8A);
tft.writedata(0xEE);
delay(10);
// VCOM voltage
tft.writecommand(ST7789_VMCTR1);
tft.writedata(0x0E);
delay(10);
// Turn on display
Serial.println("Turning on display...");
tft.writecommand(ST7789_DISPON);
delay(100);
// Set rotation
Serial.println("Setting rotation...");
tft.setRotation(0);
// Clear screen
Serial.println("Clearing screen...");
tft.fillScreen(TFT_BLACK);
delay(100);
// Initialize backlight
Serial.println("Initializing backlight...");
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
// More visible test pattern with longer delays
Serial.println("Starting display test pattern...");
// Test 1: Full screen colors
Serial.println("Drawing red screen...");
tft.fillScreen(TFT_RED);
delay(2000);
Serial.println("Drawing green screen...");
tft.fillScreen(TFT_GREEN);
delay(2000);
Serial.println("Drawing blue screen...");
tft.fillScreen(TFT_BLUE);
delay(2000);
// Test 2: Grid pattern
Serial.println("Drawing grid pattern...");
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Display Test");
// Draw a grid
for(int x = 0; x < tft.width(); x += 20) {
tft.drawLine(x, 0, x, tft.height(), TFT_WHITE);
}
for(int y = 0; y < tft.height(); y += 20) {
tft.drawLine(0, y, tft.width(), y, TFT_WHITE);
}
delay(2000);
// Test 3: Text test
Serial.println("Drawing text test...");
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.setTextSize(3);
tft.setCursor(40, 80);
tft.println("ARPA CORP");
tft.setTextSize(2);
tft.setCursor(40, 120);
tft.println("Display Test");
delay(2000);
// Initialize system info
initSystemInfo();
Serial.println("System info initialized");
// Start boot sequence
bootStartTime = millis();
bootState = LOGO_DISPLAY; // Explicitly set initial state
showBootSequence();
Serial.println("Boot sequence started");
// Initialize WiFi and scan networks
initWiFi();
// Initialize touch
touch_init();
// Enter chat mode
currentState = CHAT_MODE;
initChatInterface();
Serial.println("Setup complete");
}
void initSystemInfo() {
// Initialize NTP for time
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
// Generate session number (based on boot time)
sysInfo.sessionNumber = esp_timer_get_time() / 1000000;
// Initialize ADC for temperature
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
}
void updateSystemInfo() {
// Update memory info
sysInfo.freeHeap = ESP.getFreeHeap();
sysInfo.totalHeap = ESP.getHeapSize();
// Update temperature
sysInfo.cpuTemp = (analogRead(36) * 3.3 / 4095.0) * 100.0; // Convert to Celsius
// Update time
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
char timeStr[9];
char dateStr[11];
strftime(timeStr, sizeof(timeStr), "%H:%M:%S", &timeinfo);
strftime(dateStr, sizeof(dateStr), "%Y-%m-%d", &timeinfo);
sysInfo.currentTime = String(timeStr);
sysInfo.currentDate = String(dateStr);
}
// Update WiFi info
if (WiFi.status() == WL_CONNECTED) {
sysInfo.wifiSSID = WiFi.SSID();
sysInfo.wifiRSSI = WiFi.RSSI();
sysInfo.wifiSpeed = WiFi.channel();
}
// Update uptime
sysInfo.uptime = (millis() - bootStartTime) / 1000;
}
void showBootSequence() {
switch (bootState) {
case LOGO_DISPLAY:
showLogo();
if (millis() - bootStartTime > BOOT_DELAY) {
bootState = TITLE_DISPLAY;
bootStartTime = millis();
}
break;
case TITLE_DISPLAY:
showTitle();
if (millis() - bootStartTime > BOOT_DELAY) {
bootState = SYSTEM_INFO_DISPLAY;
bootStartTime = millis();
}
break;
case SYSTEM_INFO_DISPLAY:
updateSystemInfo();
showSystemInfo();
if (millis() - bootStartTime > BOOT_DELAY) {
bootState = WIFI_CONNECTING;
bootStartTime = millis();
}
break;
case WIFI_CONNECTING:
// WiFi connection is handled in initWiFi()
break;
case READY:
// Ready for chat mode
break;
}
}
void showLogo() {
Serial.println("Showing logo...");
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW);
tft.setTextSize(3);
// Draw ARPA CORP logo
tft.setCursor(40, 80);
tft.println("ARPA CORP");
// Draw a simple logo design
tft.drawRect(30, 30, 260, 40, TFT_YELLOW);
tft.drawLine(30, 50, 290, 50, TFT_YELLOW);
Serial.println("Logo displayed");
}
void showTitle() {
tft.fillScreen(BACKGROUND_COLOR);
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(2);
// Display title
tft.setCursor(10, 80);
tft.println("Prototype Portable");
tft.setCursor(10, 110);
tft.println("SCI Interface Kit");
// Draw a border
tft.drawRect(5, 70, 310, 60, HIGHLIGHT_COLOR);
}
void showSystemInfo() {
tft.fillScreen(BACKGROUND_COLOR);
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(1);
int y = 10;
int lineHeight = 15;
// System Information
tft.setTextColor(HIGHLIGHT_COLOR);
tft.setCursor(10, y);
tft.println("System Information");
tft.setTextColor(TEXT_COLOR);
y += lineHeight * 2;
// Memory
tft.setCursor(10, y);
tft.printf("Memory: %d/%d KB", sysInfo.freeHeap/1024, sysInfo.totalHeap/1024);
y += lineHeight;
// Temperature
tft.setCursor(10, y);
tft.printf("CPU Temp: %.1f°C", sysInfo.cpuTemp);
y += lineHeight;
// Time and Date
tft.setCursor(10, y);
tft.printf("Time: %s", sysInfo.currentTime.c_str());
y += lineHeight;
tft.setCursor(10, y);
tft.printf("Date: %s", sysInfo.currentDate.c_str());
y += lineHeight;
// Session
tft.setCursor(10, y);
tft.printf("Session: #%d", sysInfo.sessionNumber);
y += lineHeight;
// WiFi Information
if (WiFi.status() == WL_CONNECTED) {
tft.setCursor(10, y);
tft.printf("WiFi: %s", sysInfo.wifiSSID.c_str());
y += lineHeight;
tft.setCursor(10, y);
tft.printf("Signal: %d dBm", sysInfo.wifiRSSI);
y += lineHeight;
tft.setCursor(10, y);
tft.printf("Channel: %d", sysInfo.wifiSpeed);
}
y += lineHeight;
// Uptime
tft.setCursor(10, y);
tft.printf("Uptime: %d seconds", sysInfo.uptime);
}
void loop() {
if (bootState != READY) {
showBootSequence();
return;
}
switch(currentState) {
case BOOTING:
// Handle booting state
break;
case WIFI_SELECTION:
if (isSelectingNetwork) {
handleNetworkSelection();
}
break;
case SYSTEM_INFO:
// Handle system info state
break;
case CHAT_MODE:
handleChatMode();
break;
}
}
void touch_init() {
ts.begin();
ts.setRotation(0);
Serial.println("Touch screen initialized");
}
void initWiFi() {
currentState = WIFI_SELECTION;
tft.fillScreen(BACKGROUND_COLOR);
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Scanning WiFi networks...");
// Start WiFi in station mode
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
// Scan for networks
int numNetworks = WiFi.scanNetworks();
availableNetworks.clear();
for (int i = 0; i < numNetworks; i++) {
WiFiNetwork network;
network.ssid = WiFi.SSID(i);
network.rssi = WiFi.RSSI(i);
network.encryptionType = WiFi.encryptionType(i);
availableNetworks.push_back(network);
}
// Sort networks by signal strength
std::sort(availableNetworks.begin(), availableNetworks.end(),
[](const WiFiNetwork& a, const WiFiNetwork& b) {
return a.rssi > b.rssi;
});
if (availableNetworks.size() > 0) {
isSelectingNetwork = true;
displayNetworkSelection();
} else {
// Fallback to default network
connectToDefaultNetwork();
}
}
void displayNetworkSelection() {
tft.fillScreen(BACKGROUND_COLOR);
tft.setTextColor(HIGHLIGHT_COLOR);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Select WiFi Network:");
tft.setTextColor(TEXT_COLOR);
for (int i = 0; i < availableNetworks.size() && i < 5; i++) {
tft.setCursor(20, 40 + (i * 30));
if (i == selectedNetworkIndex) {
tft.print("> ");
}
String networkInfo = availableNetworks[i].ssid;
networkInfo += " (";
networkInfo += availableNetworks[i].rssi;
networkInfo += " dBm)";
tft.println(networkInfo);
}
tft.setCursor(10, 200);
tft.println("Up/Down: Select");
tft.setCursor(10, 230);
tft.println("Enter: Connect");
}
void handleNetworkSelection() {
// Only touch input supported for now
// You can add touch-based up/down/enter logic here if desired
}
void connectToSelectedNetwork() {
if (selectedNetworkIndex < availableNetworks.size()) {
tft.fillScreen(BACKGROUND_COLOR);
tft.setCursor(10, 10);
tft.println("Connecting to:");
tft.setCursor(10, 40);
tft.println(availableNetworks[selectedNetworkIndex].ssid);
// Try to connect to the selected network
WiFi.begin(availableNetworks[selectedNetworkIndex].ssid.c_str());
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
tft.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
tft.println("\nConnected!");
delay(1000);
isSelectingNetwork = false;
currentState = SYSTEM_INFO;
} else {
tft.println("\nConnection failed!");
tft.println("Falling back to default network...");
delay(2000);
connectToDefaultNetwork();
}
}
}
void connectToDefaultNetwork() {
tft.fillScreen(BACKGROUND_COLOR);
tft.setCursor(10, 10);
tft.println("Connecting to default network...");
WiFi.begin(defaultSSID, defaultPassword);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
tft.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
tft.println("\nConnected!");
delay(1000);
isSelectingNetwork = false;
currentState = SYSTEM_INFO;
} else {
tft.println("\nConnection failed!");
tft.println("Please check your WiFi settings.");
delay(2000);
// Retry connection
initWiFi();
}
}
void initChatInterface() {
tft.fillScreen(BACKGROUND_COLOR);
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(2);
tft.setCursor(10, 10);
tft.println("Chat Mode Ready");
}
void handleChatMode() {
// Use XPT2046 touch input
if (ts.touched()) {
TS_Point p = ts.getPoint();
// Convert touch coordinates to screen coordinates
uint16_t x = map(p.x, 0, 4095, 0, tft.width());
uint16_t y = map(p.y, 0, 4095, 0, tft.height());
if (y > 200) {
handleTouchInput(x, y);
}
}
// Display current chat
displayChat();
}
void handleTouchInput(uint16_t x, uint16_t y) {
if (isWaitingForResponse) return;
// Simple keyboard layout
if (y > 200 && y < 240) {
if (x < 80) inputBuffer += "A";
else if (x < 160) inputBuffer += "B";
else if (x < 240) inputBuffer += "C";
else inputBuffer += "D";
if (inputBuffer.length() > 20) {
sendToGemini();
}
}
}
void displayChat() {
tft.fillScreen(BACKGROUND_COLOR);
// Display chat history
tft.setTextColor(TEXT_COLOR);
tft.setTextSize(2);
if (inputBuffer.length() > 0) {
tft.setCursor(10, cursorY);
tft.print("YOU: ");
tft.println(inputBuffer);
cursorY += LINE_HEIGHT;
}
if (responseBuffer.length() > 0) {
tft.setCursor(10, cursorY);
tft.print("OPSIE_32: ");
tft.println(responseBuffer);
cursorY += LINE_HEIGHT;
}
// Display input area
tft.drawRect(0, 200, 320, 40, HIGHLIGHT_COLOR);
tft.setCursor(10, 210);
tft.print("Input: ");
tft.print(inputBuffer);
}
void sendToGemini() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = String(GEMINI_API_URL) + "?key=" + GEMINI_API_KEY;
http.begin(url);
http.addHeader("Content-Type", "application/json");
// Build JSON payload with system prompt and user message
String jsonPayload = "{";
jsonPayload += "\"contents\":[";
jsonPayload += "{\"role\":\"user\",\"parts\":[{\"text\":\"" + String(SYSTEM_PROMPT) + "\"}]},";
jsonPayload += "{\"role\":\"user\",\"parts\":[{\"text\":\"" + inputBuffer + "\"}]}";
jsonPayload += "]}";
int httpResponseCode = http.POST(jsonPayload);
if (httpResponseCode > 0) {
String response = http.getString();
parseGeminiResponse(response);
}
http.end();
}
inputBuffer = "";
isWaitingForResponse = false;
}
void parseGeminiResponse(String response) {
DynamicJsonDocument doc(1024);
deserializeJson(doc, response);
// Extract the response text
responseBuffer = doc["candidates"][0]["content"]["parts"][0]["text"].as<String>();
// Ensure response is short (1-2 sentences)
int firstPeriod = responseBuffer.indexOf('.');
if (firstPeriod != -1) {
int secondPeriod = responseBuffer.indexOf('.', firstPeriod + 1);
if (secondPeriod != -1) {
responseBuffer = responseBuffer.substring(0, secondPeriod + 1);
}
}
}