17 Oct 2025

Getting MicroPython Working on the ESP32-C6-LCD-1.47

Run micropython on ESP32-C6-LCD-1.47 was not easy, but it worked in the end.

I recently got my hands on an ESP32-C6 board and was immediately drawn to its compact form factor. In particular, the Waveshare ESP32-C6-LCD-1.47 model comes with a 1.47” display, Wi‑Fi, and a few other handy features. You can check out the full specs on the Waveshare Wiki.

My initial goal is to use this board as the core of a MIDI controller. Getting it working, however, wasn’t straightforward. After some trial and error, I managed to flash MicroPython, configure the display, and run a simple demo app that shows random quotes. The MIDI controller is still a work in progress, but here’s how I got the board up and running.

Finding the Right Display Library

After trying a few approaches, the repo that finally worked for me was:

  • https://github.com/russhughes/st7789py_mpy

The examples I had at first didn’t work out of the box, but the code from this repo did the trick — with a few caveats. I had to tweak some helper scripts to run properly on macOS, as they assumed Linux-specific commands. The changes were minor, though.

Here’s the full process I followed.

Required Toolchain

First, install the required tools:

pipx install mpremote esptool mpy-cross

Flashing MicroPython

Download the latest ESP32-C6 firmware from micropython.org, then flash it to the board:

# Erase the flash memory
PORT=/dev/cu.usbmodemXXXX  # Replace with your actual port
esptool.py --port $PORT --baud 921600 erase_flash

# Flash the firmware
esptool.py --port $PORT --baud 921600 write_flash -z 0x0 firmware-esp32-generic-c6.bin

After flashing, reset the board.

The Random Quotes App

The sample app displays a random quote every 10 seconds on the screen.

boot.py

import esp

try:
    esp.osdebug(None)
except Exception:
    pass

main.py

import time
import random

import st7789py as st7789
import tft_config
import vga1_8x8 as font

tft = tft_config.config(tft_config.WIDE)

QUOTES = [
    "Cafe listo, bugs listos.",
    "Compilo, luego existo.",
    "Mi otro microcontrolador es un 6502.",
    "Funciona en mi placa™.",
    "Wi-Fi es mi superpoder.",
    "El LED parpadea, luego hay vida.",
    "RTFM: Realmente Tremendo Firmware Manual.",
    "El bug era un feature... hasta que no.",
    "GPIO: Grandes Ideas, Pocas Opciones.",
    "Dormir? Prefiero flashear.",
]


def center(text, fg=st7789.WHITE, bg=st7789.BLACK):
    """Centers the given text on the display."""
    length = len(text)
    tft.text(
        font,
        text,
        tft.width // 2 - length // 2 * font.WIDTH,
        tft.height // 2 - font.HEIGHT,
        fg,
        bg,
    )


def main():
    """The big show!"""
    while True:
        for color in [
            st7789.BLACK,
            st7789.color565(100, 100, 100),
            st7789.color565(0, 0, 100),
        ]:
            tft.fill(color)
            tft.rect(0, 0, tft.width, tft.height, st7789.WHITE)
            center(QUOTES[random.randint(0, len(QUOTES) - 1)], st7789.WHITE, color)
            time.sleep(10)


main()

Custom Display Configuration

The tricky part was getting the display configuration right. None of the tft_config.py files included with the library worked for the Waveshare board, so I created my own:

tft_config.py

"""Waveshare ESP32-C6-LCD-1.47

https://www.waveshare.com/wiki/ESP32-C6-LCD-1.47

172x320 ST7789 display with ESP32-C6 
Note: Using 240x320 mode with offset for 172x320 display
"""

from machine import Pin, SPI
import st7789py as st7789

TFA = 0
BFA = 0  
WIDE = 1
TALL = 0
SCROLL = 0      # orientation for scroll.py
FEATHERS = 1    # orientation for feathers.py

def config(rotation=0):
    """
    Configures and returns an instance of the ST7789 display driver.

    Args:
        rotation (int): The rotation of the display (default: 0).

    Returns:
        ST7789: An instance of the ST7789 display driver.
    """

    return st7789.ST7789(
        SPI(1, baudrate=80000000, sck=Pin(7), mosi=Pin(6), miso=Pin(5)),
        240,
        320,
        reset=Pin(21, Pin.OUT),
        cs=Pin(14, Pin.OUT),
        dc=Pin(15, Pin.OUT),
        backlight=Pin(22, Pin.OUT),
        rotation=rotation)

Deployment Script

Finally, here’s the script I used to deploy everything to the board. This was the “magic trick” that made the process smooth. I adapted it from upload_all.sh in the example folder of the st7789py_mpy repo and added font compilation.

deploy.sh

#!/usr/bin/env bash
set -euo pipefail

PORT="${PORT:-/dev/cu.usbmodem83101}"


if ! command -v mpremote >/dev/null 2>&1; then
    echo "This script requires the mpremote command. You can install it with:"
    echo "pip3 install --user mpremote"
    exit 1
fi

if ! command -v mpy-cross >/dev/null 2>&1; then
    echo "This script requires the mpy-cross command. You can install it with:"
    echo "pip3 install --user mpy-cross"
    exit 1
fi

upload_fonts () {
    cd romfonts
    rm -f *.mpy
    for font in *.py
    do
        mpy-cross $font
    done
    cd ..
    mpremote cp romfonts/*.mpy :
}

mpremote connect "$PORT" mip install "github:russhughes/st7789py_mpy/lib/st7789py.py" || true

upload_fonts
mpremote connect "$PORT" fs cp tft_config.py :
mpremote connect "$PORT" fs cp boot.py :
mpremote connect "$PORT" fs cp main.py :

echo "==> Resetting board"
mpremote connect "$PORT" reset
echo "Done."

Wrapping Up

Once I figured out the display configuration, everything else fell into place. The ESP32-C6 is a surprisingly capable board, and with MicroPython it’s easy to iterate quickly. The random quotes app is just a fun example, but the same setup will serve as the foundation for my upcoming MIDI controller project.