Adafruit_Learning_System_Gu.../CPB_Volume_BLE/code.py
2022-02-23 13:49:59 -05:00

141 lines
3.9 KiB
Python

# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
A CircuitPython 'multimedia' dial demo
Uses a Circuit Playground Bluefruit + Rotary Encoder -> BLE out
Knob controls volume, push encoder for mute, CPB button A for Play/Pause
Once paired, bonding will auto re-connect devices
"""
import time
import digitalio
import board
import rotaryio
import neopixel
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode
import adafruit_ble
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_ble.services.standard.device_info import DeviceInfoService
ble = adafruit_ble.BLERadio()
ble.name = "Bluefruit-Volume-Control"
# Using default HID Descriptor.
hid = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
manufacturer="Adafruit Industries")
advertisement = ProvideServicesAdvertisement(hid)
cc = ConsumerControl(hid.devices)
FILL_COLOR = (0, 32, 32)
UNMUTED_COLOR = (0, 128, 128)
MUTED_COLOR = (128, 0, 0)
DISCONNECTED_COLOR = (40, 40, 0)
# NeoPixel LED ring
# Ring code will auto-adjust if not 16 so change to any value!
ring = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.05, auto_write = False)
ring.fill(DISCONNECTED_COLOR)
ring.show()
dot_location = 0 # what dot is currently lit
# CPB button for Play/Pause
button_A = digitalio.DigitalInOut(board.BUTTON_A)
button_A.switch_to_input(pull=digitalio.Pull.DOWN)
button_a_pressed = False # for debounce state
# Encoder button is a digital input with pullup on A1
# so button.value == False means pressed.
button = digitalio.DigitalInOut(board.A1)
button.pull = digitalio.Pull.UP
encoder = rotaryio.IncrementalEncoder(board.A2, board.A3)
last_pos = encoder.position
muted = False
command = None
# Disconnect if already connected, so that we pair properly.
if ble.connected:
for connection in ble.connections:
connection.disconnect()
def draw():
if not muted:
ring.fill(FILL_COLOR)
ring[dot_location] = UNMUTED_COLOR
else:
ring.fill(MUTED_COLOR)
ring.show()
advertising = False
connection_made = False
print("let's go!")
while True:
if not ble.connected:
ring.fill(DISCONNECTED_COLOR)
ring.show()
connection_made = False
if not advertising:
ble.start_advertising(advertisement)
advertising = True
continue
else:
if connection_made:
pass
else:
ring.fill(FILL_COLOR)
ring.show()
connection_made = True
advertising = False
pos = encoder.position
delta = pos - last_pos
last_pos = pos
direction = 0
if delta > 0:
command = ConsumerControlCode.VOLUME_INCREMENT
direction = -1
elif delta < 0:
command = ConsumerControlCode.VOLUME_DECREMENT
direction = 1
if direction:
muted = False
for _ in range(abs(delta)):
cc.send(command)
# spin neopixel LED around in the correct direction!
dot_location = (dot_location + direction) % len(ring)
draw()
if not button.value:
if not muted:
print("Muting")
cc.send(ConsumerControlCode.MUTE)
muted = True
else:
print("Unmuting")
cc.send(ConsumerControlCode.MUTE)
muted = False
draw()
while not button.value: # debounce
time.sleep(0.1)
if button_A.value and not button_a_pressed: # button is pushed
cc.send(ConsumerControlCode.PLAY_PAUSE)
print("Play/Pause")
button_a_pressed = True # state for debouncing
time.sleep(0.05)
if not button_A.value and button_a_pressed:
button_a_pressed = False
time.sleep(0.05)