Skip to content

Commit be21e26

Browse files
authored
Merge pull request #727 from adafruit/crank-usbhid
media dial code
2 parents b42bbcc + e79250e commit be21e26

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

Crank_USB_HID/code.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""
2+
A CircuitPython 'multimedia' dial demo
3+
Uses a ItsyBitsy M0 + Rotary Encoder -> HID keyboard out with neopixel ring
4+
"""
5+
6+
import time
7+
import board
8+
from digitalio import DigitalInOut, Direction, Pull
9+
from adafruit_hid.keyboard import Keyboard
10+
from adafruit_hid.keycode import Keycode
11+
from adafruit_hid.consumer_control import ConsumerControl
12+
from adafruit_hid.consumer_control_code import ConsumerControlCode
13+
import neopixel
14+
15+
DOT_COLOR = 0xFF0000 # set to your favorite webhex color
16+
PRESSED_DOT_COLOR = 0x008080 # set to your second-favorite color
17+
LIT_TIMEOUT = 15 # after n seconds, turn off ring
18+
19+
# NeoPixel LED ring on pin D1
20+
# Ring code will auto-adjust if not 16 so change to any value!
21+
ring = neopixel.NeoPixel(board.D5, 16, brightness=0.2)
22+
dot_location = 0 # what dot is currently lit
23+
24+
# Encoder button is a digital input with pullup on D9
25+
button = DigitalInOut(board.D9)
26+
button.direction = Direction.INPUT
27+
button.pull = Pull.UP
28+
29+
# Rotary encoder inputs with pullup on D10 & D11
30+
rot_a = DigitalInOut(board.D10)
31+
rot_a.direction = Direction.INPUT
32+
rot_a.pull = Pull.UP
33+
rot_b = DigitalInOut(board.D11)
34+
rot_b.direction = Direction.INPUT
35+
rot_b.pull = Pull.UP
36+
37+
# Used to do HID output, see below
38+
kbd = Keyboard()
39+
40+
# time keeper, so we know when to turn off the LED
41+
timestamp = time.monotonic()
42+
43+
######################### MAIN LOOP ##############################
44+
45+
# the counter counts up and down, it can roll over! 16-bit value
46+
encoder_counter = 0
47+
# direction tells you the last tick which way it went
48+
encoder_direction = 0
49+
50+
# constants to help us track what edge is what
51+
A_POSITION = 0
52+
B_POSITION = 1
53+
UNKNOWN_POSITION = -1 # initial state so we know if something went wrong
54+
55+
rising_edge = falling_edge = UNKNOWN_POSITION
56+
57+
# get initial/prev state and store at beginning
58+
last_button = button.value
59+
rotary_prev_state = [rot_a.value, rot_b.value]
60+
61+
while True:
62+
# reset encoder and wait for the next turn
63+
encoder_direction = 0
64+
65+
# take a 'snapshot' of the rotary encoder state at this time
66+
rotary_curr_state = [rot_a.value, rot_b.value]
67+
68+
if rotary_curr_state != rotary_prev_state:
69+
#print("Changed")
70+
if rotary_prev_state == [True, True]:
71+
# we caught the first falling edge!
72+
if not rotary_curr_state[A_POSITION]:
73+
#print("Falling A")
74+
falling_edge = A_POSITION
75+
elif not rotary_curr_state[B_POSITION]:
76+
#print("Falling B")
77+
falling_edge = B_POSITION
78+
else:
79+
# uhh something went deeply wrong, lets start over
80+
continue
81+
82+
if rotary_curr_state == [True, True]:
83+
# Ok we hit the final rising edge
84+
if not rotary_prev_state[B_POSITION]:
85+
rising_edge = B_POSITION
86+
# print("Rising B")
87+
elif not rotary_prev_state[A_POSITION]:
88+
rising_edge = A_POSITION
89+
# print("Rising A")
90+
else:
91+
# uhh something went deeply wrong, lets start over
92+
continue
93+
94+
# check first and last edge
95+
if (rising_edge == A_POSITION) and (falling_edge == B_POSITION):
96+
encoder_counter -= 1
97+
encoder_direction = -1
98+
print("%d dec" % encoder_counter)
99+
elif (rising_edge == B_POSITION) and (falling_edge == A_POSITION):
100+
encoder_counter += 1
101+
encoder_direction = 1
102+
print("%d inc" % encoder_counter)
103+
else:
104+
# (shrug) something didn't work out, oh well!
105+
encoder_direction = 0
106+
107+
# reset our edge tracking
108+
rising_edge = falling_edge = UNKNOWN_POSITION
109+
110+
rotary_prev_state = rotary_curr_state
111+
112+
# Check if rotary encoder went up
113+
if encoder_direction == 1:
114+
ConsumerControl().send(ConsumerControlCode.VOLUME_DECREMENT) #Turn Down Volume
115+
# kbd.press(Keycode.LEFT_ARROW)
116+
# kbd.release_all()
117+
# Check if rotary encoder went down
118+
if encoder_direction == -1:
119+
ConsumerControl().send(ConsumerControlCode.VOLUME_INCREMENT) #Turn Up Volume
120+
# kbd.press(Keycode.RIGHT_ARROW)
121+
# kbd.release_all()
122+
# Button was 'just pressed'
123+
if (not button.value) and last_button:
124+
print("Button pressed!")
125+
kbd.press(Keycode.SPACE) #Keycode for spacebar
126+
kbd.release_all()
127+
ring[dot_location] = PRESSED_DOT_COLOR # show it was pressed on ring
128+
timestamp = time.monotonic() # something happened!
129+
elif button.value and (not last_button):
130+
print("Button Released!")
131+
# kbd.press(Keycode.SHIFT, Keycode.SIX)
132+
# kbd.release_all()
133+
ring[dot_location] = DOT_COLOR # show it was released on ring
134+
timestamp = time.monotonic() # something happened!
135+
last_button = button.value
136+
137+
if encoder_direction != 0:
138+
timestamp = time.monotonic() # something happened!
139+
# spin neopixel LED around!
140+
previous_location = dot_location
141+
dot_location += encoder_direction # move dot in the direction
142+
dot_location += len(ring) # in case we moved negative, wrap around
143+
dot_location %= len(ring)
144+
if button.value:
145+
ring[dot_location] = DOT_COLOR # turn on new dot
146+
else:
147+
ring[dot_location] = PRESSED_DOT_COLOR # turn on new dot
148+
ring[previous_location] = 0 # turn off previous dot
149+
150+
if time.monotonic() > timestamp + LIT_TIMEOUT:
151+
ring[dot_location] = 0 # turn off ring light temporarily

0 commit comments

Comments
 (0)