(2025) "Fantasti-Watch”, ‘Fantastic Four: First Steps’, Functional Prop Replica, Micro-Electronics

“Fantastic Four: First Steps” Movie Screen-shot

“Fantasti-Watch” V1, Full Assembly Render

“Fantasti-Watch” V1, Electronics Render

“Fantasti-Watch” V2, Full Assembly Render

“Fantasti-Watch” V2, Electronics Render

"Fantasti-Watch", V14 FINAL Assembly Render

"Fantasti-Watch", V14 FINAL Electronics Render

“Fantastic Four: First Steps” Movie Screen-Grab, Zoomed

Early Integration test between ESP32-S3 and TFT Display

First Successful Graphics Test on TFT Display

First Successful Graphics Test on TFT Display

Graphite Powder Paint Job

First Assembly, Electronics

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype

Fantasti-Watch, Finished Prototype
First non-test Gif viewed on the display
First successful Gif integration Test
First Prototype, Functioning Showcase, Electronics fitted/Soldered
First Prototype, Audio/Charging Showcase, Electronics fitted/Soldered
First Prototype, Animations Showcase
First Prototype, Animations Showcase
Part List:
Custom CircuitPython Code:
# code.py
import board, busio, displayio, time, os, gifio, digitalio, supervisor
from fourwire import FourWire
from adafruit_st7789 import ST7789
from displayio import ColorConverter, Colorspace
import board, busio, displayio, time, os, gifio, digitalio, supervisor
from fourwire import FourWire
from adafruit_st7789 import ST7789
from displayio import ColorConverter, Colorspace
displayio.release_displays()
spi = busio.SPI(clock=board.D6, MOSI=board.D7)
bus = FourWire(
spi,
command=board.D8,
chip_select=board.D9,
reset=board.D10,
baudrate=40_000_000
)
bus = FourWire(
spi,
command=board.D8,
chip_select=board.D9,
reset=board.D10,
baudrate=40_000_000
)
display = ST7789(bus, width=240, height=145, rotation=90, rowstart=40, colstart=53)
display.auto_refresh = False
btn = digitalio.DigitalInOut(board.D2)
btn.switch_to_input(pull=digitalio.Pull.UP)
btn.switch_to_input(pull=digitalio.Pull.UP)
if supervisor.runtime.usb_connected or (not btn.value):
display.root_group = None
display.refresh(minimum_frames_per_second=0)
if supervisor.runtime.usb_connected:
while supervisor.runtime.usb_connected:
pass
else:
while True:
pass
while supervisor.runtime.usb_connected:
pass
else:
while True:
pass
gif_files = sorted([f for f in os.listdir("/") if f.lower().endswith(".gif")])
if not gif_files:
g = displayio.Group(); display.root_group = g
bg = displayio.Bitmap(240, 155, 1); pal = displayio.Palette(1); pal[0] = 0x000000
g.append(displayio.TileGrid(bg, pixel_shader=pal))
print("No .gif files found on CIRCUITPY. Add some and press reset.")
while True: time.sleep(1)
if not gif_files:
g = displayio.Group(); display.root_group = g
bg = displayio.Bitmap(240, 155, 1); pal = displayio.Palette(1); pal[0] = 0x000000
g.append(displayio.TileGrid(bg, pixel_shader=pal))
print("No .gif files found on CIRCUITPY. Add some and press reset.")
while True: time.sleep(1)
conv = ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED)
odg = None
tile = None
root = displayio.Group()
display.root_group = root
odg = None
tile = None
root = displayio.Group()
display.root_group = root
def show_gif(path):
global odg, tile, root
if odg:
try: odg.deinit()
except Exception: pass
global odg, tile, root
if odg:
try: odg.deinit()
except Exception: pass
odg = gifio.OnDiskGif(path)
start = time.monotonic()
delay = odg.next_frame()
decode_time = time.monotonic() - start
delay = odg.next_frame()
decode_time = time.monotonic() - start
tile = displayio.TileGrid(odg.bitmap, pixel_shader=conv)
w, h = odg.bitmap.width, odg.bitmap.height
tile.x = max(0, (240 - w) // 2)
tile.y = max(0, (155 - h) // 2)
w, h = odg.bitmap.width, odg.bitmap.height
tile.x = max(0, (240 - w) // 2)
tile.y = max(0, (155 - h) // 2)
root = displayio.Group()
root.append(tile)
display.root_group = root
root.append(tile)
display.root_group = root
display.refresh(minimum_frames_per_second=0)
return delay, decode_time
idx = 0
delay, decode_time = show_gif("/" + gif_files[idx])
delay, decode_time = show_gif("/" + gif_files[idx])
last = btn.value
last_ms = time.monotonic()
last_ms = time.monotonic()
def wait_with_button(seconds):
global last, last_ms, idx, delay, decode_time
deadline = time.monotonic() + seconds
SLICE = 0.005
DEBOUNCE = 0.05
global last, last_ms, idx, delay, decode_time
deadline = time.monotonic() + seconds
SLICE = 0.005
DEBOUNCE = 0.05
while True:
now = time.monotonic()
# check button edge
reading = btn.value
if reading != last and (now - last_ms) > DEBOUNCE:
last_ms = now
last = reading
if not reading:
idx = (idx + 1) % len(gif_files)
delay, decode_time = show_gif("/" + gif_files[idx])
return
now = time.monotonic()
# check button edge
reading = btn.value
if reading != last and (now - last_ms) > DEBOUNCE:
last_ms = now
last = reading
if not reading:
idx = (idx + 1) % len(gif_files)
delay, decode_time = show_gif("/" + gif_files[idx])
return
if now >= deadline:
break
time.sleep(SLICE)
break
time.sleep(SLICE)
while True:
wait_with_button(max(0, delay - decode_time))
wait_with_button(max(0, delay - decode_time))
t0 = time.monotonic()
delay = odg.next_frame()
display.refresh(minimum_frames_per_second=0)
decode_time = time.monotonic() - t0
delay = odg.next_frame()
display.refresh(minimum_frames_per_second=0)
decode_time = time.monotonic() - t0