Deska ESP32-3248S035 je cenově dostupné, ale překvapivě výkonné řešení pro tvorbu profesionálních grafických rozhraní.
Srdcem desky je dvoujádrový procesor ESP32 (240 MHz), který má více než dostatek výpočetního výkonu na to, aby plynule poháněl moderní grafickou knihovnu LVGL. Díky tomu můžete vytvářet responzivní tlačítka, grafy, posuvníky a animace, které vypadají mnohem lépe než na starých Arduinech, a to vše v jednom kompaktním modulu bez nutnosti pájení kabelů.
I když tato verze využívá starší typ dotyku (rezistivní/tlakový), s dobře napsaným kódem (viz níže) reaguje spolehlivě a je ideální pro ovládací panely.
1. Instalace podpory pro ESP32
Pokud s ESP32 pracujete poprvé, musíte Arduinu říct, co je to za čip.
- Otevřete Arduino IDE.
- Jděte do Soubor → Vlastnosti.
- Do pole Správce dalších desek URL vložte: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
- Potvrďte OK.
- Jděte do Nástroje → Vývojová deska → Manažer desek.
- Vyhledejte esp32 a nainstalujte balíček od Espressif Systems
2. Instalace grafických knihoven
Pro oživení displeje použijeme dvě knihovny. Jednu pro hardware (driver) a druhou pro grafiku (tlačítka, okna).
Jděte do Nástroje → Správce knihoven (Ctrl+Shift+I) a nainstalujte:
- Driver pro displeje připojené k ESP32.
- Knihovna pro tvorbu uživatelských rozhraní.
3. Vytvoření souboru lv_conf.h
Knihovna LVGL v Arduino IDE vyžaduje existenci konfiguračního souboru na specifickém místě, jinak se program odmítne zkompilovat.
- Otevřete ve svém počítači složku s knihovnami Arduina. Obvykle: Dokumenty/Arduino/libraries/.
- Otevřete složku lvgl.
- Najděte soubor lv_conf_template.h a zkopírujte jej (Ctrl+C).
- Vraťte se o úroveň výš (zpět do Dokumenty/Arduino/libraries/).
- Vložte soubor sem (Ctrl+V), takže bude ležet vedle složky lvgl (ne uvnitř!).
- Přejmenujte tento soubor na lv_conf.h.
Poznámka: Pro konfiguraci knihovny LVGL byste mohli soubor upravit. Pro naše účely jsou ale výchozí nastavení vhodná.
4. Nastavení desky před nahráváním
Aby se kód nahrál správně, vyberte v menu vedle nahrávací šipky desku "ESP32 Dev Module" a správný sériový port. Pokud sériový port nevidíte, provděpodobně si budete muset doinnstalovat driver pro USB-serial převodník CH340, který je na desce.
#include <LovyanGFX.hpp>
#include <lvgl.h>
// --- NASTAVENÍ PINŮ ---
#define PIN_RED 4
#define PIN_GREEN 16
#define PIN_BLUE 17
#define PIN_BL 27 // Podsvícení
// Logika LED: Active Low (HIGH = Vypnuto, LOW = Zapnuto)
#define LED_OFF HIGH
#define LED_ON LOW
// --- OVLADAČ DISPLEJE (LovyanGFX) ---
class LGFX : public lgfx::LGFX_Device {
lgfx::Panel_ST7796 _panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::Touch_XPT2046 _touch_instance;
public:
LGFX(void) {
{
auto cfg = _bus_instance.config();
cfg.spi_host = HSPI_HOST;
cfg.spi_mode = 0;
cfg.freq_write = 40000000;
cfg.freq_read = 16000000;
cfg.pin_sclk = 14;
cfg.pin_mosi = 13;
cfg.pin_miso = 12;
cfg.pin_dc = 2;
cfg.dma_channel = 1;
_bus_instance.config(cfg);
_panel_instance.setBus(&_bus_instance);
}
{
auto cfg = _panel_instance.config();
cfg.pin_cs = 15;
cfg.pin_rst = -1;
cfg.panel_width = 320;
cfg.panel_height = 480;
cfg.invert = false;
cfg.rgb_order = false;
cfg.bus_shared = true;
_panel_instance.config(cfg);
}
{
auto cfg = _touch_instance.config();
// Kalibrace pro Sunton Resistive
cfg.x_min = 200;
cfg.x_max = 3700;
cfg.y_min = 200;
cfg.y_max = 3700;
cfg.bus_shared = true;
cfg.spi_host = HSPI_HOST;
cfg.freq = 2500000;
cfg.pin_sclk = 14;
cfg.pin_mosi = 13;
cfg.pin_miso = 12;
cfg.pin_cs = 33;
cfg.pin_int = -1; // Polling mód (nutné!)
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
setPanel(&_panel_instance);
}
};
LGFX tft;
// --- PAMĚŤ A VYKRESLOVÁNÍ ---
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 480
#define BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10)
static uint16_t draw_buf[BUF_SIZE];
void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
if (tft.getStartCount() == 0) tft.endWrite();
tft.pushImageDMA(area->x1, area->y1, w, h, (uint16_t*)px_map);
lv_display_flush_ready(disp);
}
// --- ČTENÍ DOTYKU ---
void my_touch_read(lv_indev_t *indev, lv_indev_data_t *data) {
uint16_t touchX, touchY;
bool touched = tft.getTouch(&touchX, &touchY);
if (touched) {
data->state = LV_INDEV_STATE_PRESSED;
// 1. Osa X je prohozená -> otočit
data->point.x = SCREEN_WIDTH - touchX;
// 2. Osa Y je v pořádku -> beze změny
data->point.y = touchY;
// Ochrana proti přetečení
if(data->point.x < 0) data->point.x = 0;
if(data->point.x >= SCREEN_WIDTH) data->point.x = SCREEN_WIDTH - 1;
if(data->point.y < 0) data->point.y = 0;
if(data->point.y >= SCREEN_HEIGHT) data->point.y = SCREEN_HEIGHT - 1;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
// --- OBSLUHA TLAČÍTEK ---
static void btn_event_handler(lv_event_t * e) {
int color_id = (int)(long)lv_event_get_user_data(e);
// Zhasnout vše
digitalWrite(PIN_RED, LED_OFF);
digitalWrite(PIN_GREEN, LED_OFF);
digitalWrite(PIN_BLUE, LED_OFF);
// Rozsvítit vybranou
switch(color_id) {
case 1: digitalWrite(PIN_RED, LED_ON); break;
case 2: digitalWrite(PIN_GREEN, LED_ON); break;
case 3: digitalWrite(PIN_BLUE, LED_ON); break;
}
}
void setup() {
Serial.begin(115200);
// 1. INIT PINŮ (Vypnuto při startu)
pinMode(PIN_RED, OUTPUT); digitalWrite(PIN_RED, LED_OFF);
pinMode(PIN_GREEN, OUTPUT); digitalWrite(PIN_GREEN, LED_OFF);
pinMode(PIN_BLUE, OUTPUT); digitalWrite(PIN_BLUE, LED_OFF);
pinMode(PIN_BL, OUTPUT); digitalWrite(PIN_BL, HIGH);
// 2. INIT DISPLEJE
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// --- OPRAVA BAREV ---
// Prohodí bajty barev (opravuje fialovou místo zelené)
tft.setSwapBytes(true);
// 3. INIT LVGL
lv_init();
lv_display_t * disp = lv_display_create(SCREEN_WIDTH, SCREEN_HEIGHT);
lv_display_set_buffers(disp, draw_buf, NULL, BUF_SIZE * 2, LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_flush_cb(disp, my_disp_flush);
lv_indev_t * indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, my_touch_read);
// 4. GUI
lv_obj_t * scr = lv_screen_active();
// ČERVENÁ
lv_obj_t * btn1 = lv_button_create(scr);
lv_obj_set_size(btn1, 150, 100);
lv_obj_align(btn1, LV_ALIGN_TOP_MID, 0, 30);
lv_obj_set_style_bg_color(btn1, lv_palette_main(LV_PALETTE_RED), 0);
lv_obj_add_event_cb(btn1, btn_event_handler, LV_EVENT_CLICKED, (void*)1);
lv_label_set_text(lv_label_create(btn1), "CERVENA");
// ZELENÁ
lv_obj_t * btn2 = lv_button_create(scr);
lv_obj_set_size(btn2, 150, 100);
lv_obj_center(btn2);
lv_obj_set_style_bg_color(btn2, lv_palette_main(LV_PALETTE_GREEN), 0);
lv_obj_add_event_cb(btn2, btn_event_handler, LV_EVENT_CLICKED, (void*)2);
lv_label_set_text(lv_label_create(btn2), "ZELENA");
// MODRÁ
lv_obj_t * btn3 = lv_button_create(scr);
lv_obj_set_size(btn3, 150, 100);
lv_obj_align(btn3, LV_ALIGN_BOTTOM_MID, 0, -30);
lv_obj_set_style_bg_color(btn3, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_obj_add_event_cb(btn3, btn_event_handler, LV_EVENT_CLICKED, (void*)3);
lv_label_set_text(lv_label_create(btn3), "MODRA");
}
void loop() {
lv_timer_handler();
lv_tick_inc(5);
delay(5);
}