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()
Text editor powered by tinymce.