With the help of the Bluefruit
and FancyLED
libraries, it's fairly simple to get some elegant LED animations up and running with BLE control. Let's take a closer look at how this is all happening in the code.
First, we import our libraries:
import random import board import neopixel import adafruit_fancyled.adafruit_fancyled as fancy from adafruit_bluefruit_connect.packet import Packet from adafruit_bluefruit_connect.button_packet import ButtonPacket from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService
Then we setup our NeoPixels. We're keeping the tree's NeoPixels and Circuit Playground Bluefruit Neopixels separate so that we'll be able to run different animations on each of them at the same time.
# setting up # of neopixels TREE_LEDS = 12 CPX_LEDS = 10 # setting up pins for neopixels TREE_PIN = board.A1 CPX_PIN = board.D8 # neopixel setup tree = neopixel.NeoPixel(TREE_PIN, TREE_LEDS, brightness=0.5, auto_write=False) cpx = neopixel.NeoPixel(CPX_PIN, CPX_LEDS, brightness=0.1, auto_write=False)
Next there's some BLE setup.
# BLE setup ble = BLERadio() uart = UARTService() advertisement = ProvideServicesAdvertisement(uart) advertising = False
Now we can get into some fun FancyLED setup. FancyLED uses color palettes that can be cycled through during the different animations. These palettes are then called later in the code when the animations are created.
# to turn neopixels off OFF = (0, 0, 0) # fancyLED color palettes fairy_palette = [fancy.CRGB(1.0, 0.0, 0.0), fancy.CRGB(1.0, 0.5, 0.0), fancy.CRGB(0.0, 0.5, 0.0), fancy.CRGB(0.0, 1.0, 1.0), fancy.CRGB(0.0, 0.0, 1.0), fancy.CRGB(0.75, 0.0, 1.0)] merry_palette = [fancy.CRGB(1.0, 0.0, 0.0), fancy.CRGB(0.0, 1.0, 0.0)] winter_palette = [fancy.CRGB(0.0, 0.75, 0.0), fancy.CRGB(0.0, 1.0, 1.0), fancy.CRGB(0.75, 0.0, 1.0), fancy.CRGB(1.0, 1.0, 1.0), fancy.CRGB(0.0, 0.75, 0.0), fancy.CRGB(0.75, 0.0, 1.0), fancy.CRGB(0.0, 0.0, 1.0), fancy.CRGB(0.0, 1.0, 1.0), fancy.CRGB(1.0, 0.0, 1.0)] star_palette = [fancy.CRGB(1.0, 0.75, 0.0), fancy.CRGB(1.0, 1.0, 1.0), fancy.CRGB(1.0, 0.75, 0.0), fancy.CRGB(0.75, 0.75, 0.75), fancy.CRGB(1.0, 0.75, 0.0)] hanukkah_palette = [fancy.CRGB(0.0, 1.0, 1.0), fancy.CRGB(0.0, 0.0, 1.0), fancy.CRGB(1.0, 0.75, 0.0), fancy.CRGB(0.0, 0.0, 1.0), fancy.CRGB(1.0, 1.0, 1.0)]
And now for the animations. The tree's NeoPixels and the Circuit Playground Bluefruit NeoPixels are animated separately in a few cases, either to have different effects and/or to show different colors. For example, with the merry()
animation, the tree will show red and green fading in and out but the Circuit Playground will have yellow and white swirling around kind of like a traditional Christmas tree.
# default offset value offset = 0 def gimel(): for i in range(TREE_LEDS): color = fancy.palette_lookup(hanukkah_palette, (offset - i) / 5) color = fancy.gamma_adjust(color, brightness=0.3) tree[i] = color.pack() tree.show() for i in range(CPX_LEDS): color = fancy.palette_lookup(hanukkah_palette, (offset - i) / 3) color = fancy.gamma_adjust(color, brightness=0.3) cpx[i] = color.pack() cpx.show() # neopixel animations def jazzy(): for i in range(TREE_LEDS): color = fancy.palette_lookup(fairy_palette, (offset - i) / 4.8) color = fancy.gamma_adjust(color, brightness=0.3) tree[i] = color.pack() tree.show() for i in range(CPX_LEDS): color = fancy.palette_lookup(fairy_palette, (offset + i) / 4) color = fancy.gamma_adjust(color, brightness=0.3) cpx[i] = color.pack() cpx.show() def latkes(): for i in range(TREE_LEDS): color = fancy.palette_lookup(hanukkah_palette, (offset - 24) / TREE_LEDS) color = fancy.gamma_adjust(color, brightness=0.3) tree[i] = color.pack() tree.show() for i in range(CPX_LEDS): color = fancy.palette_lookup(hanukkah_palette, (offset - 20) / CPX_LEDS) color = fancy.gamma_adjust(color, brightness=0.3) cpx[i] = color.pack() cpx.show() def twinkle(): for i in range(60): color = fancy.palette_lookup(fairy_palette, offset + i / CPX_LEDS) color = fancy.gamma_adjust(color, brightness=0.25) p = random.randint(0, (CPX_LEDS - 1)) cpx[p] = color.pack() cpx.show() for i in range(60): color = fancy.palette_lookup(fairy_palette, offset + i / TREE_LEDS) color = fancy.gamma_adjust(color, brightness=0.25) p = random.randint(0, (TREE_LEDS - 1)) tree[p] = color.pack() tree.show() def merry(): for i in range(TREE_LEDS): color = fancy.palette_lookup(merry_palette, (offset + i) / (TREE_LEDS / 2)) color = fancy.gamma_adjust(color, brightness=0.25) tree[i] = color.pack() tree.show() for i in range(60): color = fancy.palette_lookup(star_palette, (offset + i) / CPX_LEDS) color = fancy.gamma_adjust(color, brightness=0.25) p = random.randint(0, (CPX_LEDS - 1)) cpx[p] = color.pack() cpx.show() def festive(): for i in range(TREE_LEDS): color = fancy.palette_lookup(merry_palette, (offset - i) / 2) color = fancy.gamma_adjust(color, brightness=0.25) tree[i] = color.pack() tree.show() for i in range(CPX_LEDS): color = fancy.palette_lookup(star_palette, (offset + i) / CPX_LEDS) color = fancy.gamma_adjust(color, brightness=0.25) cpx[i] = color.pack() cpx.show() def fancy_swirl(): for i in range(TREE_LEDS): color = fancy.palette_lookup(winter_palette, (offset + i) / TREE_LEDS) color = fancy.gamma_adjust(color, brightness=0.25) tree[i] = color.pack() tree.show() for i in range(CPX_LEDS): color = fancy.palette_lookup(star_palette, (offset - i) / CPX_LEDS) color = fancy.gamma_adjust(color, brightness=0.25) cpx[i] = color.pack() cpx.show()
How will we be able to switch between these animations? With state machines! Here are the creation of the different states that will be triggered in the loop to turn the different animations that we just setup on or off. The default state for all of the states (hah) is off.
# states for different neopixel displays fairies = False feeling_fancy = False feeling_festive = False feeling_jazzy = False feeling_merry = False frying_latkes = False rolling_gimel = False
Now for the loop. We begin by defining what will happen when each of our states that we setup are true
. I had a bit of fun with naming them to not only correspond with the names of the animations but to also be holiday themed. Each state is tied to an animation and offset
value, which affects how the animation will run and varies depending on the effect we're looking for.
while True: # states to trigger the different neopixel modes if fairies: twinkle() offset += 0.5 if feeling_fancy: fancy_swirl() offset += 0.05 if feeling_festive: festive() offset += 0.05 if feeling_jazzy: jazzy() offset += 0.08 if feeling_merry: merry() offset += 0.12 if frying_latkes: latkes() offset += 0.05 if rolling_gimel: gimel() offset += 0.05
After our states, we move into some Bluetooth setup. First, if the app is not connected to our Circuit Playground, then it continues to advertise as a device.
if not ble.connected and not advertising: # not connected in the app yet ble.start_advertising(advertisement) advertising = True
Then, if the app is connecting, the Circuit Playground Bluefruit stops advertising as a device and it waits for packets, or inputs, from the app.
if ble.connected: # after connected via app advertising = False if uart.in_waiting: # waiting for input from app packet = Packet.from_stream(uart) if isinstance(packet, ButtonPacket): # if buttons in the app are pressed if packet.pressed:
For input, we're using the buttons available in the app. Each of the 8 available buttons affect our tree. The first 7 (UP
, LEFT
, RIGHT
, DOWN
, 1
, 2
and 3
) turn on animations. Each of these button presses cause all of the states except for one to be defined as false
so we don't get any crashes. The final button, 4
, defines all of the states as false
and writes our OFF
value, which we defined earlier as (0, 0, 0)
to both the tree and Circuit Playground so that all of the NeoPixels are turned off.
if packet.button == ButtonPacket.UP: fairies = True feeling_fancy = False feeling_festive = False feeling_jazzy = False feeling_merry = False frying_latkes = False rolling_gimel = False # fancy if packet.button == ButtonPacket.LEFT: fairies = False feeling_fancy = True feeling_festive = False feeling_jazzy = False feeling_merry = False frying_latkes = False rolling_gimel = False # festive if packet.button == ButtonPacket.RIGHT: fairies = False feeling_fancy = False feeling_festive = True feeling_jazzy = False feeling_merry = False frying_latkes = False rolling_gimel = False # jazzy if packet.button == ButtonPacket.DOWN: fairies = False feeling_fancy = False feeling_festive = False feeling_jazzy = True feeling_merry = False frying_latkes = False rolling_gimel = False # merry if packet.button == ButtonPacket.BUTTON_1: fairies = False feeling_fancy = False feeling_festive = False feeling_jazzy = False feeling_merry = True frying_latkes = False rolling_gimel = False # latkes if packet.button == ButtonPacket.BUTTON_2: fairies = False feeling_fancy = False feeling_festive = False feeling_jazzy = False feeling_merry = False frying_latkes = True rolling_gimel = False # gimel if packet.button == ButtonPacket.BUTTON_3: fairies = False feeling_fancy = False feeling_festive = False feeling_jazzy = False feeling_merry = False frying_latkes = False rolling_gimel = True # off if packet.button == ButtonPacket.BUTTON_4: fairies = False feeling_fancy = False feeling_festive = False feeling_jazzy = False feeling_merry = False frying_latkes = False rolling_gimel = False cpx.fill(OFF) tree.fill(OFF) tree.show() cpx.show()