Once you've finished setting up your PyPortal with CircuitPython and have connected to the internet, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click the Download Project Bundle button in the window below. It will download to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2024 Brent Rubell, written for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import displayio
import terminalio
from adafruit_display_shapes.rect import Rect
from adafruit_display_text import label
from adafruit_pyportal import PyPortal
# Adafruit IO shared feed key
IO_FEED_KEY = 'location'
# Fetch the location every 5 minutes
SLEEP_DELAY_MINUTES = 5
# Set the backlight brightness, 0.0 (off) to 1.0 (max brightness)
BACKLIGHT_BRIGHTNESS = 0.5
# Location text and images
LOCATION_IMAGES = { 'home': 'images/home.bmp', 'work': 'images/office.bmp',
'gym': 'images/workout.bmp', 'commute': 'images/subway.bmp' }
# Create the PyPortal object
pyportal = PyPortal(status_neopixel=board.NEOPIXEL)
# Configure the PyPortal's display
display = board.DISPLAY
display.rotation = 0
display.brightness = BACKLIGHT_BRIGHTNESS
# Display label and image coordinates
TEXT_AREA_X = display.width // 6
TEXT_AREA_Y = 20
TEXT_AREA_LOCATION_X = display.width // 3
TEXT_AREA_LOCATION_Y = display.height - 20
IMAGE_SPRITE_X = (display.width // 3) - 10
IMAGE_SPRITE_Y = display.height // 5
# Create a displayIO Group
group = displayio.Group()
# Draw the background
bg_group = displayio.Group()
rect = Rect(0, 0, display.width, display.height, fill=0xFFFFFF)
bg_group.append(rect)
group.append(bg_group)
# Use the default font
font = terminalio.FONT
# Draw a label for the header text
text_area = label.Label(font, text="Where's My Friend?", color=0x000000, scale=2)
text_area.x = TEXT_AREA_X
text_area.y = TEXT_AREA_Y
group.append(text_area)
# Draw a label for the location text
text_area_location = label.Label(font, text="", color=0x000000, scale=3)
text_area_location.x = TEXT_AREA_LOCATION_X
text_area_location.y = TEXT_AREA_LOCATION_Y
group.append(text_area_location)
# Create a group for the icon only
icon_group = displayio.Group()
group.append(icon_group)
# Show the group
display.root_group = group
def set_image(image_group, filename):
"""Sets the image file for a given group for display."""
print(f"Set image to {filename}")
if image_group:
image_group.pop()
image = displayio.OnDiskBitmap(filename)
image_sprite = displayio.TileGrid(image,
pixel_shader=image.pixel_shader)
image_sprite.x = IMAGE_SPRITE_X
image_sprite.y = IMAGE_SPRITE_Y
image_group.append(image_sprite)
prv_location = None
while True:
try:
print("Fetching location data...")
# Fetch the location data from Adafruit IO
feed = pyportal.get_io_feed(IO_FEED_KEY)
# If the location value is in the list of images
if feed['last_value'] in LOCATION_IMAGES:
# Check if the location has changed from the last time
# we fetched the location
if prv_location == feed['last_value']:
print("Location has not changed!")
else: # Location has changed
print(f"Location: {feed['last_value']}")
# Load the image for the current location
set_image(icon_group, LOCATION_IMAGES[feed['last_value']])
# Update the location text
text_area_location.text=f"@ {feed['last_value']}"
# Show the refreshed group
display.root_group = group
# Update the previous location
prv_location = feed['last_value']
else:
print("Location not found in images!")
# Update the location text
text_area_location.text="@ unknown"
# Show the refreshed group
display.root_group = group
except RuntimeError as e:
print("Failed to fetch location data: ", e)
# Wait 5 minutes (300 seconds) before fetching the location feed again
print("Sleeping, fetching the location again in 5 minutes!")
time.sleep(SLEEP_DELAY_MINUTES * 60)
Upload the Code and Libraries to the PyPortal
After downloading the Project Bundle, plug your PyPortal into the computer's USB port with a known good USB data + power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the CIRCUITPY drive.
- lib folder
- images folder
- code.py
The CIRCUITPY drive should look like this after copying the lib folder, images folder, and the code.py file.
How the CircuitPython Code Works
At the top of the code, we import libraries used for this project and declare variables which you can use to customize your project:
IO_FEED_KEYis the shared feed's name.-
SLEEP_DELAY_SECONDSis how often the code should fetch the location from the Adafruit IO feed, in seconds. -
BACKLIGHT_BRIGHTNESSconfigures the TFT backlight's brightness. -
LOCATION_IMAGESadds text and images for the location feed's values (more on this later!)
import time
import board
import displayio
import terminalio
import adafruit_imageload
from adafruit_display_shapes.rect import Rect
from adafruit_display_text import label
from adafruit_pyportal import PyPortal
# Adafruit IO shared feed key
IO_FEED_KEY = 'location'
# Fetch the location every 10 seconds
SLEEP_DELAY_SECONDS = 10
# Set the backlight brightness, 0.0 (off) to 1.0 (max brightness)
BACKLIGHT_BRIGHTNESS = 0.5
# Location text and images
LOCATION_IMAGES = { 'home': 'images/home.bmp', 'work': 'images/office.bmp', 'gym': 'images/workout.bmp', 'commute': 'images/subway.bmp' }
Next, the PyPortal object is created along with the white background, labels for the header and location text, and a group for the icon.
# Create the PyPortal object
pyportal = PyPortal(status_neopixel=board.NEOPIXEL)
# Configure the PyPortal's display
display = board.DISPLAY
display.rotation = 0
display.brightness = BACKLIGHT_BRIGHTNESS
# Calculate the text area and image sprite locations based on the PyPortal model
if IS_PYPORTAL_TITANO:
TEXT_AREA_X = display.width // 5
TEXT_AREA_Y = 25
TEXT_AREA_LOCATION_X = (display.width // 3) - 2
TEXT_AREA_LOCATION_Y = display.height - 25
IMAGE_SPRITE_X = display.width // 3
IMAGE_SPRITE_Y = display.height // 4
else:
TEXT_AREA_X = display.width // 5
TEXT_AREA_Y = 25
TEXT_AREA_LOCATION_X = (display.width // 3) - 2
TEXT_AREA_LOCATION_Y = display.height - 25
IMAGE_SPRITE_X = display.width // 3
IMAGE_SPRITE_Y = display.height // 4
# Create a displayIO Group
group = displayio.Group()
# Draw the background
bg_group = displayio.Group()
rect = Rect(0, 0, display.width, display.height, fill=0xFFFFFF)
bg_group.append(rect)
group.append(bg_group)
# Use the default font
font = terminalio.FONT
# Draw a label for the header text
text_area = label.Label(font, text="Where is My Friend?", color=0x000000, scale=2)
text_area.x = TEXT_AREA_X
text_area.y = TEXT_AREA_Y
group.append(text_area)
# Draw a label for the location text
text_area_location = label.Label(font, text="@ the park", color=0x000000, scale=3)
text_area_location.x = TEXT_AREA_LOCATION_X
text_area_location.y = TEXT_AREA_LOCATION_Y
group.append(text_area_location)
# Create a group for the icon only
icon_group = displayio.Group()
group.append(icon_group)
# Show the group
display.root_group = group
The set_image() method is from Richard Albritton's Making a PyPortal User Interface with DisplayIO guide. This method simplifies the process of switching between images from within the while True loop.
def set_image(group, filename):
"""Sets the image file for a given group for display."""
print(f"Set image to {filename}")
if group:
group.pop()
if not filename:
return # we're done, no icon desired
try:
if image_file:
image_file.close
except NameError:
pass
image_file = open(filename, "rb")
image = displayio.OnDiskBitmap(image_file)
image_sprite = displayio.TileGrid(image, pixel_shader=getattr(image, 'pixel_shader', displayio.ColorConverter()))
image_sprite.x = IMAGE_SPRITE_X
image_sprite.y = IMAGE_SPRITE_Y
group.append(image_sprite)
Within the while True loop, we first fetch the Adafruit IO feed's most recent value.
print("Fetching location data...")
# Fetch the location data from Adafruit IO
feed = pyportal.get_io_feed(IO_FEED_KEY)
We check if the feed's value is associated with the list of locations, LOCATION_IMAGES and if the feed's value has changed since the last time it was fetched.
# If the location value is in the list of images
if feed['last_value'] in LOCATION_IMAGES:
# Check if the location has changed from the last time
# we fetched the location
if prv_location == feed['last_value']:
print("Location has not changed!")
If the value of the location feed has changed, we print out the location feed. Then, we use the set_image() method to load the desired icon onto the screen.
else: # Location has changed
print(f"Location: {feed['last_value']}")
# Load the image for the current location
set_image(icon_group, LOCATION_IMAGES[feed['last_value']])
Within LOCATION_IMAGES there is a dictionary of locations and associated images (in bitmap (bmp) format).
If you want to display different locations from the pre-selected ones - Change the dictionary's values and the image's file path.
LOCATION_IMAGES = { 'home': 'images/home.bmp', 'work': 'images/office.bmp', 'gym': 'images/workout.bmp', 'commute': 'images/subway.bmp' }
The text on the bottom of the screen is updated to display "@ [CURRENT LOCATION]" and the display is forced to refresh. Finally, we update the prv_location variable to store the current feed value.
# Update the location text
text_area_location.text=f"@ {feed['last_value']}"
# Show the refreshed group
display.root_group = group
# Update the previous location
prv_location = feed['last_value']
At the end of the while True loop, we wait 5 minutes before attempting to fetch the location feed again.
# Wait 5 minutes (300 seconds) before fetching the location feed again
print("Sleeping, fetching the location again in 5 minutes!")
time.sleep(SLEEP_DELAY_SECONDS)
Page last edited January 22, 2025
Text editor powered by tinymce.