From ad62a8f1613949a91f0fa6699114a45ef0f2b163 Mon Sep 17 00:00:00 2001 From: helios Date: Sat, 1 Aug 2026 18:30:50 +0000 Subject: [PATCH] Add compiled Tab5 dashboard source --- src/main.cpp | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 src/main.cpp diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6e5abe5 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,254 @@ +#include +#include +#include + +namespace { + +constexpr uint16_t BG = 0x10A2; +constexpr uint16_t CARD = 0x2145; +constexpr uint16_t BORDER = 0x39E7; +constexpr uint16_t MUTED = 0xAD55; +constexpr uint16_t ACCENT = 0x4D7F; + +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; + } +}; + +struct Button { + Rect area; + const char* label; + uint16_t color; +}; + +constexpr Rect colorPanel{28, 108, 760, 326}; +constexpr Rect infoPanel{812, 108, 440, 506}; +constexpr Rect slider{74, 661, 1132, 22}; + +Button buttons[] = { + {{28, 462, 174, 126}, "RED", TFT_RED}, + {{220, 462, 174, 126}, "GREEN", TFT_GREEN}, + {{412, 462, 174, 126}, "BLUE", TFT_BLUE}, + {{604, 462, 184, 126}, "RESET", 0x7BEF}, +}; + +uint16_t panelColor = ACCENT; +uint32_t pressCount = 0; +uint32_t lastInfoUpdate = 0; +uint8_t brightness = 160; +int32_t lastTouchX = -1; +int32_t lastTouchY = -1; + +void centered(const char* text, const Rect& area, uint16_t color, + float size = 1.0f) { + M5.Display.setTextDatum(middle_center); + M5.Display.setTextColor(color); + M5.Display.setTextSize(size); + M5.Display.drawString(text, area.x + area.w / 2, area.y + area.h / 2); +} + +void drawHeader() { + M5.Display.fillRect(0, 0, M5.Display.width(), 82, CARD); + M5.Display.fillRect(0, 80, M5.Display.width(), 2, ACCENT); + M5.Display.setTextDatum(middle_left); + M5.Display.setTextColor(TFT_WHITE); + M5.Display.setTextSize(1.8f); + M5.Display.drawString("M5Stack Tab5", 28, 40); + M5.Display.setTextDatum(middle_right); + M5.Display.setTextColor(MUTED); + M5.Display.setTextSize(1.0f); + M5.Display.drawString("TOUCH DASHBOARD", M5.Display.width() - 28, 40); +} + +void drawColorPanel() { + M5.Display.fillRoundRect(colorPanel.x, colorPanel.y, colorPanel.w, + colorPanel.h, 22, panelColor); + M5.Display.drawRoundRect(colorPanel.x, colorPanel.y, colorPanel.w, + colorPanel.h, 22, TFT_WHITE); + centered("DISPLAY + TOUCH READY", + {colorPanel.x, colorPanel.y + 78, colorPanel.w, 150}, TFT_WHITE, + 2.0f); + char text[48]; + snprintf(text, sizeof(text), "Button presses: %lu", + static_cast(pressCount)); + centered(text, {colorPanel.x, colorPanel.y + 220, colorPanel.w, 52}, + TFT_WHITE, 1.1f); +} + +void drawButton(const Button& button, bool pressed = false) { + uint16_t fill = pressed ? TFT_WHITE : button.color; + uint16_t text = pressed ? button.color : TFT_WHITE; + M5.Display.fillRoundRect(button.area.x, button.area.y, button.area.w, + button.area.h, 18, fill); + M5.Display.drawRoundRect(button.area.x, button.area.y, button.area.w, + button.area.h, 18, TFT_WHITE); + centered(button.label, button.area, text, 1.35f); +} + +void drawSlider() { + M5.Display.fillRect(0, 630, M5.Display.width(), 90, BG); + M5.Display.setTextDatum(middle_left); + M5.Display.setTextColor(MUTED, BG); + M5.Display.setTextSize(0.9f); + M5.Display.drawString("BRIGHTNESS", 28, slider.y + slider.h / 2); + M5.Display.fillRoundRect(slider.x, slider.y, slider.w, slider.h, + slider.h / 2, BORDER); + int32_t filled = map(brightness, 20, 255, 0, slider.w); + if (filled > 0) { + M5.Display.fillRoundRect(slider.x, slider.y, filled, slider.h, + slider.h / 2, ACCENT); + } + int32_t knobX = slider.x + constrain(filled, 0, slider.w); + M5.Display.fillCircle(knobX, slider.y + slider.h / 2, 18, TFT_WHITE); + M5.Display.drawCircle(knobX, slider.y + slider.h / 2, 18, BORDER); + char value[8]; + snprintf(value, sizeof(value), "%u", brightness); + M5.Display.setTextDatum(middle_right); + M5.Display.setTextColor(TFT_WHITE, BG); + M5.Display.drawString(value, M5.Display.width() - 28, + slider.y + slider.h / 2); +} + +void infoLine(int32_t y, const char* label, const char* value) { + M5.Display.fillRect(infoPanel.x + 18, y, infoPanel.w - 36, 38, CARD); + M5.Display.setTextSize(0.95f); + M5.Display.setTextDatum(middle_left); + M5.Display.setTextColor(MUTED, CARD); + M5.Display.drawString(label, infoPanel.x + 24, y + 19); + M5.Display.setTextDatum(middle_right); + M5.Display.setTextColor(TFT_WHITE, CARD); + M5.Display.drawString(value, infoPanel.x + infoPanel.w - 24, y + 19); +} + +void updateInfo() { + char value[64]; + uint32_t seconds = millis() / 1000; + snprintf(value, sizeof(value), "%02lu:%02lu:%02lu", + static_cast(seconds / 3600), + static_cast((seconds / 60) % 60), + static_cast(seconds % 60)); + infoLine(infoPanel.y + 82, "Uptime", value); + snprintf(value, sizeof(value), "%d x %d", M5.Display.width(), + M5.Display.height()); + infoLine(infoPanel.y + 124, "Display", value); + snprintf(value, sizeof(value), "%ld, %ld", static_cast(lastTouchX), + static_cast(lastTouchY)); + infoLine(infoPanel.y + 166, "Last touch", value); + snprintf(value, sizeof(value), "%.1f MB", + ESP.getFreeHeap() / (1024.0 * 1024.0)); + infoLine(infoPanel.y + 208, "Free heap", value); + snprintf(value, sizeof(value), "%.1f MB", + ESP.getFreePsram() / (1024.0 * 1024.0)); + infoLine(infoPanel.y + 250, "Free PSRAM", value); + int32_t mv = M5.Power.getBatteryVoltage(); + int32_t ma = M5.Power.getBatteryCurrent(); + if (mv > 0) { + snprintf(value, sizeof(value), "%.2f V / %ld mA", mv / 1000.0, + static_cast(ma)); + } else { + snprintf(value, sizeof(value), "not detected"); + } + infoLine(infoPanel.y + 292, "Battery", value); + snprintf(value, sizeof(value), "%s", + WiFi.status() == WL_CONNECTED ? "connected" : "not connected"); + infoLine(infoPanel.y + 334, "Wi-Fi", value); + float ax = 0, ay = 0, az = 0; + M5.Imu.update(); + M5.Imu.getAccelData(&ax, &ay, &az); + snprintf(value, sizeof(value), "%+.2f %+.2f %+.2f", ax, ay, az); + infoLine(infoPanel.y + 376, "Accel XYZ", value); +} + +void drawInfoPanel() { + M5.Display.fillRoundRect(infoPanel.x, infoPanel.y, infoPanel.w, + infoPanel.h, 22, CARD); + M5.Display.drawRoundRect(infoPanel.x, infoPanel.y, infoPanel.w, + infoPanel.h, 22, BORDER); + M5.Display.setTextDatum(top_left); + M5.Display.setTextColor(TFT_WHITE); + M5.Display.setTextSize(1.25f); + M5.Display.drawString("LIVE DEVICE INFO", infoPanel.x + 24, + infoPanel.y + 22); + M5.Display.drawFastHLine(infoPanel.x + 24, infoPanel.y + 64, + infoPanel.w - 48, BORDER); + updateInfo(); +} + +void setBrightness(int32_t x) { + x = constrain(x, slider.x, slider.x + slider.w); + brightness = static_cast( + map(x, slider.x, slider.x + slider.w, 20, 255)); + M5.Display.setBrightness(brightness); + drawSlider(); +} + +void activateButton(size_t index) { + drawButton(buttons[index], true); + delay(55); + if (index < 3) { + panelColor = buttons[index].color; + ++pressCount; + } else { + panelColor = ACCENT; + pressCount = 0; + } + drawColorPanel(); + drawButton(buttons[index]); + Serial.printf("Button %s pressed; count=%lu\n", buttons[index].label, + static_cast(pressCount)); +} + +void drawScreen() { + M5.Display.startWrite(); + M5.Display.fillScreen(BG); + drawHeader(); + drawColorPanel(); + for (const auto& button : buttons) drawButton(button); + drawInfoPanel(); + drawSlider(); + M5.Display.endWrite(); +} + +} // namespace + +void setup() { + Serial.begin(115200); + auto config = M5.config(); + M5.begin(config); + M5.Display.setRotation(1); + M5.Display.setColorDepth(16); + M5.Display.setBrightness(brightness); + drawScreen(); + Serial.println("M5Stack Tab5 Touch Dashboard ready"); +} + +void loop() { + M5.update(); + auto touch = M5.Touch.getDetail(); + if (touch.isPressed()) { + lastTouchX = touch.x; + lastTouchY = touch.y; + Rect sliderHit{slider.x - 20, slider.y - 28, slider.w + 40, + slider.h + 56}; + if (sliderHit.contains(touch.x, touch.y)) { + setBrightness(touch.x); + } else if (colorPanel.contains(touch.x, touch.y)) { + M5.Display.drawCircle(touch.x, touch.y, 16, TFT_WHITE); + } + } + if (touch.wasReleased()) { + for (size_t i = 0; i < sizeof(buttons) / sizeof(buttons[0]); ++i) { + if (buttons[i].area.contains(touch.x, touch.y)) { + activateButton(i); + break; + } + } + } + if (millis() - lastInfoUpdate >= 500) { + lastInfoUpdate = millis(); + updateInfo(); + } + delay(8); +}