Once you've finished setting up your Feather nRF52840 with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries # # SPDX-License-Identifier: MIT import board from digitalio import DigitalInOut, Direction, Pull import rotaryio from adafruit_debouncer import Button from adafruit_hid.keyboard import Keyboard from adafruit_hid.keycode import Keycode import adafruit_ble from adafruit_ble.advertising import Advertisement from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.standard.hid import HIDService # pin assignments for the rotary encoder ENCA = board.SDA ENCB = board.SCL COMA = board.D5 CENTER = board.D6 RIGHT = board.D9 UP = board.D10 LEFT = board.D11 DOWN = board.D12 COMB = board.D13 # mode slide switch pin SWITCH = board.A1 # Rotary encoder setup encoder = rotaryio.IncrementalEncoder(ENCA, ENCB) last_position = 0 # setting the COMA and COMB pins to LOW aka GND com_a = DigitalInOut(COMA) com_a.switch_to_output() com_a = False com_b = DigitalInOut(COMB) com_b.switch_to_output() com_b = False # mode switch setup SWITCH = DigitalInOut(board.A1) SWITCH.direction = Direction.INPUT SWITCH.pull = Pull.UP # encoder button pins enc_buttons = ( CENTER, UP, LEFT, DOWN, RIGHT, ) # array for the encoder buttons inputs = [] # setting the encoder buttons as inputs for enc in enc_buttons: enc_button = DigitalInOut(enc) enc_button.pull = Pull.UP # adding to the inputs array with the Button Class of the Debouncer lib inputs.append(Button(enc_button)) # streaming mode keycodes CHILL_CODES = ( Keycode.SPACE, Keycode.F, Keycode.LEFT_ARROW, Keycode.M, Keycode.RIGHT_ARROW, ) # doom mode keycodes DOOM_CODES = ( Keycode.CONTROL, Keycode.UP_ARROW, Keycode.LEFT_ARROW, Keycode.DOWN_ARROW, Keycode.RIGHT_ARROW, ) # streaming state chill = True # doom state doom = False # BLE HID setup hid = HIDService() advertisement = ProvideServicesAdvertisement(hid) advertisement.appearance = 961 scan_response = Advertisement() scan_response.complete_name = "CircuitPython HID" # BLE instance ble = adafruit_ble.BLERadio() # keyboard HID setup kbd = Keyboard(hid.devices) # BLE advertisement if not ble.connected: print("advertising") ble.start_advertising(advertisement, scan_response) else: print("connected") print(ble.connections) while True: # check for BLE connection while not ble.connected: pass # while BLE connected while ble.connected: # mode switch # selects whether to be in streaming mode or doom mode # affects the keycodes assigned to the encoder's inputs if not SWITCH.value: chill = False doom = True if SWITCH.value: chill = True doom = False # rotary encoder position tracking position = encoder.position # if the encoder is turned to the right if position > last_position: # if in streaming mode if chill: # send UP arrow for volume kbd.send(Keycode.UP_ARROW) # if in doom mode if doom: # send period for right strafe kbd.send(Keycode.PERIOD) # reset encoder position last_position = position # if the encoder is turned to the left if position < last_position: # if in streaming mode if chill: # send DOWN arrow for volume kbd.send(Keycode.DOWN_ARROW) # if in doom mode if doom: # send comma for left strafe kbd.send(Keycode.COMMA) # reset encoder position last_position = position # for loop for keycodes for i in range(5): # update state of the buttons inputs[i].update() # if you press the center button for a long press if inputs[0].long_press: # sends space key # used in Doom for use/open kbd.send(Keycode.SPACE) # if a press is detected... if inputs[i].pressed: # if in streaming mode if chill: # send the streaming keycodes kbd.press(CHILL_CODES[i]) # if in doom mode if doom: # send the doom keycodes kbd.press(DOOM_CODES[i]) # if a button is released... if inputs[i].released: # if in streaming mode if chill: # release the streaming keycodes kbd.release(CHILL_CODES[i]) # if in doom mode if doom: # release the doom keycodes kbd.release(DOOM_CODES[i]) # if BLE disconnects, begin advertising again ble.start_advertising(advertisement)
Upload the Code and Libraries to the Feather nRF52840
After downloading the Project Bundle, plug your Feather nRF52840 into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the Feather nRF52840's CIRCUITPY drive.
- lib folder
- code.py
Your Feather nRF52840 CIRCUITPY drive should look like this after copying the lib folder and the code.py file.
The rotary encoder's five buttons are setup as inputs using the Button
class of the adafruit_debouncer
library. The adafruit_debouncer
library is being used so that the long_press
property can be used in the loop.
# encoder button pins enc_buttons = ( CENTER, UP, LEFT, DOWN, RIGHT, ) # array for the encoder buttons inputs = [] # setting the encoder buttons as inputs for enc in enc_buttons: enc_button = DigitalInOut(enc) enc_button.pull = Pull.UP # adding to the inputs array with the Button Class of the Debouncer lib inputs.append(Button(enc_button))
Keycodes
There are two sets of keycodes depending on the mode of the remote. The keycodes in CHILL_CODES
are for streaming media, such as Netflix or YouTube. The keycodes in DOOM_CODES
are for playing classic Doom. The keycode indexes align with the encoder buttons array's indexes. For example, the CENTER
button will send the SPACE
or CONTROL
keycode, depending on the mode.
# streaming mode keycodes CHILL_CODES = ( Keycode.SPACE, Keycode.F, Keycode.LEFT_ARROW, Keycode.M, Keycode.RIGHT_ARROW, ) # doom mode keycodes DOOM_CODES = ( Keycode.CONTROL, Keycode.UP_ARROW, Keycode.LEFT_ARROW, Keycode.DOWN_ARROW, Keycode.RIGHT_ARROW, )
The Loop
Once a BLE connection has been established, the loop checks the value of SWITCH
to determine the mode of the controller.
while ble.connected: # mode switch # selects whether to be in streaming mode or doom mode # affects the keycodes assigned to the encoder's inputs if not SWITCH.value: chill = False doom = True if SWITCH.value: chill = True doom = False
Encoder Position
The encoder also sends a keycode depending on the mode. The loop checks to see if the encoder is turning to the left or right. Depending on the direction, a keycode will be sent.
In streaming mode, the encoder controls volume. In Doom, it controls left and right strafe.
# rotary encoder position tracking position = encoder.position # if the encoder is turned to the right if position > last_position: # if in streaming mode if chill: # send UP arrow for volume kbd.send(Keycode.UP_ARROW) # if in doom mode if doom: # send period for right strafe kbd.send(Keycode.PERIOD) # reset encoder position last_position = position # if the encoder is turned to the left if position < last_position: # if in streaming mode if chill: # send DOWN arrow for volume kbd.send(Keycode.DOWN_ARROW) # if in doom mode if doom: # send comma for left strafe kbd.send(Keycode.COMMA) # reset encoder position last_position = position
Sending Keycodes
The adafruit_debouncer
library checks the status of the button inputs using update()
. A for
statement is used to iterate through all five buttons. If the center button receives a long_press
, then the SPACE
keycode is sent. This is used in Doom for the use/open control.
Otherwise, the for
statement checks to see if a button has been pressed
or released
. If a button is pressed
, then the corresponding keycode is pressed depending on the mode. If a button is released
, then the corresponding keycode is released depending on the mode.
# for loop for keycodes for i in range(5): # update state of the buttons inputs[i].update() # if you press the center button for a long press if inputs[0].long_press: # sends space key # used in Doom for use/open kbd.send(Keycode.SPACE) # if a press is detected... if inputs[i].pressed: # if in streaming mode if chill: # send the streaming keycodes kbd.press(CHILL_CODES[i]) # if in doom mode if doom: # send the doom keycodes kbd.press(DOOM_CODES[i]) # if a button is released... if inputs[i].released: # if in streaming mode if chill: # release the streaming keycodes kbd.release(CHILL_CODES[i]) # if in doom mode if doom: # release the doom keycodes kbd.release(DOOM_CODES[i])
Text editor powered by tinymce.