From 4d42233993f8869438ee100e969c592c3fa9c838 Mon Sep 17 00:00:00 2001 From: helios Date: Sun, 9 Aug 2026 20:27:36 +0200 Subject: [PATCH] Add Tab5 basic touch example --- .gitignore | 3 + README.md | 25 +++++++++ platformio.ini | 17 ++++++ src/main.cpp | 148 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f7cf23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pio/ +.vscode/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b588942 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# M5Stack Tab5 Basic Touch + +The first project in a small, beginner-friendly Tab5 basics series. It shows +the minimum useful M5Unified/M5GFX pattern for drawing a screen and reacting +to capacitive touch. + +The screen contains a header, divider line, status message, and two large +touch targets: + +- **Left / Sun:** changes the left panel colour and increments its counter. +- **Right / Moon:** toggles the moon symbol and increments its counter. + +M5Unified and M5GFX are used directly instead of LVGL. That keeps this first +example small enough to read from top to bottom while still using the native +Tab5 display and touch support. LVGL is a better fit for a later, widget-heavy +example rather than the foundational touch example. + +## Build + +```sh +pio run +pio run --target upload +pio device monitor +``` + diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..e741578 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,17 @@ +[env:tab5] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.21-2/platform-espressif32.zip +board = esp32-p4-evboard +framework = arduino +monitor_speed = 115200 +upload_speed = 1500000 +board_build.mcu = esp32p4 +board_build.flash_mode = qio +build_flags = + -DBOARD_HAS_PSRAM + -DCORE_DEBUG_LEVEL=3 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 +lib_deps = + https://github.com/M5Stack/M5Unified.git + https://github.com/M5Stack/M5GFX.git + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..dcc5654 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,148 @@ +#include +#include + +namespace { + +constexpr uint16_t BG = 0x0841; +constexpr uint16_t HEADER = 0x18E3; +constexpr uint16_t LEFT_BLUE = 0x2B8E; +constexpr uint16_t LEFT_GREEN = 0x3D88; +constexpr uint16_t RIGHT_PURPLE = 0x620B; +constexpr uint16_t RIGHT_ORANGE = 0xC3E0; +constexpr uint16_t ACCENT = 0x2E9F; +constexpr uint16_t MUTED = 0xBDF7; + +struct Rect { + int32_t x, y, w, h; + + bool contains(int32_t px, int32_t py) const { + return px >= x && px < x + w && py >= y && py < y + h; + } +}; + +Rect leftPanel; +Rect rightPanel; +uint32_t leftTouches = 0; +uint32_t rightTouches = 0; +bool leftAlternate = false; +bool moonFull = false; + +void drawText(const char* value, int32_t x, int32_t y, textdatum_t datum, + const lgfx::IFont* font, uint16_t color) { + M5.Display.setFont(font); + M5.Display.setTextDatum(datum); + M5.Display.setTextColor(color); // Transparent glyph background. + M5.Display.drawString(value, x, y); +} + +void drawSun(int32_t x, int32_t y, uint16_t color) { + M5.Display.fillCircle(x, y, 42, color); + for (int angle = 0; angle < 360; angle += 45) { + float radians = angle * DEG_TO_RAD; + int32_t x1 = x + cosf(radians) * 58; + int32_t y1 = y + sinf(radians) * 58; + int32_t x2 = x + cosf(radians) * 82; + int32_t y2 = y + sinf(radians) * 82; + M5.Display.drawWideLine(x1, y1, x2, y2, 8, color); + } +} + +void drawMoon(int32_t x, int32_t y, uint16_t panelColor) { + M5.Display.fillCircle(x, y, 72, TFT_WHITE); + if (!moonFull) { + M5.Display.fillCircle(x + 34, y - 20, 68, panelColor); + } +} + +void drawPanel(const Rect& area, bool left) { + uint16_t fill = left ? (leftAlternate ? LEFT_GREEN : LEFT_BLUE) + : (moonFull ? RIGHT_ORANGE : RIGHT_PURPLE); + M5.Display.fillRoundRect(area.x, area.y, area.w, area.h, 26, fill); + M5.Display.drawRoundRect(area.x, area.y, area.w, area.h, 26, TFT_WHITE); + + if (left) { + drawSun(area.x + area.w / 2, area.y + 142, TFT_YELLOW); + } else { + drawMoon(area.x + area.w / 2, area.y + 142, fill); + } + + drawText(left ? "LEFT" : "RIGHT", area.x + area.w / 2, area.y + 270, + middle_center, &fonts::FreeSansBold24pt7b, TFT_WHITE); + drawText(left ? "Tap to change colour" : "Tap to change the moon", + area.x + area.w / 2, area.y + 324, middle_center, + &fonts::FreeSans12pt7b, TFT_WHITE); + + char count[48]; + snprintf(count, sizeof(count), "Touches: %lu", + static_cast(left ? leftTouches : rightTouches)); + drawText(count, area.x + area.w / 2, area.y + 378, middle_center, + &fonts::FreeSansBold12pt7b, TFT_WHITE); +} + +void drawStatus(const char* message) { + M5.Display.fillRect(28, 100, M5.Display.width() - 56, 50, BG); + drawText(message, M5.Display.width() / 2, 125, middle_center, + &fonts::FreeSans12pt7b, MUTED); +} + +void drawScreen() { + int32_t width = M5.Display.width(); + int32_t height = M5.Display.height(); + constexpr int32_t margin = 28; + constexpr int32_t gap = 22; + + leftPanel = {margin, 166, (width - margin * 2 - gap) / 2, height - 194}; + rightPanel = {leftPanel.x + leftPanel.w + gap, leftPanel.y, leftPanel.w, + leftPanel.h}; + + M5.Display.fillScreen(BG); + M5.Display.fillRect(0, 0, width, 86, HEADER); + M5.Display.fillRect(0, 84, width, 3, ACCENT); + drawText("TAB5 BASIC TOUCH", 28, 40, middle_left, + &fonts::FreeSansBold24pt7b, TFT_WHITE); + drawText("M5UNIFIED + M5GFX", width - 28, 40, middle_right, + &fonts::FreeSans12pt7b, MUTED); + drawStatus("Touch either large panel"); + drawPanel(leftPanel, true); + drawPanel(rightPanel, false); +} + +void handleTouch(int32_t x, int32_t y) { + if (leftPanel.contains(x, y)) { + ++leftTouches; + leftAlternate = !leftAlternate; + drawPanel(leftPanel, true); + drawStatus("LEFT touched: colour changed"); + Serial.printf("LEFT touched at %ld,%ld; count=%lu\n", (long)x, (long)y, + (unsigned long)leftTouches); + } else if (rightPanel.contains(x, y)) { + ++rightTouches; + moonFull = !moonFull; + drawPanel(rightPanel, false); + drawStatus("RIGHT touched: moon changed"); + Serial.printf("RIGHT touched at %ld,%ld; count=%lu\n", (long)x, (long)y, + (unsigned long)rightTouches); + } +} + +} // namespace + +void setup() { + Serial.begin(115200); + auto config = M5.config(); + M5.begin(config); + M5.Display.setRotation(1); + M5.Display.setColorDepth(16); + M5.Display.setBrightness(180); + drawScreen(); + Serial.println("M5Stack Tab5 Basic Touch ready"); +} + +void loop() { + M5.update(); + auto touch = M5.Touch.getDetail(); + if (touch.wasReleased()) { + handleTouch(touch.x, touch.y); + } + delay(8); +}