若干月以前,我看到了一个关于制作炫彩智能灯的视频,这带给我浓厚的兴趣,激发了我强烈的制作欲望,遂逢某大学(鄙人本科在读)新某杯举办之际,给了我一个机会去制作这个小东西。于是乎,在找了两名志同道合的同学后,开始了打怪升级的道路。本作品可以通过电位器调节模式及亮度,也可通过Bliker app实现WIFI控制。
[备注]:其实选择一块兼容WIFI模块的板子是更好的选择
1、Arduino UNO板一块
2、ESP8266模块一个(需配烧录器)
3、WB2812B灯带(这里有1米30灯珠与60灯珠两种选择,我选择了前者,后者有点密)
4、两个1024(规格可选)的电位器(配两个旋钮)
5、导线,杜邦线(各种,谨防需要)若干
6、亚克力管(外径165mm内径159mm高度273mm,封顶)
7、3D打印底座和隔层(后附STL文件,具体尺寸可自行设计,包括亚克力管的大小)
8、一块小喷锡板(用于外焊一些电容电阻)
9、470微法电容一个,220欧姆电阻一个
10、5V10A电源适配器一个,电源转接头一个
1、灯带连接:将灯带剪为需要的长度(沿敷铜处虚线剪断),我选择了八个灯珠一组,因为我设计的高度为八层。然后借助跳线将灯带间连接起来,GND接GND,VCC接VCC,IN接OUT,并联切勿接错。
2、与UNO板的连接,见下图。其中电位器接Ax串口,可检测电位器当前的状态,用于控制灯的亮度和模式。电阻可不要,灯带接Dx串口,用于向灯带发出指令,控制灯带。电源适配器图中功率有误,实际需要电流应该根据您所需灯珠数量计算,以一个灯珠0.3w计算128个的灯珠需要38.4w,遂我选择了5V10A的电源适配器(第一次还买错了,买成了5V6A的,这里需要注意的是,电流输出大小有外电路决定,即在工作中电流输出最大可到10A但不一定为10A,可小于)。
3、ESP8266与UNO板通信连接
这里面的硬串口(RX,TX)通信应该被USB占用,同时也不推荐硬串口通信,建议选择软串口进行通信。如果需要向ESP烧录程序,此时需将GPIO0接地。
烧录模式:
正常工作:
推荐使用Arduino IDE进行烧录,简单方便。
先上代码,再讲注意重点。(代码中没有写注释,见谅,具体内容下文都有讲解)
UNO部分:
#include "FastLED.h"
#include
SoftwareSerial mySerial(10,11);//RX TX mySerial(8,1);//RX TX
#define NUM_LEDS 128
#define DATA_PIN 6
#define LED_TYPE WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
uint8_t max_bright = 128;
uint8_t val_bright = 0;
uint8_t startIndex = 0;
uint8_t startIndex1 = 0;
uint8_t bright = 0;
uint8_t book = 0;
uint8_t tips = 1;
int val1 = 0;
int val2 = 0;
String Serial_String="";
String Strings = "";
String string_r = "";
String string_g = "";
String string_b = "";
String string_bright = "";
String string_stop = "";
String string_stop2 = "";
long previousMillis = 0;
long currentMillis = 0;
uint8_t XDU_Index[26] = {
10,11,12,13,18,29,34,35,36,37,74,75,76,77,66,60,59,69,101,107,115,125,122,116,108,98};
void setup(){
Serial.begin(115200);
mySerial.begin(9600);
pinMode(LED_BUILTIN,OUTPUT);
delay(1000);
LEDS.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(max_bright);
}
void WarmLight(){
fill_solid(leds, 128, CRGB(235,68,17));
}
void White(){
fill_solid(leds, 128, CRGB::White);
}
void Black(){
fill_solid(leds, 128, CRGB::Black);
}
void Ocean_palette()
{
fill_palette(leds, NUM_LEDS, startIndex, 8, OceanColors_p, 255, LINEARBLEND);
}
void Forest_palette()
{
fill_palette(leds, NUM_LEDS, startIndex, 8, ForestColors_p, 255, LINEARBLEND);
}
uint8_t mov[25] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
void XDU()
{
uint8_t i;
uint8_t j;
uint8_t index;
for(j=0;j<128;j++){
currentMillis = millis();
if(currentMillis - previousMillis>=700){
fill_solid(leds, 128, CRGB::Blue);
for(i=0;i<26;i++){
index = XDU_Index[i];
leds[index] = CRGB::Red;
FastLED.show();
mov[i] = 2*(8-((index+1)%8))+1;
}
for(i=0;i<26;i++) XDU_Index[i] = (XDU_Index[i]+mov[i])%128;
previousMillis = millis();
}
if(analogRead(A2)<=680||(analogRead(A2)>=830&&analogRead(A2)<1000)) break;
}
}
void PWM(){
fill_palette(leds, NUM_LEDS, startIndex1++, 8, RainbowColors_p, 255, LINEARBLEND);//(leds, 数量,起始序号,灯珠间色差,色板名称,亮度,变化方式)
if(book==0){
FastLED.setBrightness(bright++);
}else{
FastLED.setBrightness(bright--);
}
if(bright==127){
book = 1;
}else if(bright==0){
book = 0;
}
delay(20);
}
void read_string(){
while(mySerial.available()>0){
Serial_String+=(char)(mySerial.read());
}
}
void R(){
val_bright = (int)val1/1023.0*128;
FastLED.setBrightness(val_bright);
if(val2<150) White();
else if(170<val2&&val2<320) WarmLight();
else if(340<val2&&val2<490) Forest_palette();
else if(510<val2&&val2<660) Ocean_palette();
else if(680<val2&&val2<830) XDU();
else if(850<val2&&val2<980) PWM();
}
void Wifi()
{
while(mySerial.available()>0){
Serial_String="";
read_string();
}
if(Serial_String=="P"||Serial_String=="Q"||Serial_String=="R"||Serial_String=="S"||Serial_String=="T"||Serial_String=="B"||Serial_String=="U")
string_stop = Serial_String;
if(Serial_String=="1"||Serial_String=="2"||Serial_String=="3"||Serial_String=="4"||Serial_String=="5"||Serial_String=="6"||Serial_String=="7"||Serial_String=="8"||Serial_String=="9")
string_stop2 = Serial_String;
if(string_stop!=""||string_stop2!="") Serial.println(string_stop+'@'+string_stop2);
if(string_stop=="P") White();
else if(string_stop=="Q") WarmLight();
else if(string_stop=="R") Forest_palette();
else if(string_stop=="S") Ocean_palette();
else if(string_stop=="T") XDU();
else if(string_stop=="B") Black();
else if(string_stop=="U") PWM();
if(string_stop!="U"){
val_bright = (int)string_stop2.toInt()/9.0*128;
FastLED.setBrightness(val_bright);
}
}
void loop(){
val2 = analogRead(A2);
val1 = analogRead(A0);
if(val2 >= 1000) Wifi();
else R();
startIndex++;
FastLED.show();
}
我们从头看起。
1、FastLED.h库是一个控制LED很方便的库。这里列写几条实用的语句,详细推荐去看官方文档,或者上b站看视频。
这是我学习过程中的记录:
#include"FastLED.h"
#define NUM_LEDS 128 //LED数量
#define DATA_PIN 9 //引脚
#define LED_TYPE WS2812B //LED型号
#define COLOR_ORDER GRB //LED颜色顺序
uint8_t max_bright = 128; //LED亮度
CRGB leds[NUM_LEDS]; //建立leds光带
//初始化
void setup()
{
Serial.begin(9600); //启动串行通讯
delay(1000); //稳定性等待
LEDS.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds,NUM_LEDS); //初始化操作
FastLED.setBrightness(max_bright); //设置光带亮度
}
void loop()
{
for(int i=0;i<=NUM_LEDS;i++)
{
leds[i] = CRGB::Red; //设置光带第一个灯珠颜色为红色
FastLED.show(); //更新LED颜色
delay(500); //等待500ms
leds[i] = CRGB::Black; //熄灭第一个灯珠
FastLED.show(); //更新LED颜色
delay(500); //等待500ms
}
}
/*
//常用语句
fill_solid(leds+5,30,CRGB::RED); //全部点亮/熄灭
CRGB myRGBcolor(50,0,50); //myRGBcolor(r,g,b)
fill_solid(leds+5,30,CRGB::RED); //点亮自定义色彩
myRGBcolor.r = 50; //改变颜色参数
CHSV myHSVcolor(80,255,200); //HSV定义颜色参数(色调,饱和度,明亮度)
fill_rainbow(leds,30,0,10); //产生彩虹色,第三个参数是H,第四个参数是色调差(可选)
fill_gradient_RGB(leds,0,CRGB::RED,29,CRGB(0,0,255)); //(光带名,起始灯珠号,起始RGB,结束灯珠号,结束RGB)
fill_gradient(leds,0,CHSV(50,255,255),29,CSHV(150,255,255),SHORTEST_HUES/LONGEST_HUS); //(光带名,起始灯珠号,起始HSV,结束灯珠号,结束HSV)
addGlitter(10);
fill_palettle(leds,30,0,8,OceanColors)p,255,LINEARBLEND/NOBLEND); //(LED,灯珠数量,其实颜色序号,灯珠色差,色板名称,亮度,颜色过渡)
//RainbowColors_p
//OceanColors_p
//CloudColors_p
//LavaColors_p
//ForestColors_p
//RainbowStripeColors_p
//PartyColors_p
//HeatColors
CRGBPalette16 myColorPalette = CRGBPalette16(
CRGB::Green, CRGB::Green,CRGB::Green, CRGB::Green,
CRGB::Green, CRGB::Green,CRGB::Green, CRGB::Green,
CRGB::Green, CRGB::Green,CRGB::Green, CRGB::Green,
CRGB::Green, CRGB::Green,CRGB::Green, CRGB::Green);
CHSVPalette16 myColorPalette = CHSVPalette16(
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200));
const TProgmePalettel16 myColorPalette PROGMEM ={
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),HSV(0,255,200),
}
*/
2、设置软串口,启动并设置波特率(波特率会影响通信效率我采用与ESP9600通信)
SoftwareSerial mySerial(10,11);//RX TX
Serial.begin(115200);
mySerial.begin(9600);
3、读取串口状态,这里是电位器状态
val2 = analogRead(A2);
val1 = analogRead(A0);
4、读取ESP的通信
void read_string(){
while(mySerial.available()>0){
//判断接受信息长度
Serial_String+=(char)(mySerial.read());//读取信息并存入变量
}
}
if(string_stop!=""||string_stop2!="") Serial.println(string_stop+'@'+string_stop2);//这一句是向硬串口写入,用于后台监视
5、将string转为int函数
string_stop2.toInt()
ESP端代码
#define BLINKER_WIFI
#include
#include
SoftwareSerial mySerial(8,1);//RX TX
String mySerial_String = "";
String RGB_String = "";
uint8_t i = 0;
char auth[] = "Blinker app分配的密钥";
char ssid[] = "WIFI名称";
char pswd[] = "WIFI密码";
#define BUTTON_1 "Button1"
#define BUTTON_2 "Button2"
#define BUTTON_3 "Button3"
#define BUTTON_4 "Button4"
#define BUTTON_5 "Button5"
#define BUTTON_6 "Button6"
#define Slider_1 "Slider1"
BlinkerButton Button1("White"); //P
BlinkerButton Button2("Warm"); //Q
BlinkerButton Button3("Forest"); //R
BlinkerButton Button4("Ocean"); //S
BlinkerButton Button5("XDU"); //T
BlinkerButton Button6("PWM"); //U
BlinkerSlider Slider1("Bright");
void slider1_callback(int32_t value){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("get slider value: ", value);
mySerial_String = String(value);
}
void button1_callback(const String & state) //White
{
digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == "on"){
BLINKER_LOG("Toggle on!");
Button1.print("on");
mySerial_String = "P";
}
else if(state == "off")
{
BLINKER_LOG("Toggle off!");
Button1.print("off");
mySerial_String = "B";
}
}
void button2_callback(const String & state) //Warm
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == "on") {
BLINKER_LOG("Toggle on!");
Button2.print("on");
mySerial_String = "Q";
}
else if(state == "off")
{
BLINKER_LOG("Toggle off!");
Button2.print("off");
mySerial_String = "B";
}
}
void button3_callback(const String & state) //Forest
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == "on") {
BLINKER_LOG("Toggle on!");
Button3.print("on");
mySerial_String = "R";
}
else if(state == "off")
{
BLINKER_LOG("Toggle off!");
Button3.print("off");
mySerial_String = "B";
}
}
void button4_callback(const String & state) //Ocean
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == "on") {
BLINKER_LOG("Toggle on!");
Button4.print("on");
mySerial_String = "S";
}
else if(state == "off")
{
BLINKER_LOG("Toggle off!");
Button4.print("off");
mySerial_String = "B";
}
}
void button5_callback(const String & state) //XDU
{
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == "on") {
BLINKER_LOG("Toggle on!");
Button5.print("on");
mySerial_String = "T";
}
else if(state == "off")
{
BLINKER_LOG("Toggle off!");
Button5.print("off");
mySerial_String = "B";
}
}
void button6_callback(const String & state) //PWM
{
digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
BLINKER_LOG("get button state: ", state);
if (state == "on") {
BLINKER_LOG("Toggle on!");
Button6.print("on");
mySerial_String = "U";
}
else if(state == "off")
{
BLINKER_LOG("Toggle off!");
Button6.print("off");
mySerial_String = "B";
}
}
void dataRead(const String & data)
{
Blinker.vibrate();
Blinker.print("ERROR");
}
void setup()
{
Serial.begin(115200);
mySerial.begin(9600);
BLINKER_DEBUG.stream(Serial);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);
Button1.attach(button1_callback);
Button2.attach(button2_callback);
Button3.attach(button3_callback);
Button4.attach(button4_callback);
Button5.attach(button5_callback);
Button6.attach(button6_callback);
Slider1.attach(slider1_callback);
}
void loop()
{
mySerial.print(mySerial_String);
delay(100);
Blinker.run();
}
6、Blinker用法可以看这个文档:Blinker说明文档
7、对于Button,如果想要更新按键(如on to off)需要Button.print(“on”) or Button.print(“off”)。
8、Blinker app的设置略,大家玩了这么多年手机,应该都会吧/doge
至此你的灯带应该可以进行基本的模式切换和亮度调节,通过电位器和WIFI两种方式
STL文件
https://pan.baidu.com/s/13h840FhqFlePZeEFGJ1Lew
提取码:49p2
1、ESP和UNO串口通信出现信息丢失等现象影响传输。我们暂时以多次写入作为解决办法,但无法传输多位信息。
2、UNO使用USB烧录时出现计算机无法识别USB现象,重启可以解决99%的问题。
3、该灯不适合显示文字图案
4、跳线不牢固,容易断裂,用热熔胶把焊接地方粘一下
5、亚克力管尽量选择允许误差足够大的规格