Skip to content

Commit 05c57a9

Browse files
authored
Merge pull request #3041 from adafruit/mother_demo
mother of all demos code
2 parents 50d5fe5 + c9f04bd commit 05c57a9

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

Mother_Of_All_Demos_Keyset/code.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import time
6+
import board
7+
import keypad
8+
import supervisor
9+
import usb_hid
10+
from adafruit_hid.keyboard import Keyboard
11+
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
12+
13+
# Dictionary of macros for single keys and combinations
14+
macros = {
15+
# Single key macros
16+
(0,): "good",
17+
(1,): "great",
18+
(2,): "nice",
19+
(3,): "awesome",
20+
(4,): "cool",
21+
22+
# Combination macros
23+
(0, 2, 4): "looks good to me",
24+
(0, 2): "be right back",
25+
(2, 4): "see you soon",
26+
(1, 3): "sounds good",
27+
}
28+
29+
KEY_PINS = (
30+
board.A1,
31+
board.A2,
32+
board.A3,
33+
board.MISO,
34+
board.MOSI,
35+
)
36+
37+
keys = keypad.Keys(
38+
KEY_PINS,
39+
value_when_pressed=False,
40+
pull=True,
41+
interval=0.01,
42+
max_events=64,
43+
debounce_threshold=3
44+
)
45+
46+
keyboard = Keyboard(usb_hid.devices)
47+
keyboard_layout = KeyboardLayoutUS(keyboard)
48+
49+
# need to wait longer for 3 key combos
50+
LARGER_COMBOS = {
51+
(0, 2): (0, 2, 4),
52+
(2, 4): (0, 2, 4),
53+
}
54+
55+
# How long to wait for possible additional keys in a combo (ms)
56+
COMBO_WAIT_TIME = 150 # Wait 150ms to see if more keys are coming
57+
58+
# How long to wait for a single key before executing (ms)
59+
SINGLE_KEY_TIMEOUT_MS = 80
60+
61+
# Minimum time between macro executions (ms)
62+
MACRO_COOLDOWN_MS = 300
63+
64+
# Store the current state of all keys
65+
key_states = {i: False for i in range(len(KEY_PINS))}
66+
67+
# Create a reusable Event object to avoid memory allocations
68+
reusable_event = keypad.Event()
69+
70+
# Track timing and state
71+
last_macro_time = 0
72+
key_combo_start_time = 0
73+
waiting_for_combo = False
74+
last_executed_combo = None
75+
76+
while True:
77+
# Process all events in the queue
78+
keys_changed = False
79+
80+
while keys.events:
81+
if keys.events.get_into(reusable_event):
82+
# Check if key state actually changed
83+
old_state = key_states[reusable_event.key_number]
84+
key_states[reusable_event.key_number] = reusable_event.pressed
85+
86+
if old_state != reusable_event.pressed:
87+
print(f"Key {reusable_event.key_number} " +
88+
f"{'pressed' if reusable_event.pressed else 'released'}")
89+
keys_changed = True
90+
91+
# Get currently pressed keys as a sorted tuple
92+
current_pressed_keys = tuple(sorted(k for k, v in key_states.items() if v))
93+
current_time = supervisor.ticks_ms()
94+
95+
# When all keys are released, reset tracking
96+
if not current_pressed_keys:
97+
waiting_for_combo = False
98+
last_executed_combo = None
99+
time.sleep(0.01)
100+
continue
101+
102+
# If this is a new key pattern or we just started
103+
if keys_changed:
104+
# If we weren't tracking before, start now
105+
if not waiting_for_combo:
106+
key_combo_start_time = current_time
107+
waiting_for_combo = True
108+
109+
# If the pressed keys have changed, update the timer
110+
if current_pressed_keys != last_executed_combo:
111+
key_combo_start_time = current_time
112+
113+
# Skip if we've already executed this exact combination
114+
if current_pressed_keys == last_executed_combo:
115+
time.sleep(0.01)
116+
continue
117+
118+
# Determine if we should execute a macro now
119+
should_execute = False
120+
wait_more = False
121+
122+
# If this is a potential part of a larger combo, wait longer
123+
if current_pressed_keys in LARGER_COMBOS:
124+
# Only wait if we've been waiting less than the combo wait time
125+
if (current_time - key_combo_start_time) < COMBO_WAIT_TIME:
126+
wait_more = True
127+
else:
128+
# We've waited long enough, go ahead and execute
129+
should_execute = True
130+
# Immediate execution for multi-key combinations that aren't potential parts of larger combos
131+
elif len(current_pressed_keys) > 1:
132+
should_execute = True
133+
# Execute single key after timeout
134+
elif waiting_for_combo and (current_time - key_combo_start_time) >= SINGLE_KEY_TIMEOUT_MS:
135+
should_execute = True
136+
137+
# If we need to wait more, skip to the next iteration
138+
if wait_more:
139+
time.sleep(0.01)
140+
continue
141+
142+
# Execute the macro if conditions are met
143+
if should_execute and current_pressed_keys in macros:
144+
# Only execute if cooldown period has passed
145+
if current_time - last_macro_time >= MACRO_COOLDOWN_MS:
146+
print(f"MACRO: {macros[current_pressed_keys]}")
147+
keyboard_layout.write(macros[current_pressed_keys])
148+
last_macro_time = current_time
149+
last_executed_combo = current_pressed_keys
150+
151+
time.sleep(0.01)

0 commit comments

Comments
 (0)