前言
之前不是双十一到了麻,本来想搞一台炫酷的主机和显示器开始我的快乐永劫生活。但是吧,就真的,有点贵,半夜辗转反侧,对于一个永劫菜鸟来说,我不太配花这个钱。于是我最终放弃了驰骋永劫世界的想法,但是炫酷的主机我买不了,我搞一条炫酷的主机灯带总没问题吧(装饰现有的笔记本)。
说干就干,首先就是选择灯带方案。阿,你说啥?为什么不直接上淘宝买现成的灯带?,原因很简单,贵呀,而且不能二次定制。所以最实惠的当然是自己买开发板和灯带进行定制,捣鼓捣鼓就可以新鲜出炉了。
其次这篇文章是纯小白教程,有错欢迎留言纠正。
实现效果
视频地址:www.bilibili.com/video/BV1j3…
准备工作
开发方案
开发工具:VSCode
开发语言:C/C++
VsCode插件:PlatformIO IDE
材料
开发板:ESP32 DevKitC(WROOM-32D) Type-C接口,一个
灯带:WS2812B灯带,一条(建议5m,可以裁剪)
杜邦线:公对母类型,母对母类型,若干
数据线:Type-C,一条
电源头:5V/3A,一个(比如树莓派充电头)
固定扣:自粘式小块固定扣,若干
制作过程
C/C++环境搭建
这是一个最容易劝退小白的步骤,因为问题就像糟糕的心情,总会不期而遇。
MAC/Linux
这两个系统都自带了c/c++的编译器,可以直接忽略环境搭建。
Windows
下载MinGW
直接下载MinGW最新版本进行安装即可,安装选项默认即可。同时需要你复制默认安装的地址,需要用于环境配置。
下载地址:win-builds.org/doku.php/do…
配置环境变量
因为我没有windows主机,这部分推荐去B站上找教程学习。
- 在桌面上寻找到电脑图标
- 选中电脑图标,右击"属性"
- 在弹出窗口中点击"高级系统设置"
- 在接着弹出的窗口中点击"高级”->"环境变量"
- 在新窗口上找到名称为"Path",选中该变量,然后点击编辑变量。把MinGW默认安装的地址复制进去,未完!还需要在地址后面加上 \bin
- 最后打开CMD工具,输入gcc -v查看环境是否配置成功
VSCode开发环境搭建
打开VSCode,点击插件商店
搜索PlatformIO IDE插件并安装
Arduino工程创建
新建Arduino工程
进入PlatformIO主页新建工程,工程具体内容参考图片即可,安装位置选中默认即可,新建好后会自动跳到工程目录。也可以在PlatformIO主页的Projects模块中找到工程。
安装Adafruit NeoPixel库
复制代码到main.cpp文件中
代码
已经有大佬写好了代码,参考如下。
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 23
// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(300, PIN, NEO_GRB + NEO_KHZ800);
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
void clear(){
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, 0);
delay(10);
}
}
//流星
void meteor(uint8_t red, uint8_t green, uint8_t blue, uint8_t wait) {
const uint8_t num = 15;
uint8_t max_color = red;
if(green > max_color)
max_color = green;
if(blue > max_color)
max_color = blue;
uint8_t instance = (max_color-200)/num;
for(uint16_t i=0; i<strip.numPixels() + num; i++) {
for(uint8_t j = 0; j < num; j ++){
if(i - j >= 0 && i - j < strip.numPixels()){
int red_after = red - (instance * j);
int green_after = green - (instance * j);
int blue_after = blue - (instance * j);
if(j>=1){
red_after -= 200;
green_after -= 200;
blue_after -= 200;
}
strip.setPixelColor(i - j, strip.Color(red_after >= 0 ? red_after : 0, green_after >= 0 ? green_after : 0, blue_after >= 0 ? blue_after : 0));
}
}
if(i - num >= 0 && i-num < strip.numPixels())
strip.setPixelColor(i-num, 0);
strip.show();
delay(wait);
}
}
void meteor_overturn(uint8_t red, uint8_t green, uint8_t blue, uint8_t wait) {
const uint8_t num = 15;
uint8_t max_color = red;
if(green > max_color)
max_color = green;
if(blue > max_color)
max_color = blue;
uint8_t instance = (max_color-200)/num;
for(int i=strip.numPixels() - 1; i>=-num; i--) {
for(uint8_t j = 0; j < num; j ++){
if(i + j >= 0 && i + j < strip.numPixels()){
int red_after = red - instance * j;
int green_after = green - instance * j;
int blue_after = blue - instance * j;
if(j>=1){
red_after -= 200;
green_after -= 200;
blue_after -= 200;
}
strip.setPixelColor(i + j, strip.Color(red_after >= 0 ? red_after : 0, green_after >= 0 ? green_after : 0, blue_after >= 0 ? blue_after : 0));
}
}
if(i + num >= 0 && i+num < strip.numPixels())
strip.setPixelColor(i+num, 0);
strip.show();
delay(wait);
}
}
void setup() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
strip.begin();
strip.setBrightness(100);
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// 递进渐变
// Some example procedures showing how to display to the pixels:
// colorWipe(strip.Color(255, 0, 0), 20); // Red
// colorWipe(strip.Color(0, 255, 0), 20); // Green
// colorWipe(strip.Color(0, 0, 255), 20); // Blue
// 交叉突变
// Send a theater pixel chase in...
// theaterChase(strip.Color(127, 127, 127), 50); // White
// theaterChase(strip.Color(127, 0, 0), 50); // Red
// theaterChase(strip.Color(0, 0, 127), 50); // Blue
// 整体渐变
rainbow(12);
// 呼吸灯效果(rainbow进阶版本)
// rainbowCycle(10);
// 交叉突变 + 整体渐变(rainbow进阶版本)
// theaterChaseRainbow(50);
// 流星
// clear();
// meteor(255, 0, 0, 10);
// meteor_overturn(255, 0, 0, 10);
}
代码修改注意点
需要指定IO脚和灯的数量,IO脚默认23即可,灯的数量根据你需要连接的灯带确定。一米WS2812有60个灯,计算总数然后替换上去即可。
烧录
连接ESP32开发板
开发板通过数据线连接上电脑,在PlatformIO主页的Devices模块可以看到新出现的设备(具体名称根据系统有区别)。(因为ESP大部分开发板内置了驱动,所以应该是不需要再去进行驱动安装。)
编译和烧录Arduino工程
PlatformIO插件提供了方便操作的按钮,先完成1.编译工程后进行2.烧录工程即可
开发板连接灯带
ESP32开发板 IO口文档
前面提到我们定义的IO脚是23,同时灯带头应该是有三根线,分别是红绿白。连接ESP32开发板时,白线需要接地,红线接电源,绿线接我们定义的IO口即可。连接的时候先将灯带的线使用杜邦线连上,起到延长线的作用,然后再使用杜邦线连接ESP开发板即可。
效果图
总结
制作过程其实很简单,毕竟连灯光算法都不是自己写的。但是最后实现的效果总是不错的,希望下次能够自己实现更好的玩意。
常见问题
为什么选择Arduino框架而不是官方的ESP-IDF框架?
新手入门最重要的是开箱即用,ESP-IDF框架相对Arduino框架更加偏向底层一些,Arduino框架使用一些成熟的库可以更快实现应用,就算遇到问题也能得到社区很好的支持。学习由浅入深,可以先由比较通用的方式入手进行学习比较好。