This project uses two Circuit Playground Bluefruit boards. One acts as the Remote Control, the other as the NeoPixel Animator. There are two separate pieces of code used in this project: one for the remote control, and one for the NeoPixel animator. You'll load each piece of code onto a separate Circuit Playground Bluefruit.

This project requires the latest version of CircuitPython! Be sure to follow the instructions found on the CircuitPython on Circuit Playground Bluefruit page to download the latest version before continuing.

For this code to work, you'll need to install the necessary libraries first. Follow the instructions on the Circuit Playground Bluefruit CircuitPython Libraries page, and then continue with the instructions in the next section.

Installing CircuitPython LED Animation Library

In addition to the libraries listed on the Circuit Playground Bluefruit CircuitPython Libraries page, this project requires one more library to work. Follow the same instructions found on the Libraries page, and include the Adafruit CircuitPython LED Animation library as well.

To install the Adafruit CircuitPython LED Animation library, drag the following folder out of the CircuitPython library bundle lib folder to the lib folder on your CIRCUITPY drive:

  • adafruit_led_animation

Before continuing, ensure you have ALL of the files and folders mentioned on the Circuit Playground Bluefruit CircuitPython Libraries page AND the adafruit_led_animation folder in the lib folder on your CIRCUITPY drive.

CircuitPython Code

Download the following file and save it as code.py to the Circuit Playground Bluefruit you'll be using as the Remote Control:

"""
Remote Control code for Circuit Playground Bluefruit NeoPixel Animation and Color Remote Control.
To be used with another Circuit Playground Bluefruit running the NeoPixel Animator code.
"""

import time

from adafruit_circuitplayground.bluefruit import cpb

from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from adafruit_bluefruit_connect.color_packet import ColorPacket
from adafruit_bluefruit_connect.button_packet import ButtonPacket


def scale(value):
    """Scale a value from acceleration value range to 0-255 (RGB range)"""
    value = abs(value)
    value = max(min(19.6, value), 0)
    return int(value / 19.6 * 255)


def send_packet(uart_connection_name, packet):
    """Returns False if no longer connected."""
    try:
        uart_connection_name[UARTService].write(packet.to_bytes())
    except:  # pylint: disable=bare-except
        try:
            uart_connection_name.disconnect()
        except:  # pylint: disable=bare-except
            pass
        return False
    return True


ble = BLERadio()

# Setup for preventing repeated button presses and tracking switch state
button_a_pressed = False
button_b_pressed = False
last_switch_state = None

uart_connection = None
# See if any existing connections are providing UARTService.
if ble.connected:
    for connection in ble.connections:
        if UARTService in connection:
            uart_connection = connection
        break

while True:
    last_switch_state = None
    if not uart_connection or not uart_connection.connected:  # If not connected...
        print("Scanning...")
        for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5):  # Scan...
            if UARTService in adv.services:  # If UARTService found...
                print("Found a UARTService advertisement.")
                uart_connection = ble.connect(adv)  # Create a UART connection...
                break
        # Stop scanning whether or not we are connected.
        ble.stop_scan()  # And stop scanning.
    while uart_connection and uart_connection.connected:  # If connected...
        if cpb.button_a and not button_a_pressed:  # If button A pressed...
            print("Button A pressed.")
            # Send a LEFT button packet.
            if not send_packet(uart_connection,
                               ButtonPacket(ButtonPacket.LEFT, pressed=True)):
                uart_connection = None
                continue
            button_a_pressed = True  # Set to True.
            time.sleep(0.05)  # Debounce.
        if not cpb.button_a and button_a_pressed:  # On button release...
            button_a_pressed = False  # Set to False.
            time.sleep(0.05)  # Debounce.
        if cpb.button_b and not button_b_pressed:  # If button B pressed...
            print("Button B pressed.")
            # Send a RIGHT button packet.
            if not send_packet(uart_connection,
                               ButtonPacket(ButtonPacket.RIGHT, pressed=True)):
                uart_connection = None
                continue
            button_b_pressed = True  # Set to True.
            time.sleep(0.05)  # Debounce.
        if not cpb.button_b and button_b_pressed:  # On button release...
            button_b_pressed = False  # Set to False.
            time.sleep(0.05)  # Debounce.
        if cpb.switch is not last_switch_state:  # If the switch state is changed...
            last_switch_state = cpb.switch  # Set state to current switch state.
            if cpb.switch:
                print("Switch is to the left: LEDs off!")
            else:
                print("Switch is to the right: LEDs on!")
            # Send a BUTTON_1 button packet.
            if not send_packet(uart_connection,
                               ButtonPacket(ButtonPacket.BUTTON_1, pressed=cpb.switch)):
                uart_connection = None
                continue
        if cpb.switch:  # If switch is to the left...
            cpb.pixels.fill((0, 0, 0))  # Turn off the LEDs.
        else:  # Otherwise...
            r, g, b = map(scale, cpb.acceleration)  # Map acceleration values to RGB values...
            color = (r, g, b)  # Set color to current mapped RGB value...
            print("Color:", color)
            cpb.pixels.fill(color)  # Fill Remote Control LEDs with current color...
            if not send_packet(uart_connection, ColorPacket(color)):  # And send a color packet.
                uart_connection = None
                continue
        time.sleep(0.1)  # Delay to prevent sending packets too quickly.

Download the following file and save it as code.py to the Circuit Playground Bluefruit you'll be using as the NeoPixel Animator, i.e. the CPB with the NeoPixel strip(s) connected to it:

"""
NeoPixel Animator code for Circuit Playground Bluefruit NeoPixel Animation and Color Remote Control.
To be used with another Circuit Playground Bluefruit running the Remote Control code.
"""

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 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

# 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

# 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

# Setup for sparkle animation
SPARKLE_SPEED = 0.03  # Lower numbers increase the animation speed

# Create the NeoPixel strip
strip_pixels = neopixel.NeoPixel(board.A1, STRIP_PIXEL_NUMBER, auto_write=False)

# Setup BLE connection
ble = BLERadio()
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)

# Setup animations
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, color.MAGENTA, tail_length=CPB_COMET_TAIL_LENGTH,
              bounce=CPB_COMET_BOUNCE),
        Comet(strip_pixels, COMET_SPEED, color.MAGENTA, tail_length=STRIP_COMET_TAIL_LENGTH,
              bounce=STRIP_COMET_BOUNCE)
    ),
    AnimationGroup(
        Sparkle(cpb.pixels, SPARKLE_SPEED, color.PURPLE),
        Sparkle(strip_pixels, SPARKLE_SPEED, color.PURPLE)
    ),
)

animation_color = None
mode = 0
blanked = False

while True:
    ble.start_advertising(advertisement)  # Start advertising.
    was_connected = False
    while not was_connected or ble.connected:
        if not blanked:  # If LED-off signal is not being sent...
            animations.animate()  # Run the animations.
        if ble.connected:  # If BLE is connected...
            was_connected = True
            if uart.in_waiting:  # Check to see if any data is available from the Remote Control.
                try:
                    packet = Packet.from_stream(uart)  # Create the packet object.
                except ValueError:
                    continue
                if isinstance(packet, ColorPacket):  # If the packet is color packet...
                    if mode == 0:  # And mode is 0...
                        animations.color = packet.color  # Update the animation to the color.
                        # Uncomment below to see the color tuple printed to the serial console.
                        # print("Color:", packet.color)
                        animation_color = packet.color  # Keep track of the current color...
                    elif mode == 1:  # Because if mode is 1...
                        animations.color = animation_color  # Freeze the animation color.
                        # Uncomment below to see the color tuple printed to the serial console.
                        # print("Color:", animation_color)
                elif isinstance(packet, ButtonPacket):  # If the packet is a button packet...
                    # Check to see if it's BUTTON_1 (which is being sent by the slide switch)
                    if packet.button == ButtonPacket.BUTTON_1:
                        if packet.pressed:  # If Remote Control switch is to the left...
                            print("Remote Control switch is to the left: LEDs off!")
                        else:  # If the Remote Control switch is to the right...
                            print("Remote Control switch is to the right: LEDs on!")
                        # If the Remote Control switch is moved from right to left...
                        if packet.pressed and not blanked:
                            animations.fill(color.BLACK)  # Turn off the LEDs.
                        blanked = packet.pressed  # Track the state of the slide switch.
                    if packet.pressed:  # If the buttons on the Remote Control are pressed...
                        if packet.button == ButtonPacket.LEFT:  # If button A is pressed...
                            print("A pressed: animation mode changed.")
                            animations.next()  # Change to the next animation.
                        elif packet.button == ButtonPacket.RIGHT:  # If button B is pressed...
                            mode += 1  # Increase the mode by 1.
                            if mode == 1:  # If mode is 1, print the following:
                                print("B pressed: color frozen!")
                            if mode > 1:  # If mode is > 1...
                                mode = 0  # Set mode to 0, and print the following:
                                print("B pressed: color changing!")

The next section shows you how to use the Remote Control Circuit Playground Bluefruit with the NeoPixel Animator Circuit Playground Bluefruit to change color and animations. Check it out!

This guide was first published on Dec 14, 2019. It was last updated on Dec 14, 2019.

This page (CircuitPython Setup and Code) was last updated on Apr 18, 2021.

Text editor powered by tinymce.