Skip to content

Commit 2e18b3c

Browse files
committed
adding pyportal busy simulator project
1 parent 81c387c commit 2e18b3c

24 files changed

+231
-0
lines changed
76.1 KB
Binary file not shown.

PyPortal_BusySimulator/code.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# SPDX-FileCopyrightText: 2020 Tim C, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
"""
5+
PyPortal implementation of Busy Simulator notification sound looper.
6+
"""
7+
import board
8+
import displayio
9+
import adafruit_touchscreen
10+
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
11+
from adafruit_displayio_layout.widgets.icon_widget import IconWidget
12+
import time
13+
from audiocore import WaveFile
14+
from audioio import AudioOut
15+
from vectorio import Circle
16+
17+
# How many seconds to wait between playing samples
18+
# Lower time means it will play faster
19+
WAIT_TIME = 3.0
20+
21+
# List that will hold indexes of notification samples to play
22+
LOOP = []
23+
24+
# last time that we played a sample
25+
LAST_PLAY_TIME = 0
26+
27+
CUR_LOOP_INDEX = 0
28+
29+
# touch events must have at least this long between them
30+
COOLDOWN_TIME = 0.25 # seconds
31+
32+
# last time that the display was touched
33+
# used for debouncing and cooldown enforcement
34+
LAST_PRESS_TIME = -1
35+
36+
# Was any icon touched last iteration.
37+
# Used for debouncing.
38+
WAS_TOUCHED = False
39+
40+
41+
def next_index():
42+
"""
43+
return the next index in the LOOP that should get played
44+
"""
45+
if CUR_LOOP_INDEX + 1 >= len(LOOP):
46+
return 0
47+
48+
return CUR_LOOP_INDEX + 1
49+
50+
51+
# list of icons to show
52+
# each entry is a tuple containing:
53+
# (Icon Label, Icon BMP image file, Notification sample wav file
54+
_icons = [
55+
("Outlook", "icons/outlook.bmp", "sounds/outlook.wav"),
56+
("Phone", "icons/phone.bmp", "sounds/phone.wav"),
57+
("Skype", "icons/skype.bmp", "sounds/skype.wav"),
58+
("Teams", "icons/teams.bmp", "sounds/teams.wav"),
59+
("Discord", "icons/discord.bmp", "sounds/discord.wav"),
60+
("Apple Mail", "icons/applemail.bmp", "sounds/applemail.wav"),
61+
("iMessage", "icons/imessage.bmp", "sounds/imessage.wav"),
62+
("Slack", "icons/slack.bmp", "sounds/slack.wav"),
63+
("G Calendar", "icons/gcal.bmp", "sounds/RE.wav"),
64+
("G Chat", "icons/gchat.bmp", "sounds/gchat.wav"),
65+
("Stop", "icons/stop.bmp", ""),
66+
]
67+
68+
# Make the display context.
69+
display = board.DISPLAY
70+
main_group = displayio.Group()
71+
display.show(main_group)
72+
73+
# Touchscreen initialization
74+
ts = adafruit_touchscreen.Touchscreen(
75+
board.TOUCH_XL,
76+
board.TOUCH_XR,
77+
board.TOUCH_YD,
78+
board.TOUCH_YU,
79+
calibration=((5200, 59000), (5800, 57000)),
80+
size=(display.width, display.height),
81+
)
82+
83+
# Setup the file as the bitmap data source
84+
bg_bitmap = displayio.OnDiskBitmap("busysim_background.bmp")
85+
86+
# Create a TileGrid to hold the bitmap
87+
bg_tile_grid = displayio.TileGrid(
88+
bg_bitmap,
89+
pixel_shader=getattr(bg_bitmap, "pixel_shader", displayio.ColorConverter()),
90+
)
91+
92+
# add it to the group that is showing
93+
main_group.append(bg_tile_grid)
94+
95+
# grid to hold the icons
96+
layout = GridLayout(
97+
x=0,
98+
y=0,
99+
width=320,
100+
height=240,
101+
grid_size=(4, 3),
102+
cell_padding=20,
103+
)
104+
105+
# initialize the icons in the grid
106+
for i, icon in enumerate(_icons):
107+
icon_widget = IconWidget(
108+
icon[0],
109+
icon[1],
110+
x=0,
111+
y=0,
112+
on_disk=True,
113+
transparent_index=0,
114+
label_background=0x888888,
115+
)
116+
117+
layout.add_content(icon_widget, grid_position=(i % 4, i // 4), cell_size=(1, 1))
118+
119+
# add the grid to the group showing on the display
120+
main_group.append(layout)
121+
122+
123+
def check_for_touch(_now):
124+
"""
125+
Check the touchscreen and do any actions necessary if an
126+
icon has been touched. Applies debouncing and cool down
127+
enforcement to filter out unneeded touch events.
128+
129+
:param int _now: The current time in seconds. Used for cool down enforcement
130+
"""
131+
global CUR_LOOP_INDEX
132+
global LOOP
133+
global LAST_PRESS_TIME
134+
global WAS_TOUCHED
135+
136+
# read the touch data
137+
p = ts.touch_point
138+
139+
# if anything is touched
140+
if p:
141+
# if the touch just began. We ignore further events until
142+
# after the touch has been lifted
143+
if not WAS_TOUCHED:
144+
145+
# set the variable so we know to ignore future events until
146+
# touch is released
147+
WAS_TOUCHED = True
148+
149+
# if it has been long enough time since previous touch event
150+
if _now - LAST_PRESS_TIME > COOLDOWN_TIME:
151+
152+
LAST_PRESS_TIME = time.monotonic()
153+
154+
# loop over the icons
155+
for i in range(len(_icons)):
156+
# lookup current icon in the grid layout
157+
icon = layout.get_cell((i % 4, i // 4))
158+
159+
# check if it's being touched
160+
if icon.contains(p):
161+
print("icon {} touched".format(i))
162+
163+
# if it's the stop icon
164+
if _icons[i][0] == "Stop":
165+
166+
# empty out the loop
167+
LOOP = []
168+
169+
# set current index back to 0
170+
CUR_LOOP_INDEX = 0
171+
172+
else: # any other icon
173+
# insert the touched icons sample index into the loop
174+
LOOP.insert(CUR_LOOP_INDEX, i)
175+
176+
# print(LOOP)
177+
178+
# break out of the for loop.
179+
# if current icon is being touched then no others can be
180+
break
181+
182+
# nothing is touched
183+
else:
184+
# set variable back to false for debouncing
185+
WAS_TOUCHED = False
186+
187+
188+
# main loop
189+
while True:
190+
# store current time in variable for cool down enforcement
191+
_now = time.monotonic()
192+
193+
# check for and process touch events
194+
check_for_touch(_now)
195+
196+
# if it's time to play a sample
197+
if LAST_PLAY_TIME + WAIT_TIME <= _now:
198+
# print("time to play")
199+
200+
# if there are any samples in the loop
201+
if len(LOOP) > 0:
202+
203+
# open the sample wav file
204+
with open(_icons[LOOP[CUR_LOOP_INDEX]][2], "rb") as wave_file:
205+
print("playing: {}".format(_icons[LOOP[CUR_LOOP_INDEX]][2]))
206+
207+
# initialize audio output pin
208+
audio = AudioOut(board.AUDIO_OUT)
209+
210+
# initialize WaveFile object
211+
wave = WaveFile(wave_file)
212+
213+
# play it
214+
audio.play(wave)
215+
216+
# while it's still playing
217+
while audio.playing:
218+
# update time variable
219+
_now = time.monotonic()
220+
221+
# check for and process touch events
222+
check_for_touch(_now)
223+
224+
# after done playing. deinit audio output
225+
audio.deinit()
226+
227+
# increment index counter
228+
CUR_LOOP_INDEX = next_index()
229+
230+
# update variable for last time we attempted to play sample
231+
LAST_PLAY_TIME = _now
4.13 KB
Binary file not shown.
2.26 KB
Binary file not shown.

PyPortal_BusySimulator/icons/gcal.bmp

1.35 KB
Binary file not shown.
698 Bytes
Binary file not shown.
1.53 KB
Binary file not shown.
1.79 KB
Binary file not shown.
2.02 KB
Binary file not shown.
1.82 KB
Binary file not shown.
1.67 KB
Binary file not shown.

PyPortal_BusySimulator/icons/stop.bmp

274 Bytes
Binary file not shown.
1.54 KB
Binary file not shown.

PyPortal_BusySimulator/sounds/RE.wav

43.8 KB
Binary file not shown.
25.8 KB
Binary file not shown.
10.8 KB
Binary file not shown.
22.8 KB
Binary file not shown.
90 KB
Binary file not shown.
90.3 KB
Binary file not shown.
56.6 KB
Binary file not shown.
13.7 KB
Binary file not shown.
9.3 KB
Binary file not shown.
17.4 KB
Binary file not shown.
22 KB
Binary file not shown.

0 commit comments

Comments
 (0)