Skip to content

Commit 2eda81f

Browse files
authored
Merge pull request #957 from kattni/cpb-neopixel-controller
Circuit Playground Bluefruit NeoPixel Controller
2 parents eb27ffa + 0e0d077 commit 2eda81f

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
Control code for Circuit Playground Bluefruit NeoPixel Animation and Color controller. To be used
3+
with another Circuit Playground Bluefruit running the receiver code.
4+
"""
5+
6+
import time
7+
8+
from adafruit_circuitplayground.bluefruit import cpb
9+
10+
from adafruit_ble import BLERadio
11+
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
12+
from adafruit_ble.services.nordic import UARTService
13+
from adafruit_bluefruit_connect.color_packet import ColorPacket
14+
from adafruit_bluefruit_connect.button_packet import ButtonPacket
15+
16+
17+
def scale(value):
18+
"""Scale a value from acceleration value range to 0-255 (RGB range)"""
19+
value = abs(value)
20+
value = max(min(19.6, value), 0)
21+
return int(value / 19.6 * 255)
22+
23+
24+
def send_packet(uart_connection_name, packet):
25+
"""Returns False if no longer connected."""
26+
try:
27+
uart_connection_name[UARTService].write(packet.to_bytes())
28+
except: # pylint: disable=bare-except
29+
try:
30+
uart_connection_name.disconnect()
31+
except: # pylint: disable=bare-except
32+
pass
33+
return False
34+
return True
35+
36+
37+
ble = BLERadio()
38+
39+
# Setup for preventing repeated button presses and tracking switch state
40+
button_a_pressed = False
41+
button_b_pressed = False
42+
last_switch_state = None
43+
44+
uart_connection = None
45+
# See if any existing connections are providing UARTService.
46+
if ble.connected:
47+
for connection in ble.connections:
48+
if UARTService in connection:
49+
uart_connection = connection
50+
break
51+
52+
while True:
53+
last_switch_state = None
54+
if not uart_connection or not uart_connection.connected: # If not connected...
55+
print("Scanning...")
56+
for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5): # Scan...
57+
if UARTService in adv.services: # If UARTService found...
58+
print("Found a UARTService advertisement.")
59+
uart_connection = ble.connect(adv) # Create a UART connection...
60+
break
61+
# Stop scanning whether or not we are connected.
62+
ble.stop_scan() # And stop scanning.
63+
while uart_connection and uart_connection.connected: # If connected...
64+
if cpb.button_a and not button_a_pressed: # If button A pressed...
65+
print("Button A pressed.")
66+
# Send a LEFT button packet.
67+
if not send_packet(uart_connection,
68+
ButtonPacket(ButtonPacket.LEFT, pressed=True)):
69+
uart_connection = None
70+
continue
71+
button_a_pressed = True # Set to True.
72+
time.sleep(0.05) # Debounce.
73+
if not cpb.button_a and button_a_pressed: # On button release...
74+
button_a_pressed = False # Set to False.
75+
time.sleep(0.05) # Debounce.
76+
if cpb.button_b and not button_b_pressed: # If button B pressed...
77+
print("Button B pressed.")
78+
# Send a RIGHT button packet.
79+
if not send_packet(uart_connection,
80+
ButtonPacket(ButtonPacket.RIGHT, pressed=True)):
81+
uart_connection = None
82+
continue
83+
button_b_pressed = True # Set to True.
84+
time.sleep(0.05) # Debounce.
85+
if not cpb.button_b and button_b_pressed: # On button release...
86+
button_b_pressed = False # Set to False.
87+
time.sleep(0.05) # Debounce.
88+
if cpb.switch is not last_switch_state: # If the switch state is changed...
89+
last_switch_state = cpb.switch # Set state to current switch state.
90+
print("Switch is to the", "left: LEDs off!" if cpb.switch else "right: LEDs on!")
91+
# Send a BUTTON_1 button packet.
92+
if not send_packet(uart_connection,
93+
ButtonPacket(ButtonPacket.BUTTON_1, pressed=cpb.switch)):
94+
uart_connection = None
95+
continue
96+
if cpb.switch: # If switch is to the left...
97+
cpb.pixels.fill((0, 0, 0)) # Turn off the LEDs.
98+
else: # Otherwise...
99+
r, g, b = map(scale, cpb.acceleration) # Map acceleration values to RGB values...
100+
color = (r, g, b) # Set color to current mapped RGB value...
101+
print("Color:", color)
102+
cpb.pixels.fill(color) # Fill controller LEDs with current color...
103+
if not send_packet(uart_connection, ColorPacket(color)): # And send a color packet.
104+
uart_connection = None
105+
continue
106+
time.sleep(0.1) # Delay to prevent sending packets too quickly.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""
2+
Receiver code for Circuit Playground Bluefruit NeoPixel Animation and Color controller. To be used
3+
with another Circuit Playground Bluefruit running the controller code.
4+
"""
5+
6+
import board
7+
import neopixel
8+
from adafruit_circuitplayground.bluefruit import cpb
9+
from adafruit_led_animation.animation import Blink, Comet, Sparkle, AnimationGroup,\
10+
AnimationSequence
11+
import adafruit_led_animation.color as color
12+
13+
from adafruit_ble import BLERadio
14+
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
15+
from adafruit_ble.services.nordic import UARTService
16+
17+
from adafruit_bluefruit_connect.packet import Packet
18+
from adafruit_bluefruit_connect.color_packet import ColorPacket
19+
from adafruit_bluefruit_connect.button_packet import ButtonPacket
20+
21+
# The number of NeoPixels in the externally attached strip
22+
# If using two strips connected to the same pin, count only one strip for this number!
23+
STRIP_PIXEL_NUMBER = 30
24+
25+
# Setup for blink animation
26+
BLINK_SPEED = 0.5 # Lower numbers increase the animation speed
27+
BLINK_INITIAL_COLOR = color.RED # Color before controller is connected
28+
29+
# Setup for comet animation
30+
COMET_SPEED = 0.03 # Lower numbers increase the animation speed
31+
COMET_INITIAL_COLOR = color.MAGENTA # Color before controller is connected
32+
CPB_COMET_TAIL_LENGTH = 5 # The length of the comet on the Circuit Playground Bluefruit
33+
STRIP_COMET_TAIL_LENGTH = 15 # The length of the comet on the NeoPixel strip
34+
CPB_COMET_BOUNCE = False # Set to True to make the comet "bounce" the opposite direction on CPB
35+
STRIP_COMET_BOUNCE = True # Set to False to stop comet from "bouncing" on NeoPixel strip
36+
37+
# Setup for sparkle animation
38+
SPARKLE_SPEED = 0.03 # Lower numbers increase the animation speed
39+
SPARKLE_INITIAL_COLOR = color.PURPLE # Color before controller is connected
40+
41+
# Create the NeoPixel strip
42+
strip_pixels = neopixel.NeoPixel(board.A1, STRIP_PIXEL_NUMBER, auto_write=False)
43+
44+
# Setup BLE connection
45+
ble = BLERadio()
46+
uart = UARTService()
47+
advertisement = ProvideServicesAdvertisement(uart)
48+
49+
# Setup animations
50+
animations = AnimationSequence(
51+
AnimationGroup(
52+
Blink(cpb.pixels, BLINK_SPEED, BLINK_INITIAL_COLOR),
53+
Blink(strip_pixels, BLINK_SPEED, BLINK_INITIAL_COLOR),
54+
sync=True
55+
),
56+
AnimationGroup(
57+
Comet(cpb.pixels, COMET_SPEED, COMET_INITIAL_COLOR, tail_length=CPB_COMET_TAIL_LENGTH,
58+
bounce=CPB_COMET_BOUNCE),
59+
Comet(strip_pixels, COMET_SPEED, COMET_INITIAL_COLOR, tail_length=STRIP_COMET_TAIL_LENGTH,
60+
bounce=STRIP_COMET_BOUNCE)
61+
),
62+
AnimationGroup(
63+
Sparkle(cpb.pixels, SPARKLE_SPEED, SPARKLE_INITIAL_COLOR),
64+
Sparkle(strip_pixels, SPARKLE_SPEED, SPARKLE_INITIAL_COLOR)
65+
),
66+
)
67+
68+
animation_color = None
69+
mode = 0
70+
blanked = False
71+
72+
while True:
73+
ble.start_advertising(advertisement) # Start advertising.
74+
was_connected = False
75+
while not was_connected or ble.connected:
76+
if not blanked: # If LED-off signal is not being sent...
77+
animations.animate() # Run the animations.
78+
if ble.connected: # If BLE is connected...
79+
was_connected = True
80+
if uart.in_waiting: # Check to see if any data is available from the controller.
81+
try:
82+
packet = Packet.from_stream(uart) # Create the packet object.
83+
except ValueError:
84+
continue
85+
if isinstance(packet, ColorPacket): # If the packet is color packet...
86+
if mode == 0: # And mode is 0...
87+
animations.color = packet.color # Update the animation to the color.
88+
print("Color:", packet.color)
89+
animation_color = packet.color # Keep track of the current color...
90+
elif mode == 1: # Because if mode is 1...
91+
animations.color = animation_color # Freeze the animation color.
92+
print("Color:", animation_color)
93+
elif isinstance(packet, ButtonPacket): # If the packet is a button packet...
94+
# Check to see if it's BUTTON_1 (which is being sent by the slide switch)
95+
if packet.button == ButtonPacket.BUTTON_1:
96+
if packet.pressed: # If controller switch is to the left...
97+
print("Controller switch is to the left: LEDs off!")
98+
else: # If the controller switch is to the right...
99+
print("Controller switch is to the right: LEDs on!")
100+
# If the controller switch is moved from right to left...
101+
if packet.pressed and not blanked:
102+
animations.fill(color.BLACK) # Turn off the LEDs.
103+
blanked = packet.pressed # Track the state of the slide switch.
104+
if packet.pressed: # If the buttons on the controller are pressed...
105+
if packet.button == ButtonPacket.LEFT: # If button A is pressed...
106+
print("A pressed: animation mode changed.")
107+
animations.next() # Change to the next animation.
108+
elif packet.button == ButtonPacket.RIGHT: # If button B is pressed...
109+
mode += 1 # Increase the mode by 1.
110+
if mode == 1: # If mode is 1, print the following:
111+
print("B pressed: color frozen!")
112+
if mode > 1: # If mode is > 1...
113+
mode = 0 # Set mode to 0, and print the following:
114+
print("B pressed: color changing!")

0 commit comments

Comments
 (0)