Skip to content
This repository was archived by the owner on Oct 28, 2026. It is now read-only.
brejcePublic archive

About

Lightweight GUI framework for M5Paper e-ink display (ESP32-S3)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

M5Paper UI — Lightweight GUI Framework for M5Paper E-Ink Display

License: MIT Platform: ESP32-S3 Arduino

A lightweight, DOM-inspired UI framework for the M5Paper S3 e-ink display. Build touch-enabled interfaces with a familiar component tree model — no LVGL, no heavy dependencies.

中文文档: README_CN.md | 组件文档 | C++入门


Why?

M5Paper's 4.7" e-ink display (540×960, 16-level grayscale) needs careful refresh management. This framework handles:

  • Smart rendering — only redraws dirty regions, config-then-draw pattern
  • EPD modes — per-component refresh quality (QUALITY / TEXT / FAST / FASTEST)
  • Touch handling — click, long-press, drag, swipe, double-click detection
  • Theme system — light/dark themes with per-component color caching
  • Task scheduler — non-blocking periodic tasks with lifecycle management
  • Power saving — auto-sleep after 5 seconds of idle

Quick Start

#include <M5Unified.h>
#include "core.h"
#include "components.h"

class MyApp : public Node {
public:
    MyApp() : Node(0, 0, 540, 960, "MyApp") {
        add(new Rect(0, 0, 540, 960));           // background
        add(new Label(20, 40, "Hello, World!"));  // text

        auto* btn = new Button(20, 145, 140, 50, "Click Me");
        btn->onClick([this]() {
            Serial.println("Clicked!");
        });
        add(btn);

        add(new BatteryIcon());                   // battery indicator
    }
};

Framework app;

void setup() {
    auto cfg = M5.config();
    cfg.clear_display = false;
    M5.begin(cfg);
    delay(500);

    app.begin();
    app.addPage(new MyApp());
    app.switchTo(app.getPage("MyApp"));
}

void loop() {
    M5.update();
    app.update();
    delay(1);
}

Project Structure

├── Core.h / Core.cpp        # Framework core: Node tree, Theme, Touch, TaskPool
├── components.h / components.cpp  # Built-in widgets
├── sketch_jun4a.ino         # Full demo app (counter + theme + task pool)
├── examples/                # Minimal examples
│   ├── basic/               #   "Hello World" on e-ink
│   ├── counter/             #   Counter with buttons (no WiFi)
│   ├── refresh_test/        #   EPD mode comparison
│   ├── sd_autocheck/        #   SD card auto-detect
│   └── startwrite_test/     #   Batch drawing performance
├── fonts/                   # TrueType fonts converted to .vlw
├── docs/                    # Detailed documentation (Chinese)
└── LICENSE

Core Concepts

Component Tree (like a DOM)

Every UI element is a Node. Pages are root nodes, widgets are children:

Page (Node)
├── Rect (background)
├── Label (title)
├── Button (click me)
├── ProgressBar
└── BatteryIcon

Lifecycle: config() → draw()

// ✅ config() computes everything once
void config() override {
    _r = h / 2;                     // cache corner radius
    _tx = (w - _tw) / 2;           // cache text x offset
}

// ✅ draw() only uses cached values — zero compute
void draw() override {
    M5.Display.fillRoundRect(x, y, w, h, _r, color);
    M5.Display.drawString(_text.c_str(), _tx, _ty);
}

Never compute in draw() — it's called on every frame. Do all measurement and color resolution in config().

Touch Events

The framework translates M5GFX raw touch data into structured events:

Event Trigger Use Case
TOUCH_CLICK Short press release (<300ms) Button clicks
TOUCH_LONG_CLICK Long press release (≥500ms) Context menus
TOUCH_LONG_HOLD Long press active (every frame) Repeat actions
TOUCH_DRAG Press + move Sliders, scroll
TOUCH_SWIPE Fast flick Page switching
TOUCH_DOUBLE_CLICK Rapid double-tap Special actions

Set touch_mask in your component's constructor to declare which events it handles.

EPD Refresh Modes

Each component declares its refresh quality:

Mode Time Quality Use For
EPD_QUALITY ~450ms Best, no ghosting Final renders
EPD_TEXT ~450ms Sharp text Labels, text
EPD_FAST ~260ms Minor ghosting Buttons, UI (default)
EPD_FASTEST ~120ms Washed out Animations

TaskPool

Non-blocking periodic tasks that follow page lifecycle:

// In your page constructor:
addTask("my_task", [this]() {
    doSomething();
}, 1000);  // runs every 1000ms

// Tasks are auto-registered on show(), auto-removed on hide()/del()

Built-in Components

Component Description
Rect Solid / outlined rectangle (theme-aware background)
Label Text with optional background fill
Button Rounded button with click/long-press callbacks
ProgressBar Horizontal progress bar (0–100)
ListView Scrollable list with arrow buttons + scrollbar
BatteryIcon Battery level + charging indicator (auto-refresh)
Image JPG/PNG/BMP from SD card with fit modes
GridSystem Grid coordinate calculator (not a Node)

Creating a Custom Component

class MyWidget : public Node {
public:
    MyWidget(int32_t x, int32_t y, int32_t w, int32_t h)
        : Node(x, y, w, h, "MyWidget")
    {
        touch_mask = TOUCH_CLICK;  // respond to clicks
        epd_mode = EPD_FAST;       // fast refresh
        config();                  // compute initial layout
    }

    void config() override {
        // Cache colors, measurements from theme
        _cached_color = theme().text_color;
    }

    void draw() override {
        // Draw using cached values
        M5.Display.fillRect(x, y, w, h, _cached_color);
    }

    void onTouch(const TouchEvent& ev) override {
        if (ev.mode == TOUCH_CLICK) {
            // Handle click
        }
    }

private:
    uint32_t _cached_color;
};

Hardware Requirements

  • M5Paper S3 (ESP32-S3, IT8951 EPD controller)
  • M5Unified library (v0.1.x+)
  • M5GFX library (included with M5Unified)

The framework uses 16-level grayscale colors (_GRAY[0..15]) matching M5Paper's display capabilities.

Getting Started

  1. Install Arduino IDE with ESP32-S3 board support
  2. Install M5Unified via Library Manager
  3. Clone this repo into your Arduino sketches folder:
    cd ~/Arduino
    git clone https://github.com/brejce/m5paper-ui.git
  4. Copy font file to SD card: fonts/HarmonyOS_Sans_SC_Regular_28.vlw
  5. Open sketch_jun4a/sketch_jun4a.ino in Arduino IDE
  6. Upload to M5Paper

For a minimal first test, start with examples/basic/basic.ino.

License

MIT — use freely in personal and commercial projects.

Contributing

Pull requests welcome! For major changes, please open an issue first to discuss.


Built with ☕ by brejce

About

Lightweight GUI framework for M5Paper e-ink display (ESP32-S3)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages