Skip to content

Repository files navigation

M5Stack PaperColor (C151) — ESPHome wall panel

A complete ESPHome firmware for the M5Stack PaperColor (SKU C151) — the 4″ Spectra-6 colour e-ink device — turning it into a battery-capable Home Assistant wall panel.

ESPHome doesn't support this device out of the box, so this repo provides the two missing pieces:

  1. A custom ESPHome external component (m5pm1_power) for the on-board M5PM1 PMIC — without it the panel can't power its own e-ink rail, won't charge, and freezes the moment USB is unplugged. The component handles EPD-rail power-up, battery charging + power-hold (survives USB-unplug), battery voltage (median-filtered), USB/charge detection, and the power button (single press → "Off" screen → PMIC shutdown).
  2. A working epaper_spi display config for the Spectra-6 panel (mainline ESPHome's Spectra-E6 model — no fork needed) plus a practical server-rendered dashboard pattern.

Reverse-engineered from M5Stack's open-source M5GFX / M5PM1 register maps. Not affiliated with or endorsed by M5Stack. Use at your own risk — it drives a PMIC over I²C.

🤖 Built with AI assistance. This project was developed with Anthropic's Claude Code on real hardware — the PMIC reverse-engineering, the firmware/component, and the docs were AI-drafted and then verified on the device. It works on my unit, but treat it as a starting point and review the code before flashing your own.

M5Stack PaperColor running this firmware on a 3D-printed stand, showing the live overview page

The panel on a 3D-printed stand (printable .3mf included), showing the live overview page — note the on-device battery indicator top-right.

Screenshots

More example carousel pages (mock data) — the buttons page through them:

Overview page (solar, battery, tank) Climate page (room temperatures) Weather page (current + forecast)

Features

  • Server-rendered dashboard — a Lovelace view is rendered to a 6-colour PNG by a renderer (e.g. the Puppet add-on) and blitted to the panel. Content is decoupled from the device: edit the dashboard in HA, no reflash.
  • 5-page carousel with the 3 user buttons: refresh / previous / next (wraps).
  • Adaptive power (PMIC full-shutdown on battery) — always-on while on USB; on battery it fully shuts down via the M5PM1 PMIC between refreshes (the e-ink image persists at zero power) and self-powers-on via the PMIC's own timer every 15 min, for ~1.5–2 weeks/charge — ~8× an ESP32 deep-sleep, which on this board leaks ~mA through always-on rails. (Measured on hardware: ~2.15 mV/h vs ~17 mV/h for deep-sleep.) On-demand wake is the power button #4 (the chip is fully off between cycles, so no ESP32-GPIO button can wake it). USB plug/unplug switches modes automatically. While shut down on battery the device shows offline in HA between wakes — normal, not a fault.
  • On-device battery indicator (top-right) with a charging bolt when on USB.
  • Status-LED feedback (the 2 on-board RGB LEDs): 🟢 refresh · 🟡 prev · 🟣 next · 🔵 booting · 🔴 shutting down.
  • Power on/off via the dedicated power button, with an "Off" screen drawn before shutdown (e-ink keeps its last image at zero power, so it must say "off").
  • HA-adopted over the ESPHome API: battery %, WiFi, uptime, button events, a Refresh button.

Quick start

You need: an M5Stack PaperColor (C151), a Home Assistant instance, and a Lovelace→PNG renderer (the Puppet add-on is easiest).

  1. Flash the firmware. With mainline ESPHome (or the HA ESPHome add-on), create secrets.yaml, then compile + upload (USB or OTA) — see Building + Secrets.
  2. Make the dashboard. Install the renderer, create a Lovelace dashboard whose URL path matches puppet_base (e.g. eink-m5paper) using eink_m5paper.example.json as a starting point, then set puppet_base in m5papercolor.yaml to point at it — see Dashboard. (Optional: add weather_forecast.example.yaml for the weather page.)
  3. (Optional) Adopt in Home Assistant. Settings → Devices & Services → ESPHome → add the device's IP + the API key, to get battery %, button events, and a Refresh button as HA entities.

The panel then pulls the rendered dashboard every few minutes, and the buttons page through it (refresh / prev / next). Mind the Caveats — a full e-ink refresh takes ~15–20 s.

Hardware

Device: M5Stack PaperColor (C151) — official product page · docs (specs, schematics, photos of the unit). Stand: a 3D-printable stand (the one in the photo) is in hardware/papercolor-stand.3mf (Bambu Studio project).

  • MCU: ESP32-S3R8 (16 MB flash / 8 MB OPI-PSRAM)
  • Display: 4″ Spectra-6 (6-ink) e-paper, 400×600 (600×400 landscape via rotation: 90), full-refresh only (~15–20 s, flashing), no partial refresh, no touch
  • PMIC: M5PM1 (I²C 0x6E, bus SDA=GPIO3 / SCL=GPIO2)
  • EPD pins: CS 44 · DC 43 · RST 12 · BUSY 11 (inverted) · CLK 15 · MOSI 13
  • Buttons: 3 user buttons on GPIO 10 / 9 / 1 (active-low) + a PMIC power button
  • RGB LEDs: 2× WS2812-style on GPIO 21

M5PM1 PMIC notes (the useful bits)

  • Device id 0x2050 at reg 0x00. EPD power = PMIC-GPIO0, SD power = PMIC-GPIO3 (set via I²C).
  • Charging + rails: PWR_CFG (0x06) bit0 CHG, bit1 DCDC, bit2 LDO — auto-clear on reset, so the firmware re-sets them each boot.
  • Power-hold (survive USB-unplug): HOLD_CFG (0x07) bit5 LDO + bit0 G0 + bit3 G3.
  • Battery voltage: VBAT regs 0x22/0x23 are a full 16-bit mV value (do not mask the high byte). VIN 0x24/0x25 ≈ 5 V on USB → charge detection.
  • Power button: reported via BTN_STATUS (0x48) bit7 (pressed-flag) — not the IRQ3 (0x42) click events. Shutdown = SYS_CMD (0x0C) = 0xA1.

Building

Builds with mainline ESPHome ≥ 2026.5.2 (when the epaper_spi Spectra-E6 model landed) — no fork needed. The easiest path is the Home Assistant ESPHome add-on: add a device, paste m5papercolor.yaml, drop the components/m5pm1_power/ folder next to it (or point external_components at github://PFalko/m5stack-papercolor-esphome), add your secrets.yaml, and Install.

Or from a CLI:

pip install "esphome>=2026.5.2"        # or: uv tool install esphome
# create secrets.yaml first (see Secrets below) — compile fails without it
esphome compile m5papercolor.yaml
esphome upload  m5papercolor.yaml --device <device-ip>   # OTA; USB COMx also works

Notes:

  • Framework = Arduino (esp-idf failed to build in an isolated/uv env here).
  • If a CMake build dies with a managed_components permission error, delete the build's managed_components/ + dependencies.lock and recompile.
  • Provide secrets.yaml (see Secrets).

Dashboard

Point puppet_base (in substitutions:) at your renderer + dashboard slug — the slug must match the Lovelace dashboard URL path you create (e.g. eink-m5paper, and 10000 is the Puppet add-on's default port). The firmware fetches, for each carousel page (panel/energy/climate/garden/weather):

<puppet_base>/<page>?viewport=600x400&format=png&wait=4000&zoom=1.6&colors=<6 inks>

The 6 colors are the panel's native inks so the renderer dithers server-side to exactly what the panel can show. (wait=4000 gives the dashboard time to render before the screenshot.)

  • eink_m5paper.example.json — a starter 5-view dashboard (replace the placeholder entities).
  • weather_forecast.example.yaml — optional HA package: a trigger-based template sensor that calls weather.get_forecasts and exposes a daily attribute (the weather entity has no forecast attribute, and a markdown card can't call services), so the weather page can show a big forecast.

Caveats

  • ~15–20 s per refresh (Spectra-6 is full-refresh only). A button press during the e-ink write is dropped (buttons are polled) — press only when the status LED is off.
  • The custom PMIC writes are device-specific; wrong register writes can wedge the device (recover with a full power-cycle: unplug, hold power ~10 s, replug).
  • Verify the physical↔GPIO button map on your unit — the firmware assumes #1 top = GPIO1, #2 left-top = GPIO10, #3 right-top = GPIO9; if your silk-screen differs, swap the GPIOs in the binary_sensor: block. (Button #1's "refresh" re-fetches the current page.)

Secrets

Create secrets.yaml next to the config with:

wifi_ssid: "YourSSID"
wifi_password: "YourWiFiPassword"
api_key: "base64-32-byte-noise-psk"   # e.g. `openssl rand -base64 32`
ota_password: "YourOtaPassword"

Credits

  • Spectra-6 epaper_spi support: mainline ESPHome's Spectra-E6 model; the PaperColor-specific 4.0in-Spectra-E6 preset comes from kepstin's PR #16031.
  • M5PM1 / EPD register maps: M5Stack's open-source M5GFX and M5PM1 libraries.
  • Server-rendered dashboard: the Puppet add-on by balloob.

License

MIT — see LICENSE.

About

ESPHome firmware for the M5Stack PaperColor (C151) Spectra-6 e-ink Home Assistant wall panel — custom M5PM1 PMIC component, battery/charging, power button, 5-page carousel.

Topics

Resources

Stars

25 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages