Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active September 3, 2024 19:15
Show Gist options
  • Save todbot/6e42d2b3dc6a215f6f167436fc1594fb to your computer and use it in GitHub Desktop.
Save todbot/6e42d2b3dc6a215f6f167436fc1594fb to your computer and use it in GitHub Desktop.
dual display GC9A01 round TFT LCDs with creepy eyeball animation in CircuitPython
# eyeballs_dual_gc9a01.py - dual display GC9A01 round TFT LCDs with creepy eyeball animation
# 17 May 2022 - @todbot / Tod Kurt
#
# requires recompiled CircuitPython with "#define CIRCUITPY_DISPLAY_LIMIT (2)" in "mpconfigboard.h"
#
import time, math, random
import board, busio
import displayio, terminalio
import gc9a01
dw,dh = 240,240 # display dimensions
iris_w, iris_h = 110,110 # iris image is 110x110
iris_cx, iris_cy = dw//2 - iris_w//2, dh//2 -iris_h//2 # "center" of iris image
eyeball_bitmap = displayio.OnDiskBitmap(open("imgs/eye0_ball2.bmp", "rb"))
iris_bitmap = displayio.OnDiskBitmap(open("imgs/eye0_iris0.bmp", "rb"))
iris_pal = iris_bitmap.pixel_shader
iris_pal.make_transparent(0)
tft_clk = board.SCK # wiring for a QTPy ESP32-S2 or ESP32-S3, but any CirPy board can work
tft_mosi = board.MOSI
tft_L_rst, tft_L_dc, tft_L_cs = board.TX, board.RX, board.A3
tft_R_rst, tft_R_dc, tft_R_cs = board.A2, board.SDA, board.SCL
spi = board.SPI() # spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) # if using other SPI port
# set up the GC9A01 round TFT LCD display, display groups, and tile grids for eyes
def display_eye_init(tft_dc, tft_cs, tft_rst, rot):
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst)
display = gc9a01.GC9A01(display_bus, width=dw, height=dh, rotation=rot)
main = displayio.Group()
display.show(main)
eyeball = displayio.TileGrid(eyeball_bitmap, pixel_shader=eyeball_bitmap.pixel_shader)
iris = displayio.TileGrid(iris_bitmap, pixel_shader=iris_pal, x = iris_cx, y = iris_cy )
main.append(eyeball) # add eyeball & iris to main group
main.append(iris)
return(display, eyeball, iris)
# make the eyes "Just eyes. I design your eyes. You Nexus, huh?"
displayio.release_displays()
(display_L, eyeball_L, iris_L) = display_eye_init( tft_L_dc, tft_L_cs, tft_L_cs, rot=0)
(display_R, eyeball_R, iris_R) = display_eye_init( tft_R_dc, tft_R_cs, tft_R_cs, rot=180)
thetaL, thetaR = 0,0 # left & right eye rotational position
dthetaL, dthetaR = 0.25, 0.25 # how fast left & right eyes spins
r = 17 # size of eye spin
while True:
iris_L.x = iris_cx + int(r * math.sin(thetaL)) # update iris positions based on angle
iris_L.y = iris_cy + int(r * math.cos(thetaL))
iris_R.x = iris_cx + int(r * math.sin(thetaR))
iris_R.y = iris_cy + int(r * math.cos(thetaR))
if random.randint(0,20) == 0:
dthetaR = -dthetaR # randomly change right eye spin direction
thetaL -= dthetaL # update angles (negative for clockwise motion)
thetaR -= dthetaR
display_L.refresh( target_frames_per_second=20 )
display_R.refresh( target_frames_per_second=20 )
@todbot
Copy link
Author

todbot commented May 17, 2022

What the above looks like on a QTPy ESP32-S3

eyeballs_dual_gc9a01.mp4

@todbot
Copy link
Author

todbot commented May 17, 2022

The eyeball images are (must be converted to BMP):
eye0_ball2
eye0_iris0

@Stupot77
Copy link

Stupot77 commented Mar 6, 2023

requires recompiled CircuitPython with "#define CIRCUITPY_DISPLAY_LIMIT (2)" in "mpconfigboard.h"

WHUT?

@todbot
Copy link
Author

todbot commented Mar 6, 2023

requires recompiled CircuitPython with "#define CIRCUITPY_DISPLAY_LIMIT (2)" in "mpconfigboard.h"

WHUT?

This post explains it a bit more: https://todbot.com/blog/2022/05/19/multiple-displays-in-circuitpython-compiling-custom-circuitpython/

@Stupot77
Copy link

Stupot77 commented Mar 6, 2023

Amazeballs! (eyeballs) I didn't think anyone answered on here! Cheers mate!

Great write-up, by the way, I'm just learning and your posts/blog really are a great help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment