Skip to content

Commit c3bc68c

Browse files
authored
Merge pull request adafruit#1485 from FoamyGuy/add_touch_deck
Add touch deck
2 parents 9fc3e0c + 872f7ba commit c3bc68c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+525
-0
lines changed

CircuitPython_Touch_Deck/code.py

+315
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
# SPDX-FileCopyrightText: 2020 Tim C, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
"""
5+
This version runs on Feather RP2040 with a 3.5" FeatherWing
6+
"""
7+
8+
import time
9+
import displayio
10+
import terminalio
11+
from adafruit_display_text import bitmap_label
12+
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
13+
from adafruit_displayio_layout.widgets.icon_widget import IconWidget
14+
from adafruit_featherwing import tft_featherwing_35
15+
import usb_hid
16+
from adafruit_hid.keyboard import Keyboard
17+
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
18+
from adafruit_hid.consumer_control import ConsumerControl
19+
from touch_deck_layers import (
20+
touch_deck_config,
21+
KEY,
22+
STRING,
23+
MEDIA,
24+
KEY_PRESS,
25+
KEY_RELEASE,
26+
)
27+
28+
29+
# seems to help the touchscreen not get stuck with chip not found
30+
time.sleep(3)
31+
32+
# display and touchscreen initialization
33+
displayio.release_displays()
34+
tft_featherwing = tft_featherwing_35.TFTFeatherWing35()
35+
display = tft_featherwing.display
36+
touchscreen = tft_featherwing.touchscreen
37+
38+
# HID setup
39+
kbd = Keyboard(usb_hid.devices)
40+
cc = ConsumerControl(usb_hid.devices)
41+
kbd_layout = KeyboardLayoutUS(kbd)
42+
43+
# variables to envorce timout between icon presses
44+
COOLDOWN_TIME = 0.5
45+
LAST_PRESS_TIME = -1
46+
47+
# 'mock' icon indexes for the layer buttons
48+
# used for debouncing
49+
PREV_LAYER_INDEX = -1
50+
NEXT_LAYER_INDEX = -2
51+
HOME_LAYER_INDEX = -3
52+
53+
# start on first layer
54+
current_layer = 0
55+
56+
# Make the main_group to hold everything
57+
main_group = displayio.Group(max_size=10)
58+
display.show(main_group)
59+
60+
# loading screen
61+
loading_group = displayio.Group()
62+
63+
# black background, screen size minus side buttons
64+
loading_background = displayio.Bitmap(
65+
(display.width - 40) // 20, display.height // 20, 1
66+
)
67+
loading_palette = displayio.Palette(1)
68+
loading_palette[0] = 0x0
69+
70+
# scaled group to match screen size minus side buttons
71+
loading_background_scale_group = displayio.Group(scale=20)
72+
loading_background_tilegrid = displayio.TileGrid(
73+
loading_background, pixel_shader=loading_palette
74+
)
75+
loading_background_scale_group.append(loading_background_tilegrid)
76+
77+
# loading screen label
78+
loading_label = bitmap_label.Label(terminalio.FONT, text="Loading...", scale=3)
79+
loading_label.anchor_point = (0.5, 0.5)
80+
loading_label.anchored_position = (display.width // 2, display.height // 2)
81+
82+
# append background and label to the group
83+
loading_group.append(loading_background_scale_group)
84+
loading_group.append(loading_label)
85+
86+
# GridLayout to hold the icons
87+
# size and location can be adjusted to fit
88+
# different sized screens.
89+
layout = GridLayout(
90+
x=20,
91+
y=20,
92+
width=420,
93+
height=290,
94+
grid_size=(4, 3),
95+
cell_padding=6,
96+
max_size=20,
97+
)
98+
99+
# list that holds the IconWidget objects for each icon.
100+
_icons = []
101+
102+
# list that holds indexes of currently pressed icons and layer buttons
103+
# used for debouncing
104+
_pressed_icons = []
105+
106+
# layer label at the top of the screen
107+
layer_label = bitmap_label.Label(terminalio.FONT)
108+
layer_label.anchor_point = (0.5, 0.0)
109+
layer_label.anchored_position = (display.width // 2, 4)
110+
main_group.append(layer_label)
111+
112+
# right side layer buttons
113+
next_layer_btn = IconWidget("", "touch_deck_icons/layer_next.bmp", on_disk=True)
114+
next_layer_btn.x = display.width - 40
115+
next_layer_btn.y = display.height - 100
116+
next_layer_btn.resize = (40, 100)
117+
main_group.append(next_layer_btn)
118+
119+
prev_layer_btn = IconWidget("", "touch_deck_icons/layer_prev.bmp", on_disk=True)
120+
prev_layer_btn.x = display.width - 40
121+
prev_layer_btn.y = 110
122+
prev_layer_btn.resize = (40, 100)
123+
main_group.append(prev_layer_btn)
124+
125+
home_layer_btn = IconWidget("", "touch_deck_icons/layer_home.bmp", on_disk=True)
126+
home_layer_btn.x = display.width - 40
127+
home_layer_btn.y = 0
128+
home_layer_btn.resize = (40, 100)
129+
main_group.append(home_layer_btn)
130+
131+
132+
# helper method to laod icons for an index by its index in the
133+
# list of layers
134+
def load_layer(layer_index):
135+
136+
# show the loading screen
137+
main_group.append(loading_group)
138+
time.sleep(0.05)
139+
140+
# resets icon lists to empty
141+
global _icons
142+
_icons = []
143+
layout._cell_content_list = []
144+
145+
# remove previous layer icons from the layout
146+
while len(layout) > 0:
147+
layout.pop()
148+
149+
# set the layer labed at the top of the screen
150+
layer_label.text = touch_deck_config["layers"][layer_index]["name"]
151+
152+
# loop over each shortcut and it's index
153+
for i, shortcut in enumerate(touch_deck_config["layers"][layer_index]["shortcuts"]):
154+
# create an icon for the current shortcut
155+
_new_icon = IconWidget(shortcut["label"], shortcut["icon"], on_disk=True)
156+
157+
# add it to the list of icons
158+
_icons.append(_new_icon)
159+
160+
# add it to the grid layout
161+
# calculate it's position from the index
162+
layout.add_content(_new_icon, grid_position=(i % 4, i // 4), cell_size=(1, 1))
163+
164+
# hide the loading screen
165+
time.sleep(0.05)
166+
main_group.pop()
167+
168+
169+
# append the grid layout to the main_group
170+
# so it gets shown on the display
171+
main_group.append(layout)
172+
173+
# load the first layer to start
174+
load_layer(current_layer)
175+
176+
# main loop
177+
while True:
178+
if touchscreen.touched:
179+
# loop over all data in touchscreen buffer
180+
while not touchscreen.buffer_empty:
181+
touches = touchscreen.touches
182+
# loop over all points touched
183+
for point in touches:
184+
if point:
185+
# current time, used for timeout between icon presses
186+
_now = time.monotonic()
187+
188+
# if the timeout has passed
189+
if _now - LAST_PRESS_TIME > COOLDOWN_TIME:
190+
# print(point)
191+
192+
# map the observed minimum and maximum touch values
193+
# to the screen size
194+
y = point["y"] - 250
195+
x = 4096 - point["x"] - 250
196+
y = y * display.width // (3820 - 250)
197+
x = x * display.height // (3820 - 250)
198+
199+
# touch data is 90 degrees rotated
200+
# flip x, and y here to account for that
201+
p = (y, x)
202+
# print(p)
203+
204+
# Next layer button pressed
205+
if (
206+
next_layer_btn.contains(p)
207+
and NEXT_LAYER_INDEX not in _pressed_icons
208+
):
209+
210+
# increment layer
211+
current_layer += 1
212+
# wrap back to beginning from end
213+
if current_layer >= len(touch_deck_config["layers"]):
214+
current_layer = 0
215+
# load the new layer
216+
load_layer(current_layer)
217+
218+
# save current time to check for timeout
219+
LAST_PRESS_TIME = _now
220+
221+
# append this index to pressed icons for debouncing
222+
_pressed_icons.append(NEXT_LAYER_INDEX)
223+
224+
# home layer button pressed
225+
if (
226+
home_layer_btn.contains(p)
227+
and HOME_LAYER_INDEX not in _pressed_icons
228+
):
229+
# 0 index is home layer
230+
current_layer = 0
231+
# load the home layer
232+
load_layer(current_layer)
233+
234+
# save current time to check for timeout
235+
LAST_PRESS_TIME = _now
236+
237+
# append this index to pressed icons for debouncing
238+
_pressed_icons.append(HOME_LAYER_INDEX)
239+
240+
# Previous layer button pressed
241+
if (
242+
prev_layer_btn.contains(p)
243+
and PREV_LAYER_INDEX not in _pressed_icons
244+
):
245+
246+
# decrement layer
247+
current_layer -= 1
248+
# wrap back to end from beginning
249+
if current_layer < 0:
250+
current_layer = len(touch_deck_config["layers"]) - 1
251+
252+
# load the new layer
253+
load_layer(current_layer)
254+
255+
# save current time to check for timeout
256+
LAST_PRESS_TIME = _now
257+
258+
# append this index to pressed icons for debouncing
259+
_pressed_icons.append(PREV_LAYER_INDEX)
260+
261+
# loop over current layer icons and their indexes
262+
for index, icon_shortcut in enumerate(_icons):
263+
# if this icon was pressed
264+
if icon_shortcut.contains(p):
265+
# debounce logic, check that it wasn't already pressed
266+
if index not in _pressed_icons:
267+
# print("pressed {}".format(index))
268+
269+
# get actions for this icon from config object
270+
_cur_actions = touch_deck_config["layers"][
271+
current_layer
272+
]["shortcuts"][index]["actions"]
273+
274+
# tuple means it's a single action
275+
if isinstance(_cur_actions, tuple):
276+
# put it in a list by itself
277+
_cur_actions = [_cur_actions]
278+
279+
# loop over the actions
280+
for _action in _cur_actions:
281+
# HID keyboard keys
282+
if _action[0] == KEY:
283+
kbd.press(*_action[1])
284+
kbd.release(*_action[1])
285+
286+
# String to write from layout
287+
elif _action[0] == STRING:
288+
kbd_layout.write(_action[1])
289+
290+
# Consumer control code
291+
elif _action[0] == MEDIA:
292+
cc.send(_action[1])
293+
294+
# Key press
295+
elif _action[0] == KEY_PRESS:
296+
kbd.press(*_action[1])
297+
298+
# Key release
299+
elif _action[0] == KEY_RELEASE:
300+
kbd.release(*_action[1])
301+
302+
# if there are multiple actions
303+
if len(_cur_actions) > 1:
304+
# small sleep to make sure
305+
# OS can respond to previous action
306+
time.sleep(0.2)
307+
308+
# save current time to check for timeout
309+
LAST_PRESS_TIME = _now
310+
# append this index to pressed icons for debouncing
311+
_pressed_icons.append(index)
312+
else: # screen not touched
313+
314+
# empty the pressed icons list
315+
_pressed_icons.clear()

CircuitPython_Touch_Deck/readme.md

+2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)