Wiring
It's easy to connect everything together with the Mini PiTFT player. First connect the display to the Raspberry Pi:
Then just connect a STEMMA QT Rotary Encoded up the the STEMMA QT Connector on the Mini PiTFT.
Setup
You will want to start by installing the latest version of the Raspberry Pi Lite onto an SD card. You can refer to the CircuitPython Libraries on Linux and Raspberry Pi guide for more help setting it up.
Once you have everything set up, you will need to open a terminal and install Blinka. Refer to the Installing CircuitPython Libraries on Raspberry Pi page to quickly get up and running.
Install Required Libraries
You will need to have a few libraries installed before the script will run on your computer.
Install the required CircuitPython libraries:
pip3 install adafruit-circuitpython-rgb-display pip3 install adafruit-circuitpython-seesaw pip3 install --upgrade --force-reinstall spidev
Also, install NumPy since it will speed up the RGB Display library:
pip3 install numpy
DejaVu TTF Font
Raspberry Pi usually comes with the DejaVu font already installed, but in case it didn't, you can run the following to install it:
sudo apt-get install ttf-dejavu
Pillow Library
We also need PIL, the Python Imaging Library, to allow graphics and using text with custom fonts. There are several system libraries that PIL relies on, so installing via a package manager is the easiest way to bring in everything:
sudo apt-get install python3-pil
Download the Code
Copy mini_pitft_player.py and animatedgif.py onto the Raspberry Pi. You can download them by clicking on the Download Project Bundle below or you can use wget to copy them right off the web into your current folder:
wget https://github.com/adafruit/Adafruit_Learning_System_Guides/raw/main/Raspberry_Pi_Animated_Gif_Player/mini_pitft_player.py wget https://github.com/adafruit/Adafruit_Learning_System_Guides/raw/main/Raspberry_Pi_Animated_Gif_Player/animatedgif.py
python3 mini_pitft_player.py
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
This example is for use on (Linux) computers that are using CPython with
Adafruit Blinka to support CircuitPython libraries. CircuitPython does
not support PIL/pillow (python imaging library)!
Author(s): Melissa LeBlanc-Williams for Adafruit Industries
"""
import digitalio
import board
from animatedgif import AnimatedGif
import numpy # pylint: disable=unused-import
from adafruit_seesaw import seesaw, rotaryio, digitalio as ss_digitalio
from adafruit_rgb_display import st7789
i2c = board.I2C() # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
seesaw = seesaw.Seesaw(i2c, addr=0x36)
seesaw_product = (seesaw.get_version() >> 16) & 0xFFFF
print("Found product {}".format(seesaw_product))
if seesaw_product != 4991:
print("Wrong firmware loaded? Make sure you have a rotary encoder connected.")
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
button = ss_digitalio.DigitalIO(seesaw, 24)
encoder = rotaryio.IncrementalEncoder(seesaw)
# Change to match your display
BUTTON_UP = board.D23
BUTTON_DOWN = board.D24
# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D25)
def init_button(pin):
digital_button = digitalio.DigitalInOut(pin)
digital_button.switch_to_input(pull=digitalio.Pull.UP)
return digital_button
class TFTAnimatedGif(AnimatedGif):
def __init__(self, display, include_delays=True, folder=None):
self._width = display.width
self._height = display.height
self.up_button = init_button(BUTTON_UP)
self.down_button = init_button(BUTTON_DOWN)
self._last_position = None
self._button_state = False
super().__init__(display, include_delays=include_delays, folder=folder)
def get_next_value(self):
while self._running:
position = -encoder.position
if position != self._last_position:
self._last_position = position
return str(position)
elif not button.value and not self._button_state:
self._button_state = True
return str("click")
elif button.value and self._button_state:
self._button_state = False
if not self.up_button.value or not self.down_button.value:
self._running = False
return None
def update_display(self, image):
self.display.image(image)
# Config for display baudrate (default max is 64mhz):
BAUDRATE = 64000000
# Setup SPI bus using hardware SPI:
spi = board.SPI()
# Create the display:
disp = st7789.ST7789(
spi,
height=240,
y_offset=80,
rotation=180,
cs=cs_pin,
dc=dc_pin,
rst=None,
baudrate=BAUDRATE,
)
gif_player = TFTAnimatedGif(disp, include_delays=False, folder="images")
# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
from PIL import Image, ImageOps
# pylint: disable=too-few-public-methods
class Frame:
def __init__(self, duration=0):
self.duration = duration
self.image = None
# pylint: enable=too-few-public-methods
class AnimatedGif:
def __init__(self, display, include_delays=True, folder=None):
self._frame_count = 0
self._loop = 0
self._index = 0
self._duration = 0
self._gif_files = []
self._frames = []
self._running = True
self.display = display
self.include_delays = include_delays
if folder is not None:
self.load_files(folder)
self.run()
def advance(self):
self._index = (self._index + 1) % len(self._gif_files)
def back(self):
self._index = (self._index - 1 + len(self._gif_files)) % len(self._gif_files)
def load_files(self, folder):
gif_files = [f for f in os.listdir(folder) if f.endswith(".gif")]
gif_folder = folder
if gif_folder[:-1] != "/":
gif_folder += "/"
for gif_file in gif_files:
image = Image.open(gif_folder + gif_file)
# Only add animated Gifs
if image.is_animated:
self._gif_files.append(gif_folder + gif_file)
print("Found", self._gif_files)
if not self._gif_files:
print("No Gif files found in current folder")
exit() # pylint: disable=consider-using-sys-exit
def preload(self):
image = Image.open(self._gif_files[self._index])
print("Loading {}...".format(self._gif_files[self._index]))
if "duration" in image.info:
self._duration = image.info["duration"]
else:
self._duration = 0
if "loop" in image.info:
self._loop = image.info["loop"]
else:
self._loop = 1
self._frame_count = image.n_frames
self._frames.clear()
for frame in range(self._frame_count):
image.seek(frame)
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
frame_object = Frame(duration=self._duration)
if "duration" in image.info:
frame_object.duration = image.info["duration"]
frame_object.image = ImageOps.pad( # pylint: disable=no-member
image.convert("RGB"),
(self._width, self._height),
method=Image.NEAREST,
color=(0, 0, 0),
centering=(0.5, 0.5),
)
self._frames.append(frame_object)
def play(self):
self.preload()
current_frame = 0
last_action = None
# Check if we have loaded any files first
if not self._gif_files:
print("There are no Gif Images loaded to Play")
return False
self.update_display(self._frames[current_frame].image)
while self._running:
action = self.get_next_value()
if action:
if not last_action:
last_action = action
if action == "click":
self.advance()
return False
elif int(action) < int(last_action):
current_frame -= 1
else:
current_frame += 1
current_frame %= self._frame_count
frame_object = self._frames[current_frame]
start_time = time.monotonic()
self.update_display(frame_object.image)
if self.include_delays:
remaining_delay = frame_object.duration / 1000 - (
time.monotonic() - start_time
)
if remaining_delay > 0:
time.sleep(remaining_delay)
last_action = action
if self._loop == 1:
return True
if self._loop > 0:
self._loop -= 1
def run(self):
while self._running:
auto_advance = self.play()
if auto_advance:
self.advance()
Page last edited January 22, 2025
Text editor powered by tinymce.