High-level, ergonomic wrapper around an ILI9488 480×320 TFT driver with:
- Simple
Displayclass - Drawing primitives (lines, rectangles, circles, polygons, etc.)
- Text (built-in 8×8 or XglcdFont)
- Images & sprites
- Rotation, scrolling, sleep/power control
- Optional debug logging
Full API reference & advanced docs live in the dedicated documentation (see Documentation below).
- screenILI · ILI9488 Display Helper for MicroPython & CircuitPython
- High-level
Displaywrapper (screenILI.Display) - Works with ILI9488 480×320 TFT displays (like: This one on Aliexpress)
- Supports MicroPython and CircuitPython
- Automatic or custom SPI configuration
- Rotations:
landscape,portrait,reverse_landscape,reverse_portrait - Drawing primitives:
- Pixels, lines, polylines
- Rectangles (outline & filled)
- Circles & ellipses (outline & filled)
- Regular polygons (outline & filled)
- Text rendering:
- Built-in 8×8 MicroPython font (with rotation)
- XglcdFont bitmap fonts (portrait & landscape)
- Images & sprites:
- Full-screen or partial raw RGB565 images
- RAM sprites & buffer blitting
- Scrolling:
- Vertical scrolling and scroll regions
- Power control:
- Display on/off, sleep mode
- Optional debug logging with multiple verbosity levels
- Display: ILI9488-based TFT (e.g. 480×320)
- MCU: Board running:
- MicroPython (preferred), or
- CircuitPython (driver supports both)
- Interface: SPI
- Pins:
SCK,MOSI, (optionalMISO)CS– Chip SelectDC– Data/CommandRST– Reset
- Copy the following files to your board:
screenILI.pyili9488.pyxglcd_font.py- (Optional) any X-GLCD
.cfont files and/or image/sprite binary files you use
- Make sure they are on the Python path (typically the board root or
/lib).
from machine import Pin
from screenILI import Display, color565
# Example pins – adjust to your board and wiring
SCK_PIN = 10
MOSI_PIN = 11
MISO_PIN = None # or an actual pin if available
CS_PIN = 13
DC_PIN = 14
RST_PIN = 15
display = Display(
spi_id=0,
baudrate=60_000_000,
sck=SCK_PIN,
mosi=MOSI_PIN,
miso=MISO_PIN,
cs=CS_PIN,
dc=DC_PIN,
rst=RST_PIN,
rotation="landscape", # or "portrait", "reverse_landscape", "reverse_portrait"
debug_level="warn", # "debug", "info", "warn", "error", "none"
)
# Clear screen to black
display.clear()
# Fill with a color
display.fill(color565(30, 144, 255)) # Dodger bluefrom screenILI import color565
# Pixel
display.pixel(10, 10, color565(255, 255, 0))
# Lines
display.line(0, 0, display.width - 1, display.height - 1, color565(255, 0, 0))
display.hline(0, 50, 100, color565(0, 255, 0))
display.vline(50, 0, 100, color565(0, 0, 255))
# Rectangle
display.rect(20, 20, 100, 60, color565(255, 255, 255))
display.fill_rect(25, 25, 90, 50, color565(128, 0, 128)) # purple
# Circle
display.circle(display.width // 2, display.height // 2, 40, color565(255, 255, 255))
display.fill_circle(60, 60, 25, color565(255, 165, 0)) # orange
# Polygon (e.g. hexagon)
display.polygon(6, 200, 120, 40, color565(0, 255, 255), rotate=30)
display.fill_polygon(3, 260, 180, 30, color565(255, 0, 255)) # triangleYou can also access ellipse helpers directly via the underlying driver:
display.draw_ellipse(100, 200, 40, 20, color565(0, 255, 0))
display.fill_ellipse(200, 220, 30, 15, color565(0, 0, 255))from screenILI import color565
display.clear()
display.text8x8(10, 10, "Hello", color565(255, 255, 255))
display.text8x8(10, 30, "World!", color565(0, 255, 0), background=color565(0, 0, 0))
# Rotated text (90, 180, 270)
display.text8x8(100, 100, "ROT 90", color565(255, 0, 0), rotate=90)from screenILI import color565
from xglcd_font import XglcdFont
display.clear()
# X-GLCD C font exported from MikroElektronika GLCD Font Creator
font = XglcdFont(
"/assets/fonts/ArcadePix9x11.c",
width=9,
height=11,
start_letter=32,
letter_count=96,
)
display.text(
x=10,
y=50,
text="Custom font!",
font=font,
color=color565(255, 255, 0),
background=color565(0, 0, 0),
landscape=False,
spacing=1,
)ili9488.Display.draw_image expects a raw RGB565 binary file of size width × height × 2 bytes.
from screenILI import color565
display.clear()
# Full-screen (defaults to display size if width/height are omitted)
display.image("/images/bg.raw")
# Partial
display.image("/images/logo.raw", x=50, y=50, width=120, height=80)# Load a small sprite (raw RGB565) from disk via low-level helper
sprite_buf = display.load_sprite("/sprites/player.raw", w=32, h=32)
# Draw at (x, y)
display.sprite(sprite_buf, x=100, y=150, w=32, h=32)
# Move / animate by redrawing at new coordinates
display.sprite(sprite_buf, x=120, y=150, w=32, h=32)
# Or use the alias
display.blit_buffer(sprite_buf, x=140, y=150, w=32, h=32)This is a high-level view of the most commonly used methods. See full documentation for complete details and low-level commands.
| Method / attribute | Description |
|---|---|
Display(...) |
Create high-level display instance |
display.width, display.height |
Current logical width/height (depends on rotation) |
display.rotation |
Rotation name ("landscape", etc.) |
display.rotation_degrees |
Rotation in degrees (0, 90, 180, 270) |
| Method | Description |
|---|---|
pixel(x, y, color) |
Draw a single pixel |
line(x1, y1, x2, y2, color) |
Line between two points |
hline(x, y, w, color) |
Horizontal line |
vline(x, y, h, color) |
Vertical line |
rect(x, y, w, h, color) |
Rectangle outline |
fill_rect(x, y, w, h, color) |
Filled rectangle |
circle(x, y, r, color) |
Circle outline |
fill_circle(x, y, r, color) |
Filled circle |
polygon(sides, x0, y0, r, color) |
Regular polygon outline |
fill_polygon(sides, x0, y0, r, color) |
Filled regular polygon |
draw_ellipse(...) |
Ellipse outline (low-level) |
fill_ellipse(...) |
Filled ellipse (low-level) |
| Method | Description |
|---|---|
text(x, y, text, font=None, color, background, ...) |
High-level text (built-in or XglcdFont) |
text8x8(x, y, text, color, background=0, rotate=0) |
Built-in 8×8 font with rotation |
draw_text(...), draw_letter(...) |
Low-level XglcdFont helpers |
| Method | Description |
|---|---|
image(path, x=0, y=0, width=None, height=None) |
Draw raw RGB565 image from storage |
sprite(buf, x, y, w, h) |
Draw sprite from buffer |
blit_buffer(buf, x, y, w, h) |
Alias of sprite(...) |
load_sprite(path, w, h) |
Load raw sprite from file |
| Method | Description |
|---|---|
fill(color=0x0000) |
Fill entire screen with color |
clear(color=0x0000) |
Clear screen |
set_rotation(rotation) |
Change rotation |
scroll(y) |
Vertical scroll |
set_scroll(top, bottom) |
Configure scroll region |
| Method | Description |
|---|---|
on() / off() |
Display on / off |
sleep(enable=True) |
Enter/exit sleep mode |
cleanup(clear=True, turn_off=True, ...) |
Clear screen, turn off, deinit SPI |
self_test(delay_ms=600) |
Built-in self-test pattern |
| Method | Description |
|---|---|
set_debug_level(level) |
"debug", "info", "warn", "error", "none" |
get_debug_level() |
Get current debug level name |
The high-level Display exposes a friendly rotation API:
display.set_rotation("landscape") # 0°
display.set_rotation("portrait") # 90°
display.set_rotation("reverse_landscape") # 180°
display.set_rotation("reverse_portrait") # 270°display.width/display.heightare automatically updated.- All primitives (
pixel,line,rect,text, etc.) use the rotated coordinate system.
You can also pass degrees directly:
display.set_rotation(90)The low-level driver supports vertical scrolling and scroll regions:
# Simple vertical scroll
display.scroll(50) # scroll content by 50 pixels
# Define scrollable area (with fixed top/bottom)
top_margin = 20
bottom_margin = 20
display.set_scroll(top_margin, bottom_margin)This makes it possible to:
- Keep a header area static
- Scroll the central area
- Keep a footer/status bar fixed
# Turn display off/on
display.off()
display.on()
# Enter sleep mode
display.sleep(True)
# Exit sleep mode
display.sleep(False)
# Cleanup resources when done
display.cleanup(
clear=True, # optional: clear screen
turn_off=True, # send DISPLAY_OFF
deinit_spi=True, # deinit SPI if owned by this instance
)The high-level Display also calls cleanup() in its destructor (__del__), with clear=False by default.
The wrapper has a simple built-in logger:
display.set_debug_level("debug") # "debug", "info", "warn", "error", "none"
print(display.get_debug_level()) # e.g. "debug""debug": very verbose, logs most drawing operations."info": high-level operations (rectangles, fills, text, image calls, etc.)."warn": only warnings."error": critical errors."none": silent.
Logs are printed in a structured format prefixed with [ILI9488].
Use the built-in self-test to verify wiring, basic drawing and colors:
display.self_test(delay_ms=600)It will:
- Fill the screen red, green, blue, then black.
- Draw a centered circle.
- Render
"SELF TEST"using 8×8 text.
This README provides a high-level overview and quick examples.
For full API reference and more examples (animations, UI widgets, font preparation, image format details, performance tips, etc.), see the dedicated documentation:
-
Report issues (bug descriptions, board type, display model, wiring, MicroPython/CircuitPython version).
-
Open pull requests for:
- New examples
- Additional helpers (widgets, convenience APIs)
- Documentation improvements
Please keep code compatible with both MicroPython and CircuitPython where possible.
See CREDITS.md for third-party authors and attributions.
This project is licensed under the MIT License:
MIT License
Copyright (c) 2026 Nolly
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.