# SPDX-FileCopyrightText: 2019 Noe Ruiz for Adafruit Industries # # SPDX-License-Identifier: MIT """ A CircuitPython 'multimedia' dial demo Uses a ItsyBitsy M0 + Rotary Encoder -> HID keyboard out with neopixel ring """ import time import board from digitalio import DigitalInOut, Direction, Pull from adafruit_hid.keyboard import Keyboard from adafruit_hid.keycode import Keycode from adafruit_hid.consumer_control import ConsumerControl from adafruit_hid.consumer_control_code import ConsumerControlCode import neopixel import usb_hid DOT_COLOR = 0xFF0000 # set to your favorite webhex color PRESSED_DOT_COLOR = 0x008080 # set to your second-favorite color LIT_TIMEOUT = 15 # after n seconds, turn off ring # NeoPixel LED ring on pin D1 # Ring code will auto-adjust if not 16 so change to any value! ring = neopixel.NeoPixel(board.D5, 16, brightness=0.2) dot_location = 0 # what dot is currently lit # Encoder button is a digital input with pullup on D9 button = DigitalInOut(board.D9) button.direction = Direction.INPUT button.pull = Pull.UP # Rotary encoder inputs with pullup on D10 & D11 rot_a = DigitalInOut(board.D10) rot_a.direction = Direction.INPUT rot_a.pull = Pull.UP rot_b = DigitalInOut(board.D11) rot_b.direction = Direction.INPUT rot_b.pull = Pull.UP # Used to do HID output, see below kbd = Keyboard(usb_hid.devices) consumer_control = ConsumerControl(usb_hid.devices) # time keeper, so we know when to turn off the LED timestamp = time.monotonic() ######################### MAIN LOOP ############################## # the counter counts up and down, it can roll over! 16-bit value encoder_counter = 0 # direction tells you the last tick which way it went encoder_direction = 0 # constants to help us track what edge is what A_POSITION = 0 B_POSITION = 1 UNKNOWN_POSITION = -1 # initial state so we know if something went wrong rising_edge = falling_edge = UNKNOWN_POSITION # get initial/prev state and store at beginning last_button = button.value rotary_prev_state = [rot_a.value, rot_b.value] while True: # reset encoder and wait for the next turn encoder_direction = 0 # take a 'snapshot' of the rotary encoder state at this time rotary_curr_state = [rot_a.value, rot_b.value] if rotary_curr_state != rotary_prev_state: #print("Changed") if rotary_prev_state == [True, True]: # we caught the first falling edge! if not rotary_curr_state[A_POSITION]: #print("Falling A") falling_edge = A_POSITION elif not rotary_curr_state[B_POSITION]: #print("Falling B") falling_edge = B_POSITION else: # uhh something went deeply wrong, lets start over continue if rotary_curr_state == [True, True]: # Ok we hit the final rising edge if not rotary_prev_state[B_POSITION]: rising_edge = B_POSITION # print("Rising B") elif not rotary_prev_state[A_POSITION]: rising_edge = A_POSITION # print("Rising A") else: # uhh something went deeply wrong, lets start over continue # check first and last edge if (rising_edge == A_POSITION) and (falling_edge == B_POSITION): encoder_counter -= 1 encoder_direction = -1 print("%d dec" % encoder_counter) elif (rising_edge == B_POSITION) and (falling_edge == A_POSITION): encoder_counter += 1 encoder_direction = 1 print("%d inc" % encoder_counter) else: # (shrug) something didn't work out, oh well! encoder_direction = 0 # reset our edge tracking rising_edge = falling_edge = UNKNOWN_POSITION rotary_prev_state = rotary_curr_state # Check if rotary encoder went up if encoder_direction == 1: consumer_control.send(ConsumerControlCode.VOLUME_DECREMENT) #Turn Down Volume # kbd.press(Keycode.LEFT_ARROW) # kbd.release_all() # Check if rotary encoder went down if encoder_direction == -1: consumer_control.send(ConsumerControlCode.VOLUME_INCREMENT) #Turn Up Volume # kbd.press(Keycode.RIGHT_ARROW) # kbd.release_all() # Button was 'just pressed' if (not button.value) and last_button: print("Button pressed!") kbd.press(Keycode.SPACE) #Keycode for spacebar kbd.release_all() ring[dot_location] = PRESSED_DOT_COLOR # show it was pressed on ring timestamp = time.monotonic() # something happened! elif button.value and (not last_button): print("Button Released!") # kbd.press(Keycode.SHIFT, Keycode.SIX) # kbd.release_all() ring[dot_location] = DOT_COLOR # show it was released on ring timestamp = time.monotonic() # something happened! last_button = button.value if encoder_direction != 0: timestamp = time.monotonic() # something happened! # spin neopixel LED around! previous_location = dot_location dot_location += encoder_direction # move dot in the direction dot_location += len(ring) # in case we moved negative, wrap around dot_location %= len(ring) if button.value: ring[dot_location] = DOT_COLOR # turn on new dot else: ring[dot_location] = PRESSED_DOT_COLOR # turn on new dot ring[previous_location] = 0 # turn off previous dot if time.monotonic() > timestamp + LIT_TIMEOUT: ring[dot_location] = 0 # turn off ring light temporarily