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.
Download the project files with the Download Project Bundle button below. 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).
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:
# SPDX-FileCopyrightText: 2020 Tim C, written for Adafruit Industries # # SPDX-License-Identifier: Unlicense """ PyPortal implementation of Busy Simulator notification sound looper. """ import time import board import displayio import adafruit_touchscreen from adafruit_displayio_layout.layouts.grid_layout import GridLayout from adafruit_displayio_layout.widgets.icon_widget import IconWidget from audiocore import WaveFile from audioio import AudioOut # How many seconds to wait between playing samples # Lower time means it will play faster WAIT_TIME = 3.0 # List that will hold indexes of notification samples to play LOOP = [] # last time that we played a sample LAST_PLAY_TIME = 0 CUR_LOOP_INDEX = 0 # touch events must have at least this long between them COOLDOWN_TIME = 0.25 # seconds # last time that the display was touched # used for debouncing and cooldown enforcement LAST_PRESS_TIME = -1 # Was any icon touched last iteration. # Used for debouncing. WAS_TOUCHED = False def next_index(): """ return the next index in the LOOP that should get played """ if CUR_LOOP_INDEX + 1 >= len(LOOP): return 0 return CUR_LOOP_INDEX + 1 # list of icons to show # each entry is a tuple containing: # (Icon Label, Icon BMP image file, Notification sample wav file _icons = [ ("Outlook", "icons/outlook.bmp", "sounds/outlook.wav"), ("Phone", "icons/phone.bmp", "sounds/phone.wav"), ("Skype", "icons/skype.bmp", "sounds/skype.wav"), ("Teams", "icons/teams.bmp", "sounds/teams.wav"), ("Discord", "icons/discord.bmp", "sounds/discord.wav"), ("Apple Mail", "icons/applemail.bmp", "sounds/applemail.wav"), ("iMessage", "icons/imessage.bmp", "sounds/imessage.wav"), ("Slack", "icons/slack.bmp", "sounds/slack.wav"), ("G Calendar", "icons/gcal.bmp", "sounds/RE.wav"), ("G Chat", "icons/gchat.bmp", "sounds/gchat.wav"), ("Stop", "icons/stop.bmp", ""), ] # Make the display context. display = board.DISPLAY main_group = displayio.Group() display.root_group = main_group # Touchscreen initialization ts = adafruit_touchscreen.Touchscreen( board.TOUCH_XL, board.TOUCH_XR, board.TOUCH_YD, board.TOUCH_YU, calibration=((5200, 59000), (5800, 57000)), size=(display.width, display.height), ) # Setup the file as the bitmap data source bg_bitmap = displayio.OnDiskBitmap("busysim_background.bmp") # Create a TileGrid to hold the bitmap bg_tile_grid = displayio.TileGrid( bg_bitmap, pixel_shader=getattr(bg_bitmap, "pixel_shader", displayio.ColorConverter()), ) # add it to the group that is showing main_group.append(bg_tile_grid) # grid to hold the icons layout = GridLayout( x=0, y=0, width=320, height=240, grid_size=(4, 3), cell_padding=20, ) # initialize the icons in the grid for i, icon in enumerate(_icons): icon_widget = IconWidget( icon[0], icon[1], x=0, y=0, on_disk=True, transparent_index=0, label_background=0x888888, ) layout.add_content(icon_widget, grid_position=(i % 4, i // 4), cell_size=(1, 1)) # add the grid to the group showing on the display main_group.append(layout) def check_for_touch(_now): """ Check the touchscreen and do any actions necessary if an icon has been touched. Applies debouncing and cool down enforcement to filter out unneeded touch events. :param int _now: The current time in seconds. Used for cool down enforcement """ # pylint: disable=global-statement, too-many-nested-blocks, consider-using-enumerate global CUR_LOOP_INDEX global LOOP global LAST_PRESS_TIME global WAS_TOUCHED # read the touch data touch_point = ts.touch_point # if anything is touched if touch_point: # if the touch just began. We ignore further events until # after the touch has been lifted if not WAS_TOUCHED: # set the variable so we know to ignore future events until # touch is released WAS_TOUCHED = True # if it has been long enough time since previous touch event if _now - LAST_PRESS_TIME > COOLDOWN_TIME: LAST_PRESS_TIME = time.monotonic() # loop over the icons for _ in range(len(_icons)): # lookup current icon in the grid layout cur_icon = layout.get_cell((_ % 4, _ // 4)) # check if it's being touched if cur_icon.contains(touch_point): print("icon {} touched".format(_)) # if it's the stop icon if _icons[_][0] == "Stop": # empty out the loop LOOP = [] # set current index back to 0 CUR_LOOP_INDEX = 0 else: # any other icon # insert the touched icons sample index into the loop LOOP.insert(CUR_LOOP_INDEX, _) # print(LOOP) # break out of the for loop. # if current icon is being touched then no others can be break # nothing is touched else: # set variable back to false for debouncing WAS_TOUCHED = False # main loop while True: # store current time in variable for cool down enforcement _now = time.monotonic() # check for and process touch events check_for_touch(_now) # if it's time to play a sample if LAST_PLAY_TIME + WAIT_TIME <= _now: # print("time to play") # if there are any samples in the loop if len(LOOP) > 0: # open the sample wav file with open(_icons[LOOP[CUR_LOOP_INDEX]][2], "rb") as wave_file: print("playing: {}".format(_icons[LOOP[CUR_LOOP_INDEX]][2])) # initialize audio output pin audio = AudioOut(board.AUDIO_OUT) # initialize WaveFile object wave = WaveFile(wave_file) # play it audio.play(wave) # while it's still playing while audio.playing: # update time variable _now = time.monotonic() # check for and process touch events check_for_touch(_now) # after done playing. deinit audio output audio.deinit() # increment index counter CUR_LOOP_INDEX = next_index() # update variable for last time we attempted to play sample LAST_PLAY_TIME = _now
Text editor powered by tinymce.