Are you new to using CircuitPython? No worries, there is a full getting started guide here.

Plug the PyPortal into your computer with a known good USB cable (not a tint charge only cable). The PyPortal will appear to your computer as a flash drive named CIRCUITPY. If the drive does not appear you can install CircuitPython on your PyPortal and then return here.

Download the project files with the Download Project Bundle button below in the Code section. Unzip the file and copy/paste the code.py and other project files to your CIRCUITPY drive using File Explorer or Finder (depending on your operating system).

Inside the project bundle you'll find all of the files you need on your PyPortal. There is also a PyPortal_Winamp_Skin_Converter directory which does not need to go onto your PyPortal. It's used from your PC if you want to use a custom skin for your Winamp Player.

Drive Structure

After copying the files, your drive should look like the listing below. It can contain other files as well, but must contain these at a minimum:

Remember to to remove the PyPortal_Winamp_Skin_Converter directory, it does not need to go onto the CIRCUITPY drive.
Project

Code

The project code.py is shown below. The next page walks through how the code works.

# SPDX-FileCopyrightText: 2022 Tim C, written for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
"""
PyPortal winamp player
"""
import time
import sys
import storage
import board
import busio
import adafruit_touchscreen
import sdcardio
from winamp_helpers import WinampApplication

# which playlist to play
PLAYLIST_FILE = "playlist.json"

# which skin background to use
SKIN_IMAGE = "/base_240x320.bmp"

# skin configuration for color values
SKIN_CONFIG_FILE = "base_config.json"

# must wait at least this long between touch events
TOUCH_COOLDOWN = 0.5  # seconds

# display orientation. Must be 90 or 270.
ORIENTATION = 90

PYPORTAL_TITANO = False

if not PYPORTAL_TITANO:
    SIZE = (240, 320)
    if ORIENTATION == 270:
        # setup touch screen
        ts = adafruit_touchscreen.Touchscreen(
            board.TOUCH_YD,
            board.TOUCH_YU,
            board.TOUCH_XR,
            board.TOUCH_XL,
            calibration=((5200, 59000), (5800, 57000)),
            size=(240, 320),
        )
    elif ORIENTATION == 90:
        # setup touch screen
        ts = adafruit_touchscreen.Touchscreen(
            board.TOUCH_YU,
            board.TOUCH_YD,
            board.TOUCH_XL,
            board.TOUCH_XR,
            calibration=((5200, 59000), (5800, 57000)),
            size=(240, 320),
        )
    else:
        raise ValueError("ORIENTATION must be 90 or 270")
else:  # PyPortal Titano
    SIZE = (320, 480)
    if ORIENTATION == 270:
        # setup touch screen
        ts = adafruit_touchscreen.Touchscreen(
            board.TOUCH_YD,
            board.TOUCH_YU,
            board.TOUCH_XR,
            board.TOUCH_XL,
            calibration=((5200, 59000), (5800, 57000)),
            size=(320, 480),
        )
    elif ORIENTATION == 90:
        # setup touch screen
        ts = adafruit_touchscreen.Touchscreen(
            board.TOUCH_YU,
            board.TOUCH_YD,
            board.TOUCH_XL,
            board.TOUCH_XR,
            calibration=((5200, 59000), (5800, 57000)),
            size=(320, 480),
        )

# Initializations for SDCard
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
sdcard = sdcardio.SDCard(spi, board.SD_CS)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
sys.path.append("/sd")

# debugging, print files that exist on SDCard
# print(os.listdir("/sd"))

# get reference to the display
display = board.DISPLAY

# set rotation
display.rotation = ORIENTATION

# Initialize WinampApplication helper class
winamp_application = WinampApplication(
    playlist_file=PLAYLIST_FILE,
    skin_image=SKIN_IMAGE,
    skin_config_file=SKIN_CONFIG_FILE,
    pyportal_titano=PYPORTAL_TITANO,
)

# Add the Group to the Display
display.show(winamp_application)

# previous iteration touch events
_previous_touch = None

# last time a touch occured
_previous_touch_time = 0

# main loop
while True:
    # update winamp application
    winamp_application.update()

    # check for touch events
    p = ts.touch_point
    _now = time.monotonic()
    # if touch cooldown time has elapsed
    if _now >= _previous_touch_time + TOUCH_COOLDOWN:
        # if there is a touch
        if p and not _previous_touch:
            # store the time to compare with next iteration
            _previous_touch_time = _now
            # if touch is on bottom half
            if p[1] > SIZE[1] // 2:
                # if touch is on right half
                if p[0] >= SIZE[0] // 2:
                    winamp_application.next_track()

                # if touch is on left half
                else:
                    winamp_application.previous_track()
            # if touch is on top half
            else:
                # if currently playing song
                if winamp_application.CURRENT_STATE == winamp_application.STATE_PLAYING:
                    print("pausing")
                    winamp_application.pause()

                # if song is paused
                else:
                    print("resuming")
                    winamp_application.resume()

    # store previous touch event t compare with next iteration
    _previous_touch = p

This guide was first published on Feb 18, 2022. It was last updated on Feb 18, 2022.

This page (Project Setup) was last updated on Sep 27, 2023.

Text editor powered by tinymce.