This project involves two Circuit Playground Bluefruit boards. The second acts as the NeoPixel Animator which receives data from the Remote Control which is used to cycle through animations, change the color, freeze the color, and turn on and off the LEDs. This section will go through the NeoPixel Animator code. Let's take a look!
import board import neopixel from adafruit_circuitplayground.bluefruit import cpb from adafruit_led_animation.animation.blink import Blink from adafruit_led_animation.animation.comet import Comet from adafruit_led_animation.animation.sparkle import Sparkle from adafruit_led_animation.group import AnimationGroup from adafruit_led_animation.sequence import AnimationSequence import adafruit_led_animation.color as color from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService from adafruit_bluefruit_connect.packet import Packet from adafruit_bluefruit_connect.color_packet import ColorPacket from adafruit_bluefruit_connect.button_packet import ButtonPacket
The first two are needed to set up the external NeoPixel strip(s).
We'll be able to call on Circuit Playground Bluefruit board functions with the cpb
command, including simplified ways to access the built-in NeoPixels.
We'll use the Adafruit CircuitPython LED Animation library to display the Blink, Comet and Sparkle animations using animation groups and animation sequence. We'll also use it to set the initial animation color.
Then we import all the necessary libraries for connecting to another Bluefruit, and the modules to be able to receive and interpret color packets and button packets. We'll use the color packets to receive data from the Remote Control CPB and set the NeoPixel color based on that data, and button packets to change the animations and freeze the color of the NeoPixels.
Customisations
There are a number of features of the animations that you can customise to your liking. This section of code contains all of the customisable options.
First, set the number of pixels in your externally connected NeoPixel strip. The default is 30
. If you are using two strips connected to the same pin, count the length of both strips only once, i.e. if you have two strips of length 30 attached to pin A1, your STRIP_PIXEL_NUMBER
will be 30.
# The number of NeoPixels in the externally attached strip # If using two strips connected to the same pin, count only one strip for this number! STRIP_PIXEL_NUMBER = 30
The first animation in the cycle is Blink. This is the only animation that the color really matters for as it is the only animation that occurs when there is not another Circuit Playground Bluefruit connected and sending color data. You can set the blink speed and color here. Speed defaults to 0.5 seconds. The color defaults to red.
# Setup for blink animation BLINK_SPEED = 0.5 # Lower numbers increase the animation speed BLINK_INITIAL_COLOR = color.RED # Color before Remote Control is connected
Some of the color options available in the LED Animation library are: RED, YELLOW, ORANGE, GREEN, TEAL, CYAN, BLUE, PURPLE, MAGENTA, WHITE, GOLD, PINK, AQUA, JADE, AMBER.
Next is the setup for the comet animation. First you can set the comet speed. Default is 0.03 seconds. Next you can set the tail length of the comet animating on the Circuit Playground Bluefruit - this comet defaults to a length of 5. Then you can set the tail length of the comet animating on the NeoPixel strip(s) - this comet defaults to a length of 15.
The comet animation has an option to "bounce" which means when it completes animating in one direction, it returns the other direction. You can set whether or not the comet animations bounce. The comet on the Bluefruit defaults to not bouncing. The comet on the strip(s) defaults to bouncing. Set these to True or False depending on whether you want it to bounce or not.
# Setup for comet animation COMET_SPEED = 0.03 # Lower numbers increase the animation speed CPB_COMET_TAIL_LENGTH = 5 # The length of the comet on the Circuit Playground Bluefruit STRIP_COMET_TAIL_LENGTH = 15 # The length of the comet on the NeoPixel strip CPB_COMET_BOUNCE = False # Set to True to make the comet "bounce" the opposite direction on CPB STRIP_COMET_BOUNCE = True # Set to False to stop comet from "bouncing" on NeoPixel strip
Last is the setup for the sparkle animation. The only thing to set here is sparkle speed. It defaults to 0.03 seconds.
# Setup for sparkle animation SPARKLE_SPEED = 0.03 # Lower numbers increase the animation speed
NeoPixel Strip and Bluetooth Setup
Next we create the NeoPixel strip object. If you're using two strips connected to the same pin, you still only need one strip object as it treats them both as the same strip.
strip_pixels = neopixel.NeoPixel(board.A1, STRIP_PIXEL_NUMBER, auto_write=False)
Then we create the BLE, UART service, and advertisement objects.
ble = BLERadio() uart = UARTService() advertisement = ProvideServicesAdvertisement(uart)
Animations
Next we create an animation sequence made up of animation groups. Animation sequences allow you to cycle through multiple animations, such as Blink, Comet and Sparkle. Animation groups allow you to animate on multiple LED objects at the same time, in this case, the built-in NeoPixels on the Bluefruit and the externally connected NeoPixel strip(s).
This animation sequence is made up of three animation groups. Each group contains two instances of one of three animations, grouped together by animation type. Within the groups, two animations objects are created for each of the three animations - one for the Circuit Playground Bluefruit as cpb.pixels
, and one for the external NeoPixel strip(s) as strip_pixels
. Each animation object includes the necessary information for setup, e.g. for Blink, you provide the pixel object, the speed and the initial color.
animations = AnimationSequence( AnimationGroup( Blink(cpb.pixels, BLINK_SPEED, BLINK_INITIAL_COLOR), Blink(strip_pixels, BLINK_SPEED, BLINK_INITIAL_COLOR), sync=True ), AnimationGroup( Comet(cpb.pixels, COMET_SPEED, COMET_INITIAL_COLOR, tail_length=CPB_COMET_TAIL_LENGTH, bounce=CPB_COMET_BOUNCE), Comet(strip_pixels, COMET_SPEED, COMET_INITIAL_COLOR, tail_length=STRIP_COMET_TAIL_LENGTH, bounce=STRIP_COMET_BOUNCE) ), AnimationGroup( Sparkle(cpb.pixels, SPARKLE_SPEED, SPARKLE_INITIAL_COLOR), Sparkle(strip_pixels, SPARKLE_SPEED, SPARKLE_INITIAL_COLOR) ), )
The last thing before the main loop are three variables created for various uses in the code. We set animation_color
to None
, the mode
to 0, and blanked
to False
.
animation_color = None mode = 0 blanked = False
Advertising
The first thing we do in the main loop is begin advertising, i.e. sending out a signal that says the NeoPixel Animator is available for a BLE connection.
[...] ble.start_advertising(advertisement) # Start advertising.
Animate the Animations
Regardless of whether we are connected or not, as long as we haven't set blanked to True, we animate the animations.
[...] while not was_connected or ble.connected: if not blanked: # If LED-off signal is not being sent... animations.animate() # Run the animations.
Packet Time
Once we are connected, we check to see if any data is available from the Remote Control CPB, and then create the packet object.
[...] if ble.connected: # If BLE is connected... was_connected = True if uart.in_waiting: # Check to see if any data is available try: packet = Packet.from_stream(uart) # Create the packet object. except ValueError: continue
Color Packets
The color of the NeoPixels on the NeoPixel Animator are constantly changing based on the orientation of the Remote Control, which is constantly sending updated color packets. There are two color modes: color changing and color frozen.
The code first checks to see if the packet received is a color packet. If it is, we check to see if the mode is 0 or 1.
If the mode is 0, we set the animation color to the packet color. Then we save the current packet color as animation_color
.
This is so we can use it if the mode is 1. What if you find a favorite color and want to keep the animations that color? If the mode is 1, we "freeze" the animation color by setting it to animation_color
. This only updates when the mode is returned to 0, so as long as the mode is 1, the animations will remain the same color.
[...] if isinstance(packet, ColorPacket): if mode == 0: animations.color = packet.color print("Color:", packet.color) animation_color = packet.color elif mode == 1: animations.color = animation_color print("Color:", animation_color)
Button Packets
The NeoPixel Animator is also constantly listening for button packets from the Remote Control. These are used to turn the NeoPixels on and off, change animations, and freeze the animation color.
First we check to see if the packet received is a button packet. If it is, we check to see which button packet it is: BUTTON_1
, LEFT
or RIGHT
.
The Remote Control CPB slide switch is sending the BUTTON_1
packet. Even though it's not a button you can "press" we're using the "pressed" or "not pressed" state to determine direction: left is the "pressed" state, and right is the "not pressed" state. So we check to see if BUTTON_1
is "pressed. If it is pressed, and the LEDs are currently on (not blanked), we turn off the LEDs by setting all of them to BLACK
.
Last, we set blanked
equal to the "pressed" state of the slide switch - this is so we can track whether the LEDs are currently on or off so we know whether to turn them off or on in the previous two lines of code.
[...] elif isinstance(packet, ButtonPacket): if packet.button == ButtonPacket.BUTTON_1: if packet.pressed: print("Remote Control switch is to the left: LEDs off!") else: print("Remote Control switch is to the right: LEDs on!") if packet.pressed and not blanked: animations.fill(color.BLACK) blanked = packet.pressed
Next we are looking for the button presses. The two buttons on the Remote Control are sending LEFT
and RIGHT
button packets.
If the packet received is a LEFT
packet, we move to the next animation.
If the packet received is a RIGHT
packet, we increase the mode by 1. The mode starts as 0 in the beginning of the code. If the mode is 1, we print that the color is frozen. If the mode is greater than 1, we set the mode back to 0, and print that the color is changing. Increasing the mode by 1, and setting it back to 0 when it is greater than 1 allows us to cycle between two different modes.
[...] if packet.pressed: if packet.button == ButtonPacket.LEFT: print("A pressed: animation mode changed.") animations.next() elif packet.button == ButtonPacket.RIGHT: mode += 1 if mode == 1: print("B pressed: color frozen!") if mode > 1: mode = 0 print("B pressed: color changing!")
That's how the NeoPixel Animator code for the Circuit Playground Bluefruit Animation and Color Remote Control works!
Text editor powered by tinymce.