LED 제어를 위해 Adafruit FadeCandy를 사용하는 방법

개요

이미지 출처: Adafruit

Scanlime의 Adafruit와 Micah의 공동 작업으로, USB를 통해 제어할 수 있고 디더링이 내장된 구동기 기판 Fadecandy가 도입되었습니다. Fadecandy는 WS2811/WS2812 주소 지정 가능 LED 아트 프로젝트를 더 쉽게 구축하고 제어할 수 있도록 하는 하드웨어 및 소프트웨어로 구성되어 있어 마이크로 컨트롤러 리소스에 대한 부담을 줄이는 멋진 프로젝트를 가능하게 합니다. FadeCandy는 초보자도 쉽게 시작할 수 있지만 전문가를 위한 고급 도구이기도 합니다

Fadecandy 서버 소프트웨어는 Fadecandy 기판 한 개 또는 여러 개와 통신합니다. 이 소프트웨어는 Windows, Linux, Mac OS 또는 임베디드 플랫폼(예: Raspberry Pi)에서 실행됩니다. Open Pixel Control 프로토콜은 픽셀 데이터를 Fadecandy 서버로 가져오는 간단한 방법입니다. 각 컨트롤러 기판은 64개씩 8개 스트립으로 배열된 최대 512개 LED를 지원하지만 RGBW LED를 지원하지는 않습니다. 이 글이 쓰여진 당시에는 RGB LED만 지원됩니다. 이 기사의 뒷 부분에서 기본 프로그래밍에 대해 논의합니다.

진행 중인 프로젝트에서는 시각적 카운트다운 타이머 역할을 하는 동시에 플레이어를 클로 머신 게임으로 유인하기 위해 8개의 열 각각에 52개의 Adafruit NeoPixel을 사용합니다. GPIO에 직접 연결된 여러 개의 주소 지정 가능 LED와 기존 Arduino 라이브러리를 사용하면 프로세서의 속도가 느려지고 게임 기능의 지연과 실행 타이밍 문제가 발생합니다. Raspberry Pi 3은 클로 머신의 음향 효과에 대해 이미 예정되어 있으므로 FadeCandy와의 조명 통신은 Pi 3 작업 목록에 추가됩니다.

전력 요구 사항 결정

LED 스트립을 사용할 때에는 전력 소비를 신중하게 고려해야 합니다. 각 컬러 LED는 20mA에 불과한 전류를 사용하지만, 예를 들어 각 Adafruit NeoPixel은 3개의 LED가 모두 활성화된 경우 총 60mA의 3가지 색상을 제공합니다. 여기에 각 스트립에 병렬로 연결된 52개의 NeoPixel을 곱하면 각 스트립의 최대 전류가 3A를 초과합니다. 이 전류에 8열을 곱하면 프로젝트 주변에 배치된 기타 작은 스트립을 제외하고 25A의 잠재적인 전류가 소모됩니다. 그러나 프로젝트에서 각 NeoPixel의 모든 LED가 동시에 오랫동안 켜지는 이유는 무엇일까요? 정답은 그렇지 않다는 것입니다. 따라서 전원 공급 장치 선택은 추측 게임이 됩니다. NeoPixel의 75%가 특정 지점에서 켜지고 각각 하나의 색상만 표시한다고 가정할 경우 전류는 약 6A로 떨어집니다. 확실하게 확인할 수 있는 유일한 방법은 조명 시나리오를 프로그래밍하고 계측기로 전류 소비를 테스트하는 것입니다.

설계 및 배선 문제를 해결하기 위해 브레이크아웃 PCB ​​고려

프로그래밍을 시작하기 전에, KiCad를 사용하여 맞춤형 PCB가 설계되며 이를 통해 FadeCandy 연결을 편리한 단자대로 분리하여 연결 및 출력 구성을 더 쉽게 할 수 있습니다. 클로 머신의 경우 후면 LED는 채널을 보존하기 위한 오른쪽, 왼쪽 및 전면 LED와 마찬가지로 하나의 채널에 있습니다. 만일의 경우에 대비하여 보조 연결을 위한 2개의 채널을 남겨두고 상위 및 하위 경품 슈트 LED가 따로 분리되어 있습니다.

브레이크아웃 PCB는 전력 버스의 잠재적 전력 소비를 처리할 수 있는 트레이스를 포함해야 합니다. 또한 기판에 2oz 구리를 선택하면 높은 전류를 처리하는 데 도움이 됩니다. 그림 1과 2에 KiCad 및 DigiKey의 PCB 빌더 도구 를 사용하여 만든 PCB 예가 표시되어 있습니다.

그림 1. 벤더의 무실장 PCB.

그림 2. 고전류 전력 버스 트레이스.

PCB 빌더 도구 준비

개발이 완료되면 KiCad의 PCB 설계 도구에 있는 파일 메뉴의 플롯 기능을 사용하여 맞춤형 PCB를 내보냅니다. 플롯 대화 상자에서 드릴 파일 생성 버튼을 선택하여 원하는 폴더에 거버 드릴 파일을 저장합니다. 다음으로, 플롯 버튼을 선택합니다. KiCad는 추가 거버 파일을 생성하여 드릴 파일과 동일한 폴더에 배치합니다. Windows 파일 관리자를 사용하여 거버 파일이 포함된 폴더로 이동합니다. PCB와 관련된 모든 파일을 선택합니다. 그런 다음 파일 블록을 마우스 오른쪽 버튼으로 클릭하고 압축 폴더로 보내기를 선택합니다. 원래 폴더에 새로운 압축 폴더가 표시됩니다.

DigiKey의 웹 기반 PCB 빌더 도구는 맞춤형 PCB 주문을 지원하여 다양한 옵션과 벤더를 허용합니다. PCB 빌더 도구가 실행되면 거버 파일 업로드 버튼을 선택한 다음 이전에 만든 압축 폴더를 찾아 선택합니다. PCB 도구 뷰어 창이 열리고, 그림 3에 표시된 대로 기판 이미지와 생산에 포함시킬 파일/레이어 목록이 표시됩니다.

그림 3. PCB 빌더 뷰어. DigiKey의 PCB 빌더 도구를 사용하는 첫 번째 단계.

PCB 빌더 뷰어는 제안된 PCB를 검사할 수 있는 여러 도구를 제공합니다. PCB 이미지 위에 마우스를 올리고 스크롤링하면 확대 및 축소되며 손 모양 커서를 통해 PCB를 모든 방향으로 이동할 수 있습니다. 레이어 목록의 각 레이어별로 눈 모양 아이콘을 토글하여 레이어를 선택적으로 볼 수 있습니다.

업로드 완료 버튼을 선택하여 다음 주문 단계로 이동합니다. 다음 창에는 PCB에 대한 통계와 색상 및 구리 두께와 같이 선택할 수 있는 옵션 목록이 표시됩니다(그림 4 참조). 옵션을 선택하면 가격과 함께, 선택한 옵션을 제공하지 않을 수 있는 벤더에 대한 주문 가능성이 변경됩니다. 기판 수량을 1개로 시작한 다음 필요에 따라 다른 기판을 선택하세요.

그림 4. PCB 사양, 벤더, 수량 선택.

모든 선택을 완료하고 선호하는 벤더가 결정되면 기판 수량을 1개 늘리고 가격을 확인합니다. 가격이 증가할 때까지 이 단계를 반복합니다. 이 방법을 통해 최저 가격으로 생산할 수 있는 최대 기판 수를 결정할 수 있습니다. 주문할 준비가 되면 장바구니에 추가 버튼을 선택합니다.

맞춤형 브레이크아웃 PCB 조립

완성된 PCB에는 LED 스트립 단자대, 전력 단자대, 16핀 헤더가 실장됩니다. Adafruit FadeCandy 기판에는 헤더 핀이 실장되며 기판의 USB 엔드를 지원하기 위한 3D 인쇄 스페이서와 함께 PCB의 헤더에 삽입됩니다. 그림 5를 참조하세요.

그림 5. 완전히 실장된 맞춤형 브레이크아웃 기판.

기판을 구현하고 프로그래밍을 시작하기 위해 26개 Adafruit Neopixel의 8개 열로 구성된 테스트 베드를 사용하여 나중에 실제 클로 머신에서 52개의 Neopixel로 업그레이드되는 개념을 시연합니다.

LED 스트립을 녹색 단자대에 연결하여 적절한 전력 및 신호 연결을 관찰합니다. 5V 전원을 검은색 단자대에 연결하고 적절한 전력 및 접지 연결을 관찰합니다. 그림 6은 배선 개선 사항을 보여주는 그림 7과 함께 브레이크아웃 기판을 사용하기 전의 NeoPixel 배선을 보여줍니다.

그림 6. 브레이크아웃 기판을 사용하기 전 테스트 베드 배선.

그림 7. 브레이크아웃 구성을 사용하는 테스트 베드.

하드웨어와 배선이 제대로 배치되면, 적절한 USB 케이블을 사용하여 Raspberry Pi 3을 FadeCandy에 연결합니다. 그런 다음 Raspberry Pi를 모니터, 키보드 및 마우스에 연결합니다. 시스템에 전력을 공급하고 프로그래밍을 시작합니다. FadeCandy는 서버 역할을 하는 Pi에서 USB를 통해 데이터를 수신하는 클라이언트로 설정됩니다. 이 설정에서 Pi는 또한 USB 통한 직렬 연결을 사용하여 Arduino Mega와 통신합니다. Mega는 게임 머신의 모든 입력과 출력을 처리하고 단순히 게임이 실행 중인지 여부를 Pi에 알립니다. Pi는 사운드 및 조명 효과를 처리합니다.

FadeCandy에는 다양한 기능과 응용 프로그램이 있습니다. 온라인에서, 더욱 자주 추가되는 여러 간단하고 복잡한 예를 확인할 수 있습니다. 다음 코드는 이 프로젝트의 조명에 대한 특정 요구 사항을 위한 매우 기본적인 일부 다중 스레드 기능을 나타냅니다. Pi는 기본 색상으로 Neopixel을 채우고 필드를 강조하기 위해 임의의 플래시를 추가하도록 프로그래밍되었습니다. 게임이 실행되면 두 개의 스트립이 시각적 카운트다운 클록으로 변환됩니다. 참조를 위해 동영상 1을 참조하십시오. 이 프로젝트에 사용된 코드는 아래에 포함되어 있습니다(목록 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()

목록 1. 클로 프로젝트의 LED 제어를 위해 사용된 코드.

요약

주소 지정이 가능한 LED로 작업하는 것은 만족스럽지만 어려운 작업입니다. 종종 완벽한 시각 효과에 필요한 코드는 마이크로 컨트롤러의 다른 작업을 방해할 수 있습니다. FadeCandy 기판과 다른 유형의 전용 LED 구동기를 사용하여 이러한 문제를 완화하고 무한한 범위의 조명 시나리오에 대한 찬스를 획득할 수 있습니다. 올바른 구동기와 함께 맞춤형 PCB는 입력 및 출력을 구성하고 전력을 분배하는 좋은 방법입니다.

작성자 정보

Image of Don Johanneck

DigiKey의 기술 콘텐츠 개발자인 Don Johanneck은 2014년부터 근무해 왔습니다. 최근 현재 직책으로 이동한 그는 동영상 설명 및 제품 콘텐츠 작성을 담당하고 있습니다. Don은 DigiKey의 장학금 프로그램을 통해 노스랜드 커뮤니티 및 테크니컬 컬리지에서 응용 과학 전문 학사 학위를 받았습니다. 그는 무선 조정 모델링, 클래식 기계 복원 및 수리를 즐깁니다.

More posts by Don Johanneck
 TechForum

Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.

Visit TechForum