如何在多軸機裝遙控的LED航燈?

How to DIY remote control LED light to your multicopter?

請先看以下完成品的片。可遙控開關,改變閃光效果,效果式樣也可以自定。如果你也想DIY到你的多軸機上,只要小於HK$100,一些焊接技巧和簡單的編程知識。

需要的材料如下:

WS2812 LED
WS2812 燈條
SparkFun-Pro-Micro
SparkFun Pro Micro (Arduino)

關於WS2812

我以為LED燈條就是LED燈條啦,但原來它們有很多種,甚麼WS2801, WS2811 和 WS2812,而我們要用的是WS2812,因為無論在接線和編程上它都比較簡單方便。

通常WS2812都是以一米為單位賣的,例如你隻四軸機想裝四條燈條,你可以買一條回來自己剪開便好了。

WS2812只有三條接線,5v電源,地線和數據線。要注意數據的方向,燈條上有箭頭指示。數據線要焊在DIN上, 如果焊錯了在DOUT,燈條是不會著的。

How to wire WS2812 LED


關於電源

你需要提供5v電源給Arduino和LED燈條。你可以用獨立電源或由多軸機電池取電。多軸機電池一般由7v-21v不等,所以必須降壓才可接上,否則你會馬上燒掉Arduino和燈條

不要由receiver,flight controller取電。如果你使用了大量的LED,receiver和flight controller都沒有足夠的電流可以提供,最壞的情況可使你失去愛機。

如果已經接上了燈條,不要單獨用USB接上Arduino,要先接上5v電才插USB,否則可能會燒了Arduino。

如果用降壓,也要知道它可提供的電流。每粒LED使用60mA電流,例如一條一米的燈條有60粒LED,60 x 60mA = 3600mA = 3.6A,要確保你的降壓板能提供3.6A電流而不會過熱燒掉。否則便要滅少LED的數量。

 每一顆LED用60mA電(紅綠藍各20 mA)

How to wire WS2812 LED and Arduino


關於Arduino

Arduino是控制LED燈條如何閃動的控製器,如果你沒有接觸過Arduino,可以看看它的網站,有很清楚的教學,基本的如何使用就不在這裡說了。

Arduino有很多版本,我使用了SparkFun Pro Micro,因為它細,而且有USB頭,不用FTDI,比較方便。

淘寶有很多這個版本的複製品,名稱都不一樣,文件更不用說了。找了好一回才知道原版是SparkFun Pro Micro,也找到了清楚的文件,使用起來也確定些。(對不起,我有想過買原版的,但運費貴過塊版,實在買不下去。)


LED data pin連接Arduino

我用了pin9-6連接4條LED,可以根據需要連接不同數量的燈條,pin位也可以任意,編程時對應輸出的pin位就行了。還要加上470Ω電阻,以保護Arduino。
How to wire WS2812 LED Arduino and data line




關於遙控

我們可以用遙控來開關LED,改變顏色,閃光效果等。我用的receiver是FrSky X8R,把channel12連到Arduino的A0 pin,同樣地channel和pin位沒有硬性規定,視乎你的需要而改變,編程時應改動就好了。

連接-WS2812-LED-Arduino-FrSky-X8R

我設定了channel12到遙控的TRN switch,這個鍵會自動回彈,剛好可用來每撥一次改變LED到下一個的顯示模式。

FrSky-TH9X-TRN-switch


WS2812-led-arduino-wiring
Arduino接線完成圖

 

LED 接線測試中
LED 接線測試中

 


在Arduino編程控制WS2812

我們已完成了硬件的連接,可以看看如何加入軟件,使LED做事了。

控制LED,有兩個很好用的library:

 如果你還沒有安裝Arduino的軟件,可以先看看入門篇

如果你用SparkFun Pro Micro,可以跟這裡裝driver

我用的是FastLED,由它的example中找了一些合適的再作修改,得到以下的程式:

 

[cpp]
#include <FastLED.h>

#define LED_TYPE WS2811
#define COLOR_ORDER GRB
#define BRIGHTNESS 255
#define FRAMES_PER_SECOND 120

#define LED_TOP_PIN 6
#define LED_LEFT_PIN 8
#define LED_RIGHT_PIN 7
#define LED_BACK_PIN 9
#define RC_PIN A0

#define LONG_LEDS_NUM 12
#define LEDS_NUM 8

CRGB leds_top[LONG_LEDS_NUM];
CRGB leds_left[LEDS_NUM];
CRGB leds_right[LEDS_NUM];
CRGB leds_back[LEDS_NUM];

void setup() {
delay(3000); // 3 second delay for recovery
FastLED.addLeds<LED_TYPE, LED_TOP_PIN, COLOR_ORDER>(leds_top, LONG_LEDS_NUM).setCorrection(TypicalLEDStrip);
FastLED.addLeds<LED_TYPE, LED_LEFT_PIN, COLOR_ORDER>(leds_left, LEDS_NUM).setCorrection(TypicalLEDStrip);
FastLED.addLeds<LED_TYPE, LED_RIGHT_PIN, COLOR_ORDER>(leds_right, LEDS_NUM).setCorrection(TypicalLEDStrip);
FastLED.addLeds<LED_TYPE, LED_BACK_PIN, COLOR_ORDER>(leds_back, LEDS_NUM).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);

pinMode(RC_PIN, INPUT);
}

// List of patterns to cycle through. Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { off, flash, off, police };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating “base color” used by many of the patterns
int pulse_light = LEDS_NUM-1;
int leds_pos = 0;
int rc;
int rc_high = true;

void loop() {

rc = pulseIn(A0, HIGH, 25000);

leds_pos ++;
leds_pos = (leds_pos > 100) ? 0 : leds_pos;

// Top LED effect
topEffect();

// Call the current pattern function once, updating the ‘leds’ array
gPatterns[gCurrentPatternNumber]();

// pulse light in last LED of arms
pulse();

// send the ‘leds’ array out to the actual LED strip
FastLED.show();
// insert a delay to keep the framerate modest
// FastLED.delay(1000 / FRAMES_PER_SECOND);

// do some periodic updates
EVERY_N_MILLISECONDS( 20 ) {
gHue++; // slowly cycle the “base color” through the rainbow
}
EVERY_N_SECONDS( 10 ) {
// nextPattern(); // change patterns periodically
}

// RC control
if (rc > 1500 && !rc_high) {
rc_high = true;
nextPattern();
}
if (rc < 1500 && rc_high) {
rc_high = false;
}

}

#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern()
{
// add one to the current pattern number, and wrap around at the end
gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
}

void topEffect()
{
// a colored dot sweeping back and forth, with fading trails
fadeToBlackBy( leds_top, LONG_LEDS_NUM, 30);
int pos = beatsin16(40, 0, LONG_LEDS_NUM);
leds_top[pos] += CHSV( gHue, 255, 192);
}

void off()
{
for (int i = 0; i < LEDS_NUM; i++) {
leds_left[i] = CRGB::Black;
leds_right[i] = CRGB::Black;
leds_back[i] = CRGB::Black;
}
}

void flash()
{
fadeToBlackBy( leds_left, LEDS_NUM, 50);
fadeToBlackBy( leds_right, LEDS_NUM, 50);
fadeToBlackBy( leds_back, LEDS_NUM, 50);
if (leds_pos < pulse_light) { leds_left[leds_pos] = CRGB::Red; leds_right[leds_pos] = CRGB::Blue; leds_back[leds_pos] = CRGB::White; } } void police() { CRGB c1 = CRGB::Red; CRGB c2 = CRGB::Blue; CRGB c3 = CRGB::White; if (leds_pos > 50) {
c1 = CRGB::Blue;
c2 = CRGB::Red;
c3 = CRGB::White;
}
if ((leds_pos % 5) == 0) {
c1 = c2 = c3 = CRGB::Black;
}
for (int i = 0; i < pulse_light; i++) {
leds_left[i] = c1;
leds_right[i] = c2;
leds_back[i] = c3;
}

}

void pulse()
{
if ((leds_pos % 50) == 0) {
leds_left[pulse_light] = leds_right[pulse_light] = leds_back[pulse_light] = leds_top[0] = CRGB::White;
} else {
leds_left[pulse_light] = leds_right[pulse_light] = leds_back[pulse_light] = leds_top[0] = CRGB::Black;
}
}
[/cpp]

下載NavLED.ino

下載後,修改一下pins位,燈的數量,以配合你硬件的設定,再上傳到Arduino執行便可以了。我只寫了兩個燈效,你有興趣可以輕易地修改或增加自己的燈效。

 

Welcome your comments