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

Run the Script

Just run it using the following command:

python3 mini_pitft_player.py

Full Player Code

# 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()

This guide was first published on Sep 28, 2021. It was last updated on Sep 28, 2021.

This page (Mini PiTFT Player) was last updated on Nov 27, 2023.

Text editor powered by tinymce.