Add Tab5 basic touch example

This commit is contained in:
2026-08-09 20:27:36 +02:00
commit 4d42233993
4 changed files with 193 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
.pio/
.vscode/
+25
View File
@@ -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
```
+17
View File
@@ -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
+148
View File
@@ -0,0 +1,148 @@
#include <Arduino.h>
#include <M5Unified.h>
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<unsigned long>(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);
}