Merge pull request #3041 from adafruit/mother_demo
mother of all demos code
This commit is contained in:
commit
05c57a9690
1 changed files with 151 additions and 0 deletions
151
Mother_Of_All_Demos_Keyset/code.py
Normal file
151
Mother_Of_All_Demos_Keyset/code.py
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
import keypad
|
||||||
|
import supervisor
|
||||||
|
import usb_hid
|
||||||
|
from adafruit_hid.keyboard import Keyboard
|
||||||
|
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
|
||||||
|
|
||||||
|
# Dictionary of macros for single keys and combinations
|
||||||
|
macros = {
|
||||||
|
# Single key macros
|
||||||
|
(0,): "good",
|
||||||
|
(1,): "great",
|
||||||
|
(2,): "nice",
|
||||||
|
(3,): "awesome",
|
||||||
|
(4,): "cool",
|
||||||
|
|
||||||
|
# Combination macros
|
||||||
|
(0, 2, 4): "looks good to me",
|
||||||
|
(0, 2): "be right back",
|
||||||
|
(2, 4): "see you soon",
|
||||||
|
(1, 3): "sounds good",
|
||||||
|
}
|
||||||
|
|
||||||
|
KEY_PINS = (
|
||||||
|
board.A1,
|
||||||
|
board.A2,
|
||||||
|
board.A3,
|
||||||
|
board.MISO,
|
||||||
|
board.MOSI,
|
||||||
|
)
|
||||||
|
|
||||||
|
keys = keypad.Keys(
|
||||||
|
KEY_PINS,
|
||||||
|
value_when_pressed=False,
|
||||||
|
pull=True,
|
||||||
|
interval=0.01,
|
||||||
|
max_events=64,
|
||||||
|
debounce_threshold=3
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard = Keyboard(usb_hid.devices)
|
||||||
|
keyboard_layout = KeyboardLayoutUS(keyboard)
|
||||||
|
|
||||||
|
# need to wait longer for 3 key combos
|
||||||
|
LARGER_COMBOS = {
|
||||||
|
(0, 2): (0, 2, 4),
|
||||||
|
(2, 4): (0, 2, 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
# How long to wait for possible additional keys in a combo (ms)
|
||||||
|
COMBO_WAIT_TIME = 150 # Wait 150ms to see if more keys are coming
|
||||||
|
|
||||||
|
# How long to wait for a single key before executing (ms)
|
||||||
|
SINGLE_KEY_TIMEOUT_MS = 80
|
||||||
|
|
||||||
|
# Minimum time between macro executions (ms)
|
||||||
|
MACRO_COOLDOWN_MS = 300
|
||||||
|
|
||||||
|
# Store the current state of all keys
|
||||||
|
key_states = {i: False for i in range(len(KEY_PINS))}
|
||||||
|
|
||||||
|
# Create a reusable Event object to avoid memory allocations
|
||||||
|
reusable_event = keypad.Event()
|
||||||
|
|
||||||
|
# Track timing and state
|
||||||
|
last_macro_time = 0
|
||||||
|
key_combo_start_time = 0
|
||||||
|
waiting_for_combo = False
|
||||||
|
last_executed_combo = None
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Process all events in the queue
|
||||||
|
keys_changed = False
|
||||||
|
|
||||||
|
while keys.events:
|
||||||
|
if keys.events.get_into(reusable_event):
|
||||||
|
# Check if key state actually changed
|
||||||
|
old_state = key_states[reusable_event.key_number]
|
||||||
|
key_states[reusable_event.key_number] = reusable_event.pressed
|
||||||
|
|
||||||
|
if old_state != reusable_event.pressed:
|
||||||
|
print(f"Key {reusable_event.key_number} " +
|
||||||
|
f"{'pressed' if reusable_event.pressed else 'released'}")
|
||||||
|
keys_changed = True
|
||||||
|
|
||||||
|
# Get currently pressed keys as a sorted tuple
|
||||||
|
current_pressed_keys = tuple(sorted(k for k, v in key_states.items() if v))
|
||||||
|
current_time = supervisor.ticks_ms()
|
||||||
|
|
||||||
|
# When all keys are released, reset tracking
|
||||||
|
if not current_pressed_keys:
|
||||||
|
waiting_for_combo = False
|
||||||
|
last_executed_combo = None
|
||||||
|
time.sleep(0.01)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If this is a new key pattern or we just started
|
||||||
|
if keys_changed:
|
||||||
|
# If we weren't tracking before, start now
|
||||||
|
if not waiting_for_combo:
|
||||||
|
key_combo_start_time = current_time
|
||||||
|
waiting_for_combo = True
|
||||||
|
|
||||||
|
# If the pressed keys have changed, update the timer
|
||||||
|
if current_pressed_keys != last_executed_combo:
|
||||||
|
key_combo_start_time = current_time
|
||||||
|
|
||||||
|
# Skip if we've already executed this exact combination
|
||||||
|
if current_pressed_keys == last_executed_combo:
|
||||||
|
time.sleep(0.01)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Determine if we should execute a macro now
|
||||||
|
should_execute = False
|
||||||
|
wait_more = False
|
||||||
|
|
||||||
|
# If this is a potential part of a larger combo, wait longer
|
||||||
|
if current_pressed_keys in LARGER_COMBOS:
|
||||||
|
# Only wait if we've been waiting less than the combo wait time
|
||||||
|
if (current_time - key_combo_start_time) < COMBO_WAIT_TIME:
|
||||||
|
wait_more = True
|
||||||
|
else:
|
||||||
|
# We've waited long enough, go ahead and execute
|
||||||
|
should_execute = True
|
||||||
|
# Immediate execution for multi-key combinations that aren't potential parts of larger combos
|
||||||
|
elif len(current_pressed_keys) > 1:
|
||||||
|
should_execute = True
|
||||||
|
# Execute single key after timeout
|
||||||
|
elif waiting_for_combo and (current_time - key_combo_start_time) >= SINGLE_KEY_TIMEOUT_MS:
|
||||||
|
should_execute = True
|
||||||
|
|
||||||
|
# If we need to wait more, skip to the next iteration
|
||||||
|
if wait_more:
|
||||||
|
time.sleep(0.01)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Execute the macro if conditions are met
|
||||||
|
if should_execute and current_pressed_keys in macros:
|
||||||
|
# Only execute if cooldown period has passed
|
||||||
|
if current_time - last_macro_time >= MACRO_COOLDOWN_MS:
|
||||||
|
print(f"MACRO: {macros[current_pressed_keys]}")
|
||||||
|
keyboard_layout.write(macros[current_pressed_keys])
|
||||||
|
last_macro_time = current_time
|
||||||
|
last_executed_combo = current_pressed_keys
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
||||||
Loading…
Reference in a new issue