Once you've finished setting up your Qualia S3 with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import ssl
import time
import microcontroller
import wifi
import socketpool
import adafruit_requests
import board
import displayio
import keypad
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
from adafruit_display_text import outlined_label
from adafruit_bitmap_font import bitmap_font
from adafruit_qualia.graphics import Graphics, Displays
urls = [{'name': "Epcot",
'url': "https://queue-times.com/en-US/parks/5/queue_times.json"},
{'name': "Magic Kingdom",
'url': "https://queue-times.com/en-US/parks/6/queue_times.json"},
{'name': "Hollywood Studios",
'url': "https://queue-times.com/en-US/parks/7/queue_times.json"},
{'name': "Animal Kingdom",
'url': "https://queue-times.com/en-US/parks/8/queue_times.json"},
]
bitmap = displayio.OnDiskBitmap("/park-bg.bmp")
key = keypad.Keys((board.A0,), value_when_pressed=False, pull=True)
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}")
context = ssl.create_default_context()
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, context)
graphics = Graphics(Displays.ROUND40, default_bg=None, auto_refresh=True)
grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
group = displayio.Group()
group.append(grid)
font = bitmap_font.load_font("/Roboto-Regular-47.pcf")
ride_text = []
wait_text = []
for i in range(5):
text_ride = outlined_label.OutlinedLabel(font, text=" ",
outline_color=0x000000,
outline_size=3,
padding_left=4,
padding_right=4,
padding_top=4,
padding_bottom=4)
ride_text.append(text_ride)
text_wait = outlined_label.OutlinedLabel(font, text=" ",
outline_color=0x000000,
outline_size=3,
padding_left=4,
padding_right=4,
padding_top=4,
padding_bottom=4)
wait_text.append(text_wait)
group.append(text_ride)
group.append(text_wait)
text_park = outlined_label.OutlinedLabel(font, text=urls[0]['name'],
outline_color = 0x000000,
outline_size=3,
padding_left=4,
padding_right=4,
padding_top=4,
padding_bottom=4)
text_park.x = (graphics.display.width - text_park.width) // 2
text_park.y = graphics.display.height - (text_park.height * 2)
group.append(text_park)
def center(g, b):
# center the image
g.x -= (b.width - graphics.display.width) // 2
g.y -= (b.height - graphics.display.height) // 2
center(grid, bitmap)
graphics.display.root_group = group
def sort_rides(data):
y = 30
x = [135, 55, 15, 25, 45]
all_rides = []
for land in data['lands']:
all_rides.extend(land['rides'])
sorted_rides = sorted(all_rides, key=lambda x: x['wait_time'], reverse=True)
for ride in sorted_rides:
r = sorted_rides.index(ride)
if r > 4:
break
#print(wait_text[r])
ride_text[r].text = f"{ride['name']:.20}"
if len(ride['name']) > 20:
ride_text[r].text = ride_text[r].text + ".."
ride_text[r].x = x[r]
ride_text[r].y = y + 70
wait_text[r].text = f"{ride['wait_time']} Minutes"
wait_text[r].x = 400
wait_text[r].y = ride_text[r].y + wait_text[r].height + 20
y += wait_text[r].height * 2 + 30
clock_timer = 5 * 60 * 1000
clock_clock = ticks_ms()
park_index = 0
update = True
while True:
try:
event = key.events.get()
if event:
if event.pressed:
print("updating display")
park_index = (park_index + 1) % len(urls)
text_park.text=urls[park_index]['name']
text_park.x = (graphics.display.width - text_park.width) // 2
text_park.y = graphics.display.height - (text_park.height * 2)
update = True
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer or update:
response = requests.get(urls[park_index]['url'])
# packs the response into a JSON
response_data = response.json()
sort_rides(response_data)
update = False
clock_clock = ticks_add(clock_clock, clock_timer)
except Exception as error: # pylint: disable=broad-except
print(f"error! {error} resetting..")
time.sleep(5)
microcontroller.reset()
Upload the Code and Libraries to the Qualia S3
After downloading the Project Bundle, plug your Qualia S3 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 Qualia S3's CIRCUITPY drive.
- lib folder
- code.py
- park-bg.bmp
- Roboto-Regular-47.pcf
Your Qualia S3 CIRCUITPY drive should look like this after copying the lib folder, park-bg.bmp file, Roboto-Regular-47.pcf file and the code.py file.
Add Your settings.toml File
As of CircuitPython 8.0.0, there is support for Environment Variables. Environment variables are stored in a settings.toml file. Similar to secrets.py, the settings.toml file separates your sensitive information from your main code.py file. Add your settings.toml file as described in the Create Your settings.toml File page earlier in this guide. You'll need to include your CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD.
CIRCUITPY_WIFI_SSID = "your-ssid-here" CIRCUITPY_WIFI_PASSWORD = "your-ssid-password-here"
How the CircuitPython Code Works
At the top of the code is a dictionary with the name of the park and its Queue Times API JSON feed. The text in the name entry will be shown on the display. You can update this dictionary with the names of the parks that you want to track. The code will dynamically adjust to a different number of feeds.
urls = [{'name': "Epcot", 'url': "https://queue-times.com/en-US/parks/5/queue_times.json"},
{'name': "Magic Kingdom", 'url': "https://queue-times.com/en-US/parks/6/queue_times.json"},
{'name': "Hollywood Studios", 'url': "https://queue-times.com/en-US/parks/7/queue_times.json"},
{'name': "Animal Kingdom", 'url': "https://queue-times.com/en-US/parks/8/queue_times.json"},
]
Bitmap, Keypad and WiFi
The background image is loaded in as an OnDiskBitmap and the button that changes the park view is instantiated as a Keypad object on pin A0. A WiFi connection is established using your SSID and password entries from settings.toml.
bitmap = displayio.OnDiskBitmap("/park-bg.bmp")
key = keypad.Keys((board.A0,), value_when_pressed=False, pull=True)
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}")
context = ssl.create_default_context()
pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, context)
Graphics
Ten different labels are used to display the ride name and ride wait time. These are created with a for loop and are added to the display Group. The ride text elements are added to the ride_text list and the wait time text elements are added to the wait_text list.
One text label is created for the park name that is stored in the urls dictionary. This label is located at the bottom of the display.
graphics = Graphics(Displays.ROUND40, default_bg=None, auto_refresh=True)
grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
group = displayio.Group()
group.append(grid)
font = bitmap_font.load_font("/Roboto-Regular-47.pcf")
ride_text = []
wait_text = []
for i in range(5):
text_ride = outlined_label.OutlinedLabel(font, text=" ",
outline_color=0x000000,
outline_size=3,
padding_left=4,
padding_right=4,
padding_top=4,
padding_bottom=4)
ride_text.append(text_ride)
text_wait = outlined_label.OutlinedLabel(font, text=" ",
outline_color=0x000000,
outline_size=3,
padding_left=4,
padding_right=4,
padding_top=4,
padding_bottom=4)
wait_text.append(text_wait)
group.append(text_ride)
group.append(text_wait)
text_park = outlined_label.OutlinedLabel(font, text=urls[0]['name'],
outline_color = 0x000000,
outline_size=3,
padding_left=4,
padding_right=4,
padding_top=4,
padding_bottom=4)
text_park.x = (graphics.display.width - text_park.width) // 2
text_park.y = graphics.display.height - (text_park.height * 2)
group.append(text_park)
Sorting
A function is used in the loop that sorts the returned list of rides by their wait time. The five rides with the longest wait times are shown on the display by updating the text elements in ride_text and wait_text.Â
def sort_rides(data):
y = 30
x = [135, 55, 15, 25, 45]
all_rides = []
for land in data['lands']:
all_rides.extend(land['rides'])
sorted_rides = sorted(all_rides, key=lambda x: x['wait_time'], reverse=True)
for ride in sorted_rides:
r = sorted_rides.index(ride)
if r > 4:
break
#print(wait_text[r])
ride_text[r].text = f"{ride['name']:.20}"
if len(ride['name']) > 20:
ride_text[r].text = ride_text[r].text + ".."
ride_text[r].x = x[r]
ride_text[r].y = y + 70
wait_text[r].text = f"{ride['wait_time']} Minutes"
wait_text[r].x = 400
wait_text[r].y = ride_text[r].y + wait_text[r].height + 20
y += wait_text[r].height * 2 + 30
Times and States
AÂ ticks timer, set for five minutes, is used to keep time in the loop. park_index tracks which park is selected from the urls dictionary and the update state tracks whether the JSON feed should be polled.
clock_timer = 5 * 60 * 1000 clock_clock = ticks_ms() park_index = 0 update = True
The Loop
In the loop, if the button is pressed then the park_index increases by one, resulting in the park text updating on the display and update being set to True. If update is True or if the ticks timer runs out, the JSON feed for the selected park is fetched. The sort_rides() function sorts the JSON feed by ride wait time and updates the text elements for the top five rides and their wait times.
while True:
try:
event = key.events.get()
if event:
if event.pressed:
print("updating display")
park_index = (park_index + 1) % len(urls)
text_park.text=urls[park_index]['name']
text_park.x = (graphics.display.width - text_park.width) // 2
text_park.y = graphics.display.height - (text_park.height * 2)
update = True
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer or update:
response = requests.get(urls[park_index]['url'])
# packs the response into a JSON
response_data = response.json()
sort_rides(response_data)
update = False
clock_clock = ticks_add(clock_clock, clock_timer)
except Exception as error:
print(f"error! {error} resetting..")
time.sleep(5)
microcontroller.reset()
Page last edited January 22, 2025
Text editor powered by tinymce.