How to use the Adafruit FadeCandy for LED Control
Image source: Adafruit
Collaboration between Adafruit & Micah from Scanlime resulted in the introduction of Fadecandy, a driver board with built-in dithering that can be controlled over USB. Fadecandy is comprised of both hardware and software that make WS2811/WS2812 addressable LED art projects easier to build and control, allowing for better-looking projects that are less taxing on microcontroller resources. FadeCandy is an easy way to get started for beginners that also functions as an advanced tool for professionals.
Fadecandy Server Software communicates with one Fadecandy board or dozens. It runs on Windows, Linux, Mac OS, or on embedded platforms like Raspberry Pi. The Open Pixel Control protocol is a simple way of getting pixel data onto the Fadecandy server. Each controller board supports up to 512 LEDs, arranged as 8 strips of 64 each but does not support RGBW LEDs. Only RGB LEDs are supported at the time of publication. Basic programming is discussed later in this article.
The project at hand uses 52 Adafruit NeoPixels in each of eight columns intended to entice players to a claw machine game while also serving as visual count-down timers. Using a high number of addressable LEDs and traditional Arduino libraries with direct connection to the GPIO drags down the processor and causes delays in the game function as well as execution timing issues. As a Raspberry Pi 3 is already slated for sound effects for the claw machine, lighting communication with the FadeCandy will be added to the Pi 3 task list.
Determine power requirements
An important consideration when using LED strips is power consumption. Although each color LED only uses 20 milliamps of current, each Adafruit NeoPixel, as an example, has three colors totaling 60 milliamps if all three LEDs are active. Multiply this by the 52 NeoPixels wired in parallel on each strip, and the maximum current in each strip exceeds 3 amps. Multiplying this current by 8 columns results in a potential current draw of 25 amps, not including the miscellaneous smaller strips laid out around the project. But why would a project have all the LEDs in every NeoPixel on at the same time for long periods? The answer is they won’t, so the selection of a power supply becomes a guessing game. Assuming 75% of the NeoPixels are on at any given point and that each only displays one color, the current drops to around 6 amps. Programming a lighting scenario and testing the current draw with a meter is the only way to know for sure.
Consider a breakout PCB to solve design and wiring concerns
Before programming can begin, a custom PCB is designed using KiCad to break out the FadeCandy connections to convenient terminal blocks allowing for easier connection and output organization. For the claw machine, the rear-facing LEDs are on one channel, as are the right-facing, left-facing, and front-facing ones to preserve channels. The upper and lower prize chute LEDs are broken out separately, leaving two channels for auxiliary connections just in case.
The breakout PCB must incorporate traces that can handle the potential current draw of the power bus. Selecting 2 oz copper for the board also helps in handling larger amounts of current. An example of a PCB created using KiCad and Digi-Key’s PCB Builder Tool is shown in Figures 1 and 2.
Figure 1. Bare PCB from vendor.
Figure 2. High current power bus traces.
PCB Builder Tool Preparation
Once fully developed, a custom PCB is exported using the Plot feature under the File menu in the PCB design tool in KiCad. In the Plot dialogue box, select the Generate Drill File button to save a Gerber drill file to the folder of your choice. Next, select the Plot button. KiCad will create additional Gerber files and place them in the same folder as the drill file. Using Windows File Manager, navigate to the folder that contains the Gerber files. Select all the files related to the PCB; then right-click on the block of files and select Send to Compressed (zipped) folder. A new, zipped folder will appear in the original folder.
Digi-Key’s web-based PCB Builder Tool aids in ordering custom PCBs, allowing for a wide range of options and vendors. Once the PCB Builder Tool is launched, select the Upload Gerber File button; then navigate to and select the zipped folder created earlier. A PCB Tool Viewer window opens showing an image of the board and a list of the files/layers to be included in production as shown in Figure 3.
Figure 3. PCB Builder Viewer; The first step in using Digi-Key’s PCB Builder tool.
The PCB Builder Viewer provides many tools to examine the proposed PCB. Scrolling while hovering on the PCB image zooms in and out while the hand cursor allows movement of the PCB in all directions. Layers can be viewed selectively by toggling the eye-shaped icon by each layer in the layers list.
Select the Finish Upload button to advance to the next ordering step. The next window shows the statistics for the PCB and a list of options to select, such as colors and copper thickness (See Figure 4). Notice that making selections alters prices and the availability of vendors who may not offer the selected options. Start with a quantity of one board and then make other selections as needed.
Figure 4. Choosing PCB specifications, vendor, and quantity.
When all selections have been made and the preferred vendor is determined, increase the board quantity by 1 and observe the price. Repeat this step until the price increases. This method determines the maximum number of boards that may be produced at the lowest price. Select the Add to Cart button when ready to order.
Assemble the custom breakout PCB
The finished PCB is populated with LED strip terminal blocks, power terminal blocks, and a 16 pin header. The Adafruit FadeCandy board is populated with header pins and inserted into the header on the PCB along with a 3D printed spacer to support the USB end of the board. See Figure 5.
Figure 5. Fully populated custom breakout board.
To implement the board and begin programming, a testbed made up of 8 columns of 26 Adafruit Neopixels is used to demonstrate the concept which is later upgraded to 52 Neopixels in the actual claw machine.
Connect the LED strips to the green terminal blocks observing the proper power and signal connections. A 5 V power source is connected to the black terminal blocks observing the proper power and ground connections. Figure 6 shows the NeoPixel wiring before using the breakout board with Figure 7 showing the improvements in wiring.
Figure 6. Testbed wiring prior to using a breakout board.
Figure 7. Testbed with breakout organization.
With the hardware and wiring in place, connect a Raspberry Pi 3 to the FadeCandy using the proper USB cable; then connect the Raspberry Pi to a monitor, keyboard, and mouse. Power up the system to begin programming. The FadeCandy is set up as a client that receives data via USB from the Pi acting as the server. In this setup, the Pi also communicates with an Arduino Mega via serial connection over USB. The Mega handles all input and output of the game machine and simply tells the Pi whether the game is in play. The Pi handles the sound and lighting effects.
There’s a wide range of functionality and applications for the FadeCandy. Many simple and complex examples can be found online with more added frequently. The following code represents some very basic multi-thread functionality for the specific needs of the lighting for this project. The Pi is programmed to flood the Neopixels with a base color and then add random flashes of light to accent the field. When the game is played, two of the strips are converted to a visual count-down clock. See Video 1 for reference. The code used for this project is included below (Listing 1).
#Raspberry Pi Game Machine Script import serial import threading import queue import random import opc, time import pygame #Initialize the sound mixer pygame.mixer.init(44100, 16, 2) #Create a game start sound effect object Start_Sound = pygame.mixer.Sound("glass.wav") Start_Sound.set_volume(1.0) Start_Sound.play() #Create a tick-tock sound object Tick_Sound = pygame.mixer.Sound("ticktock.wav") Tick_Sound.set_volume(1.0) #Tick_Sound.play(maxtime=600) #Create an end of game sound object End_Sound = pygame.mixer.Sound("Buzzer-sound-16.wav") End_Sound.set_volume(1.0) #End_Sound.play() #Build queue objects for transfer between threads game_q = queue.Queue(1) users_q = queue.Queue(1) matrix_q = queue.Queue(1) #State the NeoPixel array for the testbed numLEDs = 8*26 pixels = [ (0,0,0) ] * numLEDs #Set FadeCandy meter start pixel meterStartPix = 130 #Create a serial communication object for the Mega serMega = serial.Serial('/dev/ttyACM0', 115200) #Create a client object for the Open Pixel server client = opc.Client('localhost:7890') #Define a function for the t1 thread that reads data from the Mega def MegaData(): while True: if serMega.inWaiting() > 0: GameDuration = int(serMega.readline()) PlayFlag = int(serMega.readline()) game_q.put((GameDuration, PlayFlag)) TotalUsers = int(serMega.readline()) if not users_q.full(): users_q.put(TotalUsers) time.sleep(0.001) #Define a function for the t2 thread which runs the time meter Neopixels def RunMeter(): while True: GameDuration, PlayFlag = game_q.get() matrix_q.put(PlayFlag) SleepNum = (float(GameDuration)/100/27) if PlayFlag == 1: #Quickly fill the meter with green meterPix = meterStartPix Start_Sound.play() for i in range(0, 26): pixels[meterPix] = (0, 200, 0) client.put_pixels(pixels) time.sleep(.02) meterPix = meterPix+1 #Fill the meter with red based on game timer meterPix = meterStartPix + 25 for i in range(0, 26): if not game_q.empty(): GameDuration, PlayFlag = game_q.get() if PlayFlag == 1: pixels[meterPix] = (200, 0, 0) Tick_Sound.play(maxtime=600) client.put_pixels(pixels) time.sleep(SleepNum) meterPix = meterPix-1 else: break #Wait a tad bit time.sleep(.50) End_Sound.play() time.sleep(.50) #Quickly Clear the meter with soft white meterPix = meterStartPix for i in range(0, 26): pixels[meterPix] = (30, 30, 30) client.put_pixels(pixels) time.sleep(.01) meterPix = meterPix+1 time.sleep(2) else: #Quickly Clear the meter with soft white meterPix = meterStartPix for i in range(0, 26): pixels[meterPix] = (30, 30, 30) client.put_pixels(pixels) time.sleep(.01) meterPix = meterPix+1 time.sleep(2) time.sleep(0.001) #Define a function for the t3 thread that controls the non-meter Neopixels def RunMatrix(): numLEDs = 6*26 while True: if not matrix_q.empty(): play_flag = matrix_q.get() if play_flag == 1: numLEDs = 5*26 else: numLEDs = 6*26 r = random.randint(25,85) g = random.randint(25,85) b = random.randint(25,85) Bright = 3 DotNum = 10 for j in range(5): for h in range(10): pixels = [ (r, g, b) ] * numLEDs for g in range(DotNum): p = random.randint(0,numLEDs-1) pixels[p] = (r*Bright, g*Bright, b*Bright) client.put_pixels(pixels) if not matrix_q.empty(): play_flag = matrix_q.get() if play_flag == 1: numLEDs = 5*26 else: numLEDs = 6*26 time.sleep(.1) #Create thread objects t1 = threading.Thread(target = MegaData) t2 = threading.Thread(target = RunMeter) t3 = threading.Thread(target = RunMatrix) t1.start() t2.start() t3.start()
Listing 1. Code used for controlling the LEDs on the claw machine project.
Working with addressable LEDs is satisfying but challenging work. Often the code needed for the perfect visual effect can interfere with other operations of the microcontroller. The FadeCandy board as well as other types of Dedicated LED Drivers are used to alleviate such issues and open the door to an endless range of lighting scenarios. Along with the right driver, custom PCBs are a great way to organize inputs and outputs and distribute power.