【花雕动手做】有趣好玩的音乐可视化系列小项目(22)--LED无限魔方
项目程序之六:一个用 FastLED 编程的LED无限魔方体
/*
【花雕动手做】有趣好玩的音乐可视化系列小项目(22)--LED无限魔方
项目程序之六:一个用 FastLED 编程的LED无限魔方体
*/
#include <FastLED.h>
#define LED_PIN 6
#define LEDS_PER_SEGMENT 5
#define SEGMENTS 12
#define BRIGHTNESS 200
#define NUM_LEDS LEDS_PER_SEGMENT * SEGMENTS
#define NUM_LEDS_WITH_SAFETY NUM_LEDS + 1
CRGB source1[NUM_LEDS_WITH_SAFETY];
CRGB source2[NUM_LEDS_WITH_SAFETY];
CRGB output[NUM_LEDS_WITH_SAFETY];
uint8_t blendAmount = 0;
uint8_t patternCounter = 0;
uint8_t source1Pattern = 0;
uint8_t source2Pattern = 1;
bool useSource1 = false;
void setup() {
FastLED.addLeds<WS2812B, LED_PIN, GRB>(output, NUM_LEDS_WITH_SAFETY);
FastLED.setBrightness(BRIGHTNESS);
Serial.begin(57600);
}
void loop() {
EVERY_N_MILLISECONDS(10) {
blend(source1, source2, output, NUM_LEDS, blendAmount); //在两个源之间混合
if (useSource1) {
if (blendAmount < 255) blendAmount++; //混合“向上”到源 2
} else {
if (blendAmount > 0) blendAmount--; //将“向下”混合到源 1
}
}
EVERY_N_SECONDS(8) {
nextPattern();
}
runPattern(source1Pattern, source1); //同时运行两种模式
runPattern(source2Pattern, source2);
FastLED.show();
}
void nextPattern() {
patternCounter = (patternCounter + 1) % 5;
if (useSource1) source1Pattern = patternCounter;
else source2Pattern = patternCounter;
useSource1 = !useSource1;
}
void runPattern(uint8_t pattern, CRGB *LEDarray) {
switch (pattern) {
case 0:
rainbowComet(LEDarray);//彩虹彗星
break;
case 1:
prettyNoise(LEDarray);//漂亮噪声
break;
case 2:
randomStar(LEDarray);//随机星
break;
case 3:
fillRainbow(LEDarray);//填充彩虹
break;
case 4:
pixels(LEDarray);// 像素
break;
}
}
uint8_t xyz(uint8_t x, uint8_t y, uint8_t z) {
/*边缘的坐标从 0 到 5。每条边缘只有 5 个“真实”像素,
* 所以只有 1 - 5 个 LED。缺少顶点(角)。
* 如果请求这些顶点之一,则返回一个不显示的安全像素。
* 我们对立方体内部的坐标也做同样的事情,即不在边缘上。
*/
uint8_t lps = LEDS_PER_SEGMENT;
uint8_t safePx = NUM_LEDS;
if ((x == 0 || x == lps + 1) && (y == 0 || y == lps + 1) && (z == 0 || z == lps + 1)) return safePx;
// z 方向边缘
if (x == 0 && y == 0) return (8 * lps) - z; // Seg 7
if (x == 0 && y == lps + 1) return (12 * lps) - z; // Seg 11
if (x == lps + 1 && y == 0) return (3 * lps) + z - 1; // Seg 3
if (x == lps + 1 && y == lps + 1) return (9 * lps) + z - 1; // Seg 9
// y 方向边
if (x == 0 && z == 0) return y - 1; // Seg 0
if (x == 0 && z == lps + 1) return (7 * lps) - y; // Seg 6
if (x == lps + 1 && z == 0) return (3 * lps) - y; // Seg 2
if (x == lps + 1 && z == lps + 1) return (4 * lps) + y - 1; // Seg 4
// x 方向边
if (y == 0 && z == 0) return (8 * lps) + x - 1; // Seg 8
if (y == 0 && z == lps + 1) return (11 * lps) - x; // Seg 10
if (y == lps + 1 && z == 0) return lps + x - 1; // Seg 1
if (y == lps + 1 && z == lps + 1) return (6 * lps) - x; // Seg 5
//如果以上都不是,我们的坐标无效
return safePx;
}
//------------ Patterns below ------------//
void pixels(CRGB *LEDarray) {
static uint8_t pos = 0;
static uint8_t a = 0;
static uint8_t b = 0;
//填充所有像素并将它们混合在一起
for (int c = 0; c <= LEDS_PER_SEGMENT + 1; c++) {
LEDarray[xyz(a,b,c)] = blend(LEDarray[xyz(a,b,c)], CRGB::Orange, 128);
LEDarray[xyz(a,c,b)] = blend(LEDarray[xyz(a,c,b)], CRGB::Magenta, 128);
LEDarray[xyz(c,a,b)] = blend(LEDarray[xyz(c,a,b)], CRGB::Blue, 128);
}
EVERY_N_MILLISECONDS(33) {
//围绕正方形移动的坐标
if(pos < 15) a++;
else if (pos <= (LEDS_PER_SEGMENT * 2) + 1) b++;
else if (pos <= (LEDS_PER_SEGMENT * 3) + 2) a--;
else b--;
//再次开始我们到达正方形的尽头
pos = (pos + 1) % ((LEDS_PER_SEGMENT + 1) * 4);
}
fadeToBlackBy(LEDarray, NUM_LEDS, 10);
}
void fillRainbow(CRGB *LEDarray) {
static uint8_t pos = 0;
uint8_t noise = inoise8(millis()/5);
fill_rainbow(LEDarray, LEDS_PER_SEGMENT, noise, 10);
//复制到其他段
for (int i = 0; i < SEGMENTS; i++) {
memmove8(&LEDarray[LEDS_PER_SEGMENT * i], &LEDarray[0], LEDS_PER_SEGMENT * sizeof(CRGB));
}
//垂直柱子上下移动的白点
LEDarray[xyz(0, 0, LEDS_PER_SEGMENT - pos)] = CRGB::White;
LEDarray[xyz(0, LEDS_PER_SEGMENT + 1, pos)] = CRGB::White;
LEDarray[xyz(LEDS_PER_SEGMENT + 1, LEDS_PER_SEGMENT + 1, LEDS_PER_SEGMENT - pos)] = CRGB::White;
LEDarray[xyz(LEDS_PER_SEGMENT + 1, 0, pos)] = CRGB::White;
EVERY_N_MILLISECONDS(20) {
pos = (pos + 1) % LEDS_PER_SEGMENT;
}
}
void rainbowComet(CRGB *LEDarray) {
static uint8_t easeOutVal = 0;
static uint8_t easeInVal = 0;
//使图案出现在两个段上
uint8_t ledsPerSegment = LEDS_PER_SEGMENT * 2;
uint8_t segments = SEGMENTS / 2;
easeOutVal = ease8InOutQuad(easeInVal);
easeInVal++;
uint8_t pos = lerp8by8(0, ledsPerSegment, easeOutVal);
uint8_t hue = map(pos, 0, ledsPerSegment, 0, 230);
LEDarray[pos] = CHSV(hue, 255, 255);
fadeToBlackBy(LEDarray, ledsPerSegment, 20);
//复制到其他段
for (int i = 0; i < segments; i++) {
memmove8(&LEDarray[ledsPerSegment * i], &LEDarray[0], ledsPerSegment * sizeof(CRGB));
}
}
void randomStar(CRGB *LEDarray) {
EVERY_N_MILLISECONDS(75) {
LEDarray[random16(0, NUM_LEDS)] = CRGB::LightGrey;
}
for (int i = 0; i < NUM_LEDS; i++) {
// 亮度
uint8_t bNoise = inoise8(i * 100, millis());
bNoise = constrain(bNoise, 50, 200);
bNoise = map(bNoise, 50, 200, 20, 80);
// 色调
uint8_t hNoise = inoise8(i * 20, millis() / 5);
hNoise = constrain(hNoise, 50, 200);
hNoise = map(hNoise, 50, 200, 160, 192);
if (LEDarray[i].g == 0) {
LEDarray[i] = CHSV(hNoise, 255, bNoise);
}
}
fadeToBlackBy(LEDarray, NUM_LEDS, 5);//淡入黑色(LED 阵列,NUM LEDS,5 个)
}
void prettyNoise(CRGB *LEDarray) {
fill_noise16 (LEDarray, NUM_LEDS, 1, 0, 100, 1, 1, 50, millis() / 3, 5);
} 复制代码