#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
// Piny TFT
#define TFT_CS 15
#define TFT_DC 2
#define TFT_MOSI 23
#define TFT_CLK 18
#define TFT_RST 4
#define TFT_MISO 19
// Piny przycisków
#define BTN_UP 32
#define BTN_DOWN 33
#define BTN_OK 25
// Piny LED
#define LED_RED 26
#define LED_GREEN 27
#define LED_BLUE 14
// Kolory
#define COLOR_BG 0x0000
#define COLOR_HEADER 0x001F
#define COLOR_SELECTED 0x4208
#define COLOR_FOOTER 0xFD20
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);
struct MenuItem {
const char* name;
uint16_t color;
uint8_t ledPin;
bool state;
};
MenuItem menu[] = {
{"CZERWONA", ILI9341_RED, LED_RED, false},
{"ZIELONA", ILI9341_GREEN, LED_GREEN, false},
{"NIEBIESKA", ILI9341_BLUE, LED_BLUE, false}
};
const int MENU_SIZE = 3;
int selectedItem = 0;
int previousSelectedItem = -1;
// Debounce
bool lastBtnUp = HIGH;
bool lastBtnDown = HIGH;
bool lastBtnOk = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 100;
void setup() {
Serial.begin(115200);
Serial.println("ESP32 TFT Menu - FAST & SIMPLE");
// SPI 40MHz dla Wokwi
tft.begin(40000000);
tft.setRotation(0);
tft.fillScreen(COLOR_BG);
// Przyciski
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_OK, INPUT_PULLUP);
// LED
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_BLUE, LOW);
// Rysuj interfejs
drawHeader();
drawFooter();
drawFullMenu();
Serial.println("Ready!");
}
void loop() {
handleButtons();
delay(5); // Minimalne opóźnienie
}
void drawHeader() {
tft.fillRect(0, 0, 240, 50, COLOR_HEADER);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.setCursor(35, 15);
tft.print("MENU LED");
}
void drawFooter() {
tft.fillRect(0, 290, 240, 30, COLOR_FOOTER);
tft.setTextColor(ILI9341_BLACK);
tft.setTextSize(1);
tft.setCursor(20, 302);
tft.print("UP/DOWN-wybor OK-zmien");
}
void drawFullMenu() {
for (int i = 0; i < MENU_SIZE; i++) {
drawMenuItem(i);
}
}
void drawMenuItem(int index) {
if (index < 0 || index >= MENU_SIZE) return;
int yPos = 60 + (index * 80);
bool isSelected = (index == selectedItem);
uint16_t bgColor = isSelected ? COLOR_SELECTED : COLOR_BG;
// Użyj startWrite dla szybszego rysowania
tft.startWrite();
tft.writeFillRect(5, yPos, 230, 70, bgColor);
tft.endWrite();
// Ramka dla zaznaczonego
if (isSelected) {
tft.drawRect(5, yPos, 230, 70, ILI9341_WHITE);
}
// Nazwa LED
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE, bgColor);
tft.setCursor(15, yPos + 10);
tft.print(menu[index].name);
// Status
tft.setTextColor(menu[index].color, bgColor);
tft.setCursor(15, yPos + 35);
tft.print(menu[index].state ? "[ ON ]" : "[ OFF ]");
// Wskaźnik
uint16_t indicatorColor = menu[index].state ? menu[index].color : ILI9341_DARKGREY;
tft.fillRect(190, yPos + 15, 35, 35, indicatorColor);
tft.drawRect(190, yPos + 15, 35, 35, ILI9341_WHITE);
}
void handleButtons() {
unsigned long currentTime = millis();
bool btnUp = digitalRead(BTN_UP);
bool btnDown = digitalRead(BTN_DOWN);
bool btnOk = digitalRead(BTN_OK);
if ((currentTime - lastDebounceTime) > debounceDelay) {
// Przycisk UP
if (btnUp == LOW && lastBtnUp == HIGH) {
lastDebounceTime = currentTime;
previousSelectedItem = selectedItem;
selectedItem--;
if (selectedItem < 0) selectedItem = MENU_SIZE - 1;
// Odśwież tylko 2 elementy
drawMenuItem(previousSelectedItem);
drawMenuItem(selectedItem);
Serial.print("Wybrano: ");
Serial.println(menu[selectedItem].name);
}
// Przycisk DOWN
if (btnDown == LOW && lastBtnDown == HIGH) {
lastDebounceTime = currentTime;
previousSelectedItem = selectedItem;
selectedItem++;
if (selectedItem >= MENU_SIZE) selectedItem = 0;
// Odśwież tylko 2 elementy
drawMenuItem(previousSelectedItem);
drawMenuItem(selectedItem);
Serial.print("Wybrano: ");
Serial.println(menu[selectedItem].name);
}
// Przycisk OK
if (btnOk == LOW && lastBtnOk == HIGH) {
lastDebounceTime = currentTime;
menu[selectedItem].state = !menu[selectedItem].state;
digitalWrite(menu[selectedItem].ledPin, menu[selectedItem].state ? HIGH : LOW);
// Odśwież tylko 1 element
drawMenuItem(selectedItem);
Serial.print("LED ");
Serial.print(menu[selectedItem].name);
Serial.print(": ");
Serial.println(menu[selectedItem].state ? "ON" : "OFF");
}
}
lastBtnUp = btnUp;
lastBtnDown = btnDown;
lastBtnOk = btnOk;
}