Install CircuitPython

The Adafruit nRF52840 Feather ships with CircuitPython but it's best to go ahead and update it to the latest version. This project needs version 5.0.0-beta.0 or higher. It's super easy with the circuitpython.org website, just click the link below to launch the page. There you can choose to install stable release or beta. 

Quick Start

  • Connect board to computer via a known good USB data cable and double press the reset button.
  • Download the CircuitPython UF2 and upload to the FTHR840BOOT drive.
  • Open CIRCUITPY drive and upload the required libraries (listed below) and code.py

The Adafruit CircuitPython Libraries

Download the CircuitPython library bundle and unzip the folder. Create a new folder in the CIRCUITPY drive and name it "lib". The following libraries are required to run the code properly. Double check to ensure all of the files and folders are inside the lib folder on the CIRCUITPY drive.

  • adafruit_ble
  • adafruit_bluefruit_connect
  • adafruit_fancyled
  • neopixel.mpy

Mu Python Editor

Check out Mu, it's a simple Python editor that works with Adafruit CircuitPython hardware. It's written in Python and works on Windows, MacOS, Linux and Raspberry Pi. The serial console is built right in so you get immediate feedback from your board's serial output!

Upload code.py

Click the link below to download the project zip – this contains the code. Upload the code.py file to the CIRCUITPY drive. That's it! You're ready to start controlling your lights with the Bluefruit LE connect app. The rainbow animation is programmed to automatically play after boot.

# SPDX-FileCopyrightText: 2019 Phillip Burgess for Adafruit Industries
# SPDX-FileCopyrightText: 2019 Dan Halbert for Adafruit Industries
# SPDX-FileCopyrightText: 2019 Erin St Blaine for Adafruit Industries
#
# SPDX-License-Identifier: MIT

""" FancyLED Palette and Color Picker Control with BlueFruit App
    Code by Phil Burgess, Dan Halbert & Erin St Blaine for Adafruit Industries
"""
import board
import neopixel
import adafruit_fancyled.adafruit_fancyled as fancy

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.button_packet import ButtonPacket
from adafruit_bluefruit_connect.color_packet import ColorPacket

NUM_LEDS = 60                   # change to reflect your LED strip
NEOPIXEL_PIN = board.D13        # change to reflect your wiring

# Palettes can have any number of elements in various formats
# check https://learn.adafruit.com/fancyled-library-for-circuitpython/colors
# for more info

# Declare a 6-element RGB rainbow palette
PALETTE_RAINBOW = [fancy.CRGB(1.0, 0.0, 0.0),  # Red
                   fancy.CRGB(0.5, 0.5, 0.0),  # Yellow
                   fancy.CRGB(0.0, 1.0, 0.0),  # Green
                   fancy.CRGB(0.0, 0.5, 0.5),  # Cyan
                   fancy.CRGB(0.0, 0.0, 1.0),  # Blue
                   fancy.CRGB(0.5, 0.0, 0.5)]  # Magenta

# Declare a Purple Gradient palette
PALETTE_GRADIENT = [fancy.CRGB(160, 0, 141),  # Purples
                    fancy.CRGB(77, 0, 160),
                    fancy.CRGB(124, 0, 255),
                    fancy.CRGB(0, 68, 214)]

# Declare a FIRE palette
PALETTE_FIRE = [fancy.CRGB(0, 0, 0),        # Black
                fancy.CHSV(1.0),            # Red
                fancy.CRGB(1.0, 1.0, 0.0),  # Yellow
                0xFFFFFF]                   # White

# Declare a Water Colors palette
PALETTE_WATER = [fancy.CRGB(0, 214, 214),  # blues and cyans
                 fancy.CRGB(0, 92, 160),
                 fancy.CRGB(0, 123, 255),
                 fancy.CRGB(0, 68, 214)]

# Declare a NeoPixel object on NEOPIXEL_PIN with NUM_LEDS pixels,
# no auto-write.
# Set brightness to max because we'll be using FancyLED's brightness control.
pixels = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_LEDS, brightness=1.0,
                           auto_write=False)

offset = 0  # Positional offset into color palette to get it to 'spin'
offset_increment = 1
OFFSET_MAX = 1000000

ble = BLERadio()
uart_service = UARTService()
advertisement = ProvideServicesAdvertisement(uart_service)

def set_palette(palette):
    for i in range(NUM_LEDS):
        # Load each pixel's color from the palette using an offset, run it
        # through the gamma function, pack RGB value and assign to pixel.
        color = fancy.palette_lookup(palette, (offset + i) / NUM_LEDS)
        color = fancy.gamma_adjust(color, brightness=0.25)
        pixels[i] = color.pack()
    pixels.show()

# set initial palette to run on startup
palette_choice = PALETTE_RAINBOW

# True if cycling a palette
cycling = True

while True:
    # Advertise when not connected.
    ble.start_advertising(advertisement)

    while not ble.connected:
        if cycling:
            set_palette(palette_choice)
            offset = (offset + offset_increment) % OFFSET_MAX

    # Now we're connected

    while ble.connected:
        if uart_service.in_waiting:
            packet = Packet.from_stream(uart_service)
            if isinstance(packet, ColorPacket):
                cycling = False
                # Set all the pixels to one color and stay there.
                pixels.fill(packet.color)
                pixels.show()
            elif isinstance(packet, ButtonPacket):
                cycling = True
                if packet.pressed:
                    if packet.button == ButtonPacket.BUTTON_1:
                        palette_choice = PALETTE_RAINBOW
                    elif packet.button == ButtonPacket.BUTTON_2:
                        palette_choice = PALETTE_GRADIENT
                    elif packet.button == ButtonPacket.BUTTON_3:
                        palette_choice = PALETTE_FIRE
                    elif packet.button == ButtonPacket.BUTTON_4:
                        palette_choice = PALETTE_WATER
                # change the speed of the animation by incrementing offset
                    elif packet.button == ButtonPacket.UP:
                        offset_increment += 1
                    elif packet.button == ButtonPacket.DOWN:
                        offset_increment -= 1

        if cycling:
            offset = (offset + offset_increment) % OFFSET_MAX
            set_palette(palette_choice)

BLE Connect App

Use the Bluefruit LE app to control the NeoPixels. The color picker sends solid colors. The control pad features buttons for up to four different animations. Get started by installing the Bluefruit LE connect app for iOS or Android. 

  • Open the app and find the CIRCUITPY from the list of discoverable devices.
  • Tap on the connect button to load the Modules page.
  • Locate and tap on Controller from the list of modules.
  • Tap on either Control Pad or Color Picker from the list of modules

Changing the NeoPixel Count

Once you've got the code in your editor, look near the top and find this line:

pixel = neopixel.NeoPixel(board.D6, 28)

If you soldered to a pin other than pin 6, change D6 to reflect the correct pin. The last number (28) tells the board how many NeoPixels we have. If you have more or less than 28, change this number to reflect your actual setup.

Customizing Palettes

The code features four different color palettes for the animations accessed from the Control Pad: a rainbow, a purple gradient, a "fire" and a "water" palette. You can customize these fairly easily in the code. The power of the FancyLED library allows you so much control when it comes to choosing custom colors and animating them smoothly.

Find the palette definitions in the code:

# Declare a 6-element RGB rainbow palette
PALETTE_RAINBOW = [fancy.CRGB(1.0, 0.0, 0.0), # Red
           fancy.CRGB(0.5, 0.5, 0.0), # Yellow
           fancy.CRGB(0.0, 1.0, 0.0), # Green
           fancy.CRGB(0.0, 0.5, 0.5), # Cyan
           fancy.CRGB(0.0, 0.0, 1.0), # Blue
           fancy.CRGB(0.5, 0.0, 0.5)] # Magenta

# Declare a Purple Gradient palette
PALETTE_GRADIENT = [fancy.CRGB(160, 0, 141), # Purples
           fancy.CRGB(77, 0, 160),
           fancy.CRGB(124, 0, 255),
           fancy.CRGB(0, 68, 214)]

# Declare a FIRE palette
PALETTE_FIRE = [fancy.CRGB(0, 0, 0),       # Black
              fancy.CHSV(1.0),           # Red
              fancy.CRGB(1.0, 1.0, 0.0), # Yellow
              0xFFFFFF]                  # White

# Declare a Water Colors palette
PALETTE_WATER = [fancy.CRGB(0, 214, 214), # blues and cyans
           fancy.CRGB(0, 92, 160),
           fancy.CRGB(0, 123, 255),
           fancy.CRGB(0, 68, 214)]

You can use CRGB values or CHSV values to choose colors, or use them both at the same time. There are also multiple ways to declare values and a lot of control over how spread out the gradients can be.

This is explained in detail in the FancyLED guide so take a look to find out all you need to know about creating your own custom color palettes.

This guide was first published on Aug 13, 2019. It was last updated on Nov 27, 2023.

This page (Software) was last updated on Nov 27, 2023.

Text editor powered by tinymce.