Overview

Get noticed and stand out at the convention with this DIY light-up badge lanyard. Control the color and brightness with your phone over Bluetooth (BLE) with the Adafruit BlueFruit app. This is a great conversation starter on its own. Pair it with a PyBadge for extra geek points, or make a lanyard for everyone on your team and control ALL the colors. Make it red when you're running late, green when you're ready to network, and indigo when it's time to relax with a cold drink.  

This is an easy-to-assemble project that requires a little light soldering and some drag-and-drop code. Most of the work is already done for you!

This is the easiest time I've ever had setting up a Bluetooth-to-microcontroller connection. Get the software installed and .. it just works.  Hooray!

Parts

Adafruit Feather nRF52840 Express

PRODUCT ID: 4062
The Adafruit Feather nRF52840 Express is the new Feather family member with Bluetooth Low Energy and native USB support featuring the nRF52840!  It's...
$24.95
IN STOCK

Adafruit Mini Skinny NeoPixel Digital RGB LED Strip - 60 LED/m

PRODUCT ID: 2959
So thin. So mini. So teeeeeeny-tiny. It's the 'skinny' version of our classic NeoPixel strips!These NeoPixel strips have 60 digitally-addressable pixel Mini LEDs per...
$99.80
IN STOCK

Lithium Ion Polymer Battery Ideal For Feathers - 3.7V 400mAh

PRODUCT ID: 3898
Lithium ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery...
$6.95
IN STOCK

Silicone Cover Stranded-Core Ribbon Cable - 4 Wires 1 Meter Long

PRODUCT ID: 3892
For those who are fans of our silicone-covered wires, but are always looking to up their wiring game. We now have Silicone Cover Ribbon cables! These may look...
$1.95
IN STOCK

USB cable - USB A to Micro-B

PRODUCT ID: 592
This here is your standard A to micro-B USB cable, for USB 1.1 or 2.0. Perfect for connecting a PC to your Metro, Feather, Raspberry Pi or other dev-board or...
$2.95
IN STOCK

Additional Materials Needed

  • 1" tubular webbing -- about a meter long in your preferred color
  • Swivel hook for holding the badge
  • Hot glue gun
  • Soldering iron & accessories
  • Needle & thread

You'll also need a smart phone that's capable of running the free Adafruit BlueFruit app.

Wiring Diagram

Solder three wires to the NeoPixel strip as follows:

  • NeoPixel - to Feather G
  • NeoPixel DIN to Feather 13
  • NeoPixel + to Feather BAT

We're soldering to the BAT pin because this is a 3v logic board, but the NeoPixels prefer a bit more voltage than that. This pin will take voltage directly from the battery without level-shifting -- meaning your pixels will be a bit brighter.  

Assembly

Decide how long you'd like your lanyard. I found 1m to be just about perfect - long enough to fit over my head comfortably and dangle my badge at an appropriate level. Cut a strip of webbing about 3 inches longer than you'd like -- you can always trim it down later.

Carefully cut your NeoPixels to the same length, cutting through the copper pads. Also cut off the connectors on both ends as we won't be using them this time.

Cut a short piece of ribbon cable -- you only need a few inches. Find the striped wire -- this will be our power wire. We only need three wires for this project so pull off the fourth wire (opposite the striped wire) and save it for another project.

Solder the ribbon cable to your NeoPixels. The striped wire will go to +, the middle wire to IN and the remaining wire goes to -.

These pads are teeny tiny and close together, so this can be a bit tricky. Breathe and take your time with this.

Solder the other end of the wires to the Feather:

  • Striped wire to BAT
  • Middle wire to 13
  • Remaining wire to GND

That's all our connections! Before we seal everything up and make it suitcase-proof, let's load up the software and be sure it all works.

Software

This code uses the Adafruit BlueFruit app's Control Pad and Color Picker features. The Color Picker will send a solid color to the lanyard, and the Control Pad will allow you to choose between four different customizable color gradient animations. You can also speed up and slow down the animations to get just the look you want.

We need to do a bit of setup to get the Feather working with CircuitPython. Here's what's on this page:

  1. Install the latest version of CircuitPython on the board
  2. Install the necessary CircuitPython libraries
  3. Copy and update the Python code
  4. Save the code to your Feather

Ready to start? Here we go!

Install CircuitPython

This guide tells you all you need to know about CircuitPython:

https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython

For now, I'll just cherry-pick the necessaries, but be sure to read through the guide to get all the nitty gritty details and troubleshooting tips.

Scroll down until you find the Feather nRF52840 board and click on it. Then click Download for the latest release. A file will download to your computer with a file extension of uf2.

Plug your Feather into your computer with a USB cable. You may need to click or double-click the Reset button. The board will appear on your computer as a drive called FTHR840BOOT. Drag the file you just downloaded onto this drive to install CircuitPython. The disk drive name FTHR840BOOT will magically change to read CIRCUITPY.

Note: if you don't see FTHR840BOOT, but instead see a drive called CIRCUITPY, that means CircuitPython is already installed. You probably want to follow the instructions to update CircuitPython to the latest version - this project requires at least version 5.0.0-beta.0 or higher.

Install Libraries

Now we need to install a few libraries onto our board as well. Here's a guide that tells you all you'll ever want to know about installing libraries:

https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-libraries

I'll just hit the highlights again to get you up and running.

The above button takes you to a page where you can download the latest library release. Click the big purple button to do so.

Now go to your CIRCUITPY drive and create a new folder called lib. Unzip the Library bundle and find:

  • adafruit_ble
  • adafruit_bluefruit_connect
  • adafruit_fancyled
  • neopixel.mpy

Drag these three folders/files into your brand new lib folder.

Upload the Code

The last thing we need to add is a file called code.py on the CIRCUITPY drive. This is where the Feather will look for actual instructions on what to do. Copy the code below into a text or code editor -- we recommend the Mu editor which can be downloaded here.

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

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

Download: file
pixel = neopixel.NeoPixel(board.D13, 60)

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

Customizing Palettes

I've added 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:

Download: file
# 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.

Save the code on your CIRCUITPY drive, called code.py

That's it! You're ready to start controlling your lights with Bluetooth.

Adafruit BlueFruit App

Go to the app store and find and install the Adafruit BlueFruit app on your smart phone.

Launch the app. Make sure you're in "Central Mode" and that your Feather is powered up. You should see the CIRCUITPY drive appear on the main screen. If it's not there, try pulling down to refresh or restarting the app.

If you still don't see it, try re-uploading your code, making sure you have all the libraries installed. It won't work without all the libraries! Circuit Python still lets you save the file without throwing an error, so double check you've got everything you need.

Click Connect, then Controller > Color Picker. Choose a color and press the "Send selected color" button. Your lanyard will update with the new color. Like magic!

Now go back to the Control Pad. You'll see four buttons and four arrows. The number buttons will select between the four color palettes you made, and the up and down arrows will speed up or slow down the animation of the gradient.

Finishing

Slide a piece of clear 3/8" heat shrink over your solder connections.  Squirt a little hot glue inside and then shrink it down with a heat gun while the glue is still wet. This will make a rock-solid, unbreakable plastic casing for your delicate solder connections.

Slide the lights inside the webbing so they're centered, with an inch or two at either end for overlap. You can finish the cut edges of nylon webbing with a soldering iron (use a tip you don't care about) or a lighter. 

Add some hot glue or a rubber band to attach the battery to the back of the Feather.

Fold the lanyard ends over one another and sew or glue them down to make a secure loop. Attach your lanyard swivel hook to the point at the very bottom.

You can either tuck the Feather and battery into the back of your badge, or use the mounting holes to sew it to the back of the lanyard. A 1" lanyard will cover the board and battery completely so it doesn't show from the other side.

You can turn the LEDs "off" with the app by sending the color black. However, this will still drain your battery since the Feather is constantly checking for updates. The best way to turn it off completely is to unplug the battery JST connector. 

The Feather has onboard battery charging, so to charge your battery back up, just plug in a USB cable while the battery is plugged into the JST connector. 

This guide was first published on Jul 31, 2019. It was last updated on Jul 31, 2019.