Files
gunner47-GyverLamp/firmware/GyverLamp_v1.4/effects.ino

733 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ============= ЭФФЕКТЫ ===============
// ------------- конфетти --------------
#define FADE_OUT_SPEED (70U) // скорость затухания
void sparklesRoutine()
{
for (uint8_t i = 0; i < modes[EFF_SPARKLES].Scale; i++)
{
uint8_t x = random(0U, WIDTH);
uint8_t y = random(0U, HEIGHT);
if (getPixColorXY(x, y) == 0U)
{
leds[getPixelNumber(x, y)] = CHSV(random(0U, 255U), 255U, 255U);
}
}
fader(FADE_OUT_SPEED);
}
// функция плавного угасания цвета для всех пикселей
void fader(uint8_t step)
{
for (uint8_t i = 0U; i < WIDTH; i++)
{
for (uint8_t j = 0U; j < HEIGHT; j++)
{
fadePixel(i, j, step);
}
}
}
void fadePixel(uint8_t i, uint8_t j, uint8_t step) // новый фейдер
{
int32_t pixelNum = getPixelNumber(i, j);
if (getPixColor(pixelNum) == 0U) return;
if (leds[pixelNum].r >= 30U ||
leds[pixelNum].g >= 30U ||
leds[pixelNum].b >= 30U)
{
leds[pixelNum].fadeToBlackBy(step);
}
else
{
leds[pixelNum] = 0U;
}
}
// ------------- огонь -----------------
#define SPARKLES (1U) // вылетающие угольки вкл выкл
uint8_t line[WIDTH];
uint8_t pcnt = 0U;
//these values are substracetd from the generated values to give a shape to the animation
static const uint8_t valueMask[8][16] PROGMEM =
{
{32 , 0 , 0 , 0 , 0 , 0 , 0 , 32 , 32 , 0 , 0 , 0 , 0 , 0 , 0 , 32 },
{64 , 0 , 0 , 0 , 0 , 0 , 0 , 64 , 64 , 0 , 0 , 0 , 0 , 0 , 0 , 64 },
{96 , 32 , 0 , 0 , 0 , 0 , 32 , 96 , 96 , 32 , 0 , 0 , 0 , 0 , 32 , 96 },
{128, 64 , 32 , 0 , 0 , 32 , 64 , 128, 128, 64 , 32 , 0 , 0 , 32 , 64 , 128},
{160, 96 , 64 , 32 , 32 , 64 , 96 , 160, 160, 96 , 64 , 32 , 32 , 64 , 96 , 160},
{192, 128, 96 , 64 , 64 , 96 , 128, 192, 192, 128, 96 , 64 , 64 , 96 , 128, 192},
{255, 160, 128, 96 , 96 , 128, 160, 255, 255, 160, 128, 96 , 96 , 128, 160, 255},
{255, 192, 160, 128, 128, 160, 192, 255, 255, 192, 160, 128, 128, 160, 192, 255}
};
//these are the hues for the fire,
//should be between 0 (red) to about 25 (yellow)
static const uint8_t hueMask[8][16] PROGMEM =
{
{1 , 11, 19, 25, 25, 22, 11, 1 , 1 , 11, 19, 25, 25, 22, 11, 1 },
{1 , 8 , 13, 19, 25, 19, 8 , 1 , 1 , 8 , 13, 19, 25, 19, 8 , 1 },
{1 , 8 , 13, 16, 19, 16, 8 , 1 , 1 , 8 , 13, 16, 19, 16, 8 , 1 },
{1 , 5 , 11, 13, 13, 13, 5 , 1 , 1 , 5 , 11, 13, 13, 13, 5 , 1 },
{1 , 5 , 11, 11, 11, 11, 5 , 1 , 1 , 5 , 11, 11, 11, 11, 5 , 1 },
{0 , 1 , 5 , 8 , 8 , 5 , 1 , 0 , 0 , 1 , 5 , 8 , 8 , 5 , 1 , 0 },
{0 , 0 , 1 , 5 , 5 , 1 , 0 , 0 , 0 , 0 , 1 , 5 , 5 , 1 , 0 , 0 },
{0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 }
};
void fireRoutine(bool isColored) // true - цветной огонь, false - белый
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
generateLine();
memset(matrixValue, 0, sizeof(matrixValue));
}
if (pcnt >= 100)
{
shiftUp();
generateLine();
pcnt = 0;
}
drawFrame(pcnt, isColored);
pcnt += 30;
}
// Randomly generate the next line (matrix row)
void generateLine()
{
for (uint8_t x = 0U; x < WIDTH; x++)
{
line[x] = random(64, 255);
}
}
void shiftUp()
{
for (uint8_t y = HEIGHT - 1U; y > 0U; y--)
{
for (uint8_t x = 0U; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15U) newX = x % 16U;
if (y > 7U) continue;
matrixValue[y][newX] = matrixValue[y - 1U][newX];
}
}
for (uint8_t x = 0U; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15U) newX = x % 16U;
matrixValue[0U][newX] = line[newX];
}
}
// draw a frame, interpolating between 2 "key frames"
// @param pcnt percentage of interpolation
void drawFrame(uint8_t pcnt, bool isColored)
{
int32_t nextv;
//each row interpolates with the one before it
for (uint8_t y = HEIGHT - 1U; y > 0U; y--)
{
for (uint8_t x = 0U; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15U) newX = x % 16U;
if (y < 8U)
{
nextv =
(((100.0 - pcnt) * matrixValue[y][newX]
+ pcnt * matrixValue[y - 1][newX]) / 100.0)
- pgm_read_byte(&valueMask[y][newX]);
CRGB color = CHSV(
isColored ? modes[EFF_FIRE].Scale * 2.5 + pgm_read_byte(&hueMask[y][newX]) : 0U, // H
isColored ? 255U : 0U, // S
(uint8_t)max(0, nextv) // V
);
leds[getPixelNumber(x, y)] = color;
}
else if (y == 8U && SPARKLES)
{
if (random(0, 20) == 0 && getPixColorXY(x, y - 1U) != 0U) drawPixelXY(x, y, getPixColorXY(x, y - 1U));
else drawPixelXY(x, y, 0U);
}
else if (SPARKLES)
{
// старая версия для яркости
if (getPixColorXY(x, y - 1U) > 0U)
drawPixelXY(x, y, getPixColorXY(x, y - 1U));
else drawPixelXY(x, y, 0U);
}
}
}
//first row interpolates with the "next" line
for (uint8_t x = 0U; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15U) newX = x % 16U;
CRGB color = CHSV(
isColored ? modes[EFF_FIRE].Scale * 2.5 + pgm_read_byte(&(hueMask[0][newX])): 0U, // H
isColored ? 255U : 0U, // S
(uint8_t)(((100.0 - pcnt) * matrixValue[0][newX] + pcnt * line[newX]) / 100.0) // V
);
//leds[getPixelNumber(newX, 0)] = color; // на форуме пишут что это ошибка - вместо newX должно быть x, иначе
leds[getPixelNumber(x, 0)] = color; // на матрицах шире 16 столбцов нижний правый угол неработает
}
}
// ------------- радуга вертикальная ----------------
uint8_t hue;
void rainbowVerticalRoutine()
{
hue += 4;
for (uint8_t j = 0; j < HEIGHT; j++)
{
CHSV thisColor = CHSV((uint8_t)(hue + j * modes[EFF_RAINBOW_VER].Scale), 255, 255);
for (uint8_t i = 0U; i < WIDTH; i++)
{
drawPixelXY(i, j, thisColor);
}
}
}
// ------------- радуга горизонтальная ----------------
void rainbowHorizontalRoutine()
{
hue += 4;
for (uint8_t i = 0U; i < WIDTH; i++)
{
CHSV thisColor = CHSV((uint8_t)(hue + i * modes[EFF_RAINBOW_HOR].Scale), 255, 255);
for (uint8_t j = 0U; j < HEIGHT; j++)
{
drawPixelXY(i, j, thisColor);
}
}
}
// ------------- радуга диагональная -------------
void rainbowDiagonalRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
}
hue += 8;
for (uint8_t i = 0U; i < WIDTH; i++)
{
for (uint8_t j = 0U; j < HEIGHT; j++)
{
float twirlFactor = 3.0F * (modes[EFF_RAINBOW_DIAG].Scale / 100.0F); // на сколько оборотов будет закручена матрица, [0..3]
CRGB thisColor = CHSV((uint8_t)(hue + (float)(WIDTH / HEIGHT * i + j * twirlFactor) * (float)(255 / maxDim)), 255, 255);
drawPixelXY(i, j, thisColor);
}
}
}
// ------------- цвета -----------------
void colorsRoutine()
{
if (loadingFlag)
{
hue += modes[EFF_COLORS].Scale;
for (uint16_t i = 0U; i < NUM_LEDS; i++)
{
leds[i] = CHSV(hue, 255U, 255U);
}
}
}
// ------------- цвет ------------------
void colorRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
for (int16_t i = 0U; i < NUM_LEDS; i++)
{
leds[i] = CHSV(modes[EFF_COLOR].Scale * 2.5, 255U, 255U);
}
}
}
// ------------- снегопад ----------
void snowRoutine()
{
// сдвигаем всё вниз
for (uint8_t x = 0U; x < WIDTH; x++)
{
for (uint8_t y = 0U; y < HEIGHT - 1; y++)
{
drawPixelXY(x, y, getPixColorXY(x, y + 1U));
}
}
for (uint8_t x = 0U; x < WIDTH; x++)
{
// заполняем случайно верхнюю строку
// а также не даём двум блокам по вертикали вместе быть
if (getPixColorXY(x, HEIGHT - 2U) == 0U && (random(0, 100 - modes[EFF_SNOW].Scale) == 0U))
drawPixelXY(x, HEIGHT - 1U, 0xE0FFFF - 0x101010 * random(0, 4));
else
drawPixelXY(x, HEIGHT - 1U, 0x000000);
}
}
// ------------- метель -------------
#define SNOW_DENSE (60U) // плотность снега
#define SNOW_TAIL_STEP (100U) // длина хвоста
#define SNOW_SATURATION (0U) // насыщенность (от 0 до 255)
void snowStormRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
}
// заполняем головами комет левую и верхнюю линию
for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++)
{
if (getPixColorXY(0U, i) == 0U &&
(random(0, SNOW_DENSE) == 0) &&
getPixColorXY(0U, i + 1U) == 0U &&
getPixColorXY(0U, i - 1U) == 0U)
{
leds[getPixelNumber(0U, i)] = CHSV(random(0, 200), SNOW_SATURATION, 255U);
}
}
for (uint8_t i = 0U; i < WIDTH / 2U; i++)
{
if (getPixColorXY(i, HEIGHT - 1U) == 0U &&
(random(0, map(modes[EFF_SNOWSTORM].Scale, 0U, 255U, 10U, 120U)) == 0U) &&
getPixColorXY(i + 1U, HEIGHT - 1U) == 0U &&
getPixColorXY(i - 1U, HEIGHT - 1U) == 0U)
{
leds[getPixelNumber(i, HEIGHT - 1U)] = CHSV(random(0, 200), SNOW_SATURATION, 255U);
}
}
// сдвигаем по диагонали
for (uint8_t y = 0U; y < HEIGHT - 1U; y++)
{
for (uint8_t x = WIDTH - 1U; x > 0U; x--)
{
drawPixelXY(x, y, getPixColorXY(x - 1U, y + 1U));
}
}
// уменьшаем яркость левой и верхней линии, формируем "хвосты"
for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++)
{
fadePixel(0U, i, SNOW_TAIL_STEP);
}
for (uint8_t i = 0U; i < WIDTH / 2U; i++)
{
fadePixel(i, HEIGHT - 1U, SNOW_TAIL_STEP);
}
}
// ------------- звездопад -------------
#define STAR_DENSE (60U) // плотность комет
#define STAR_TAIL_STEP (100U) // длина хвоста кометы
#define STAR_SATURATION (150U) // насыщенность кометы (от 0 до 255)
void starfallRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
}
// заполняем головами комет левую и верхнюю линию
for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++)
{
if (getPixColorXY(0U, i) == 0U &&
(random(0, STAR_DENSE) == 0) &&
getPixColorXY(0U, i + 1U) == 0U &&
getPixColorXY(0U, i - 1U) == 0U)
{
leds[getPixelNumber(0U, i)] = CHSV(random(0, 200), STAR_SATURATION, 255U);
}
}
for (uint8_t i = 0U; i < WIDTH / 2U; i++)
{
if (getPixColorXY(i, HEIGHT - 1U) == 0U &&
(random(0, map(modes[EFF_STARFALL].Scale, 0U, 255U, 10U, 120U)) == 0U) &&
getPixColorXY(i + 1U, HEIGHT - 1U) == 0U &&
getPixColorXY(i - 1U, HEIGHT - 1U) == 0U)
{
leds[getPixelNumber(i, HEIGHT - 1U)] = CHSV(random(0, 200), STAR_SATURATION, 255U);
}
}
// сдвигаем по диагонали
for (uint8_t y = 0U; y < HEIGHT - 1U; y++)
{
for (uint8_t x = WIDTH - 1U; x > 0U; x--)
{
drawPixelXY(x, y, getPixColorXY(x - 1U, y + 1U));
}
}
// уменьшаем яркость левой и верхней линии, формируем "хвосты"
for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++)
{
fadePixel(0U, i, STAR_TAIL_STEP);
}
for (uint8_t i = 0U; i < WIDTH / 2U; i++)
{
fadePixel(i, HEIGHT - 1U, STAR_TAIL_STEP);
}
}
// ------------- матрица ---------------
void matrixRoutine()
{
for (uint8_t x = 0U; x < WIDTH; x++)
{
// заполняем случайно верхнюю строку
uint32_t thisColor = getPixColorXY(x, HEIGHT - 1U);
if (thisColor == 0U)
drawPixelXY(x, HEIGHT - 1U, 0x00FF00 * (random(0, 100 - modes[EFF_MATRIX].Scale) == 0U));
else if (thisColor < 0x002000)
drawPixelXY(x, HEIGHT - 1U, 0U);
else
drawPixelXY(x, HEIGHT - 1U, thisColor - 0x002000);
}
// сдвигаем всё вниз
for (uint8_t x = 0U; x < WIDTH; x++)
{
for (uint8_t y = 0U; y < HEIGHT - 1U; y++)
{
drawPixelXY(x, y, getPixColorXY(x, y + 1U));
}
}
}
// ------------- светлячки --------------
#define LIGHTERS_AM (100U)
int32_t lightersPos[2U][LIGHTERS_AM];
int8_t lightersSpeed[2U][LIGHTERS_AM];
CHSV lightersColor[LIGHTERS_AM];
uint8_t loopCounter;
int32_t angle[LIGHTERS_AM];
int32_t speedV[LIGHTERS_AM];
int8_t angleSpeed[LIGHTERS_AM];
void lightersRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
randomSeed(millis());
for (uint8_t i = 0U; i < LIGHTERS_AM; i++)
{
lightersPos[0U][i] = random(0, WIDTH * 10);
lightersPos[1U][i] = random(0, HEIGHT * 10);
lightersSpeed[0U][i] = random(-10, 10);
lightersSpeed[1U][i] = random(-10, 10);
lightersColor[i] = CHSV(random(0U, 255U), 255U, 255U);
}
}
FastLED.clear();
if (++loopCounter > 20U) loopCounter = 0U;
for (uint8_t i = 0U; i < modes[EFF_LIGHTERS].Scale; i++)
{
if (loopCounter == 0U) // меняем скорость каждые 255 отрисовок
{
lightersSpeed[0U][i] += random(-3, 4);
lightersSpeed[1U][i] += random(-3, 4);
lightersSpeed[0U][i] = constrain(lightersSpeed[0U][i], -20, 20);
lightersSpeed[1U][i] = constrain(lightersSpeed[1U][i], -20, 20);
}
lightersPos[0U][i] += lightersSpeed[0U][i];
lightersPos[1U][i] += lightersSpeed[1U][i];
if (lightersPos[0U][i] < 0) lightersPos[0U][i] = (WIDTH - 1) * 10;
if (lightersPos[0U][i] >= (int32_t)(WIDTH * 10)) lightersPos[0U][i] = 0;
if (lightersPos[1U][i] < 0)
{
lightersPos[1U][i] = 0;
lightersSpeed[1U][i] = -lightersSpeed[1U][i];
}
if (lightersPos[1U][i] >= (int32_t)(HEIGHT - 1) * 10)
{
lightersPos[1U][i] = (HEIGHT - 1U) * 10;
lightersSpeed[1U][i] = -lightersSpeed[1U][i];
}
drawPixelXY(lightersPos[0U][i] / 10, lightersPos[1U][i] / 10, lightersColor[i]);
}
}
// ------------- светлячки со шлейфом -------------
#define BALLS_AMOUNT (3U) // количество "шариков"
#define CLEAR_PATH (1U) // очищать путь
#define BALL_TRACK (1U) // (0 / 1) - вкл/выкл следы шариков
#define TRACK_STEP (70U) // длина хвоста шарика (чем больше цифра, тем хвост короче)
int16_t coord[BALLS_AMOUNT][2U];
int8_t vector[BALLS_AMOUNT][2U];
CRGB ballColors[BALLS_AMOUNT];
void ballsRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
for (uint8_t j = 0U; j < BALLS_AMOUNT; j++)
{
int8_t sign;
// забиваем случайными данными
coord[j][0U] = WIDTH / 2 * 10;
random(0, 2) ? sign = 1 : sign = -1;
vector[j][0U] = random(4, 15) * sign;
coord[j][1U] = HEIGHT / 2 * 10;
random(0, 2) ? sign = 1 : sign = -1;
vector[j][1U] = random(4, 15) * sign;
ballColors[j] = CHSV(random(0, 9) * 28, 255U, 255U);
}
}
if (!BALL_TRACK) // режим без следов шариков
{
FastLED.clear();
}
else // режим со следами
{
fader(TRACK_STEP);
}
// движение шариков
for (uint8_t j = 0U; j < BALLS_AMOUNT; j++)
{
// движение шариков
for (uint8_t i = 0U; i < 2U; i++)
{
coord[j][i] += vector[j][i];
if (coord[j][i] < 0)
{
coord[j][i] = 0;
vector[j][i] = -vector[j][i];
}
}
if (coord[j][0U] > (int16_t)((WIDTH - 1) * 10))
{
coord[j][0U] = (WIDTH - 1) * 10;
vector[j][0U] = -vector[j][0U];
}
if (coord[j][1U] > (int16_t)((HEIGHT - 1) * 10))
{
coord[j][1U] = (HEIGHT - 1) * 10;
vector[j][1U] = -vector[j][1U];
}
leds[getPixelNumber(coord[j][0U] / 10, coord[j][1U] / 10)] = ballColors[j];
}
}
// ------------- пейнтбол -------------
const uint8_t BorderWidth = 2U;
void lightBallsRoutine()
{
// Apply some blurring to whatever's already on the matrix
// Note that we never actually clear the matrix, we just constantly
// blur it repeatedly. Since the blurring is 'lossy', there's
// an automatic trend toward black -- by design.
uint8_t blurAmount = dim8_raw(beatsin8(3,64,100));
blur2d(leds, WIDTH, HEIGHT, blurAmount);
// Use two out-of-sync sine waves
uint8_t i = beatsin8( 91, BorderWidth, WIDTH-BorderWidth);
uint8_t j = beatsin8( 109, BorderWidth, WIDTH-BorderWidth);
uint8_t k = beatsin8( 73, BorderWidth, WIDTH-BorderWidth);
uint8_t m = beatsin8( 123, BorderWidth, WIDTH-BorderWidth);
// The color of each point shifts over time, each at a different speed.
uint16_t ms = millis();
leds[XY( i, j)] += CHSV( ms / 29, 200U, 255U);
leds[XY( j, k)] += CHSV( ms / 41, 200U, 255U);
leds[XY( k, m)] += CHSV( ms / 73, 200U, 255U);
leds[XY( m, i)] += CHSV( ms / 97, 200U, 255U);
}
// Trivial XY function for the SmartMatrix; use a different XY
// function for different matrix grids. See XYMatrix example for code.
uint16_t XY(uint8_t x, uint8_t y)
{
uint16_t i;
if (y & 0x01)
{
// Odd rows run backwards
uint8_t reverseX = (WIDTH - 1) - x;
i = (y * WIDTH) + reverseX;
}
else
{
// Even rows run forwards
i = (y * WIDTH) + x;
}
return i;
}
// ------------- блуждающий кубик -------------
#define RANDOM_COLOR (1U) // случайный цвет при отскоке
int16_t coordB[2U];
int8_t vectorB[2U];
CRGB ballColor;
int8_t ballSize;
void ballRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
//FastLED.clear();
for (uint8_t i = 0U; i < 2U; i++)
{
coordB[i] = WIDTH / 2 * 10;
vectorB[i] = random(8, 20);
ballColor = CHSV(random(0, 9) * 28, 255U, 255U);
}
}
ballSize = map(modes[EFF_CUBE].Scale, 0U, 255U, 2U, max((uint8_t)min(WIDTH,HEIGHT) / 3, 2));
for (uint8_t i = 0U; i < 2U; i++)
{
coordB[i] += vectorB[i];
if (coordB[i] < 0)
{
coordB[i] = 0;
vectorB[i] = -vectorB[i];
if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255U, 255U);
//vectorB[i] += random(0, 6) - 3;
}
}
if (coordB[0U] > (int16_t)((WIDTH - ballSize) * 10))
{
coordB[0U] = (WIDTH - ballSize) * 10;
vectorB[0U] = -vectorB[0U];
if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255U, 255U);
//vectorB[0] += random(0, 6) - 3;
}
if (coordB[1U] > (int16_t)((HEIGHT - ballSize) * 10))
{
coordB[1U] = (HEIGHT - ballSize) * 10;
vectorB[1U] = -vectorB[1U];
if (RANDOM_COLOR)
{
ballColor = CHSV(random(0, 9) * 28, 255U, 255U);
}
//vectorB[1] += random(0, 6) - 3;
}
FastLED.clear();
for (uint8_t i = 0U; i < ballSize; i++)
{
for (uint8_t j = 0U; j < ballSize; j++)
{
leds[getPixelNumber(coordB[0U] / 10 + i, coordB[1U] / 10 + j)] = ballColor;
}
}
}
// ------------- белый свет -------------
void whiteColorRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
for (uint16_t i = 0U; i < NUM_LEDS; i++)
{
leds[i] = CHSV(0U, 0U, 255U);
}
}
}
// ------------- белый свет (светится горизонтальная полоса по центру лампы; масштаб - высота центральной горизонтальной полосы; скорость - регулировка от холодного к тёплому; яркость - общая яркость) -------------
void whiteColorStripeRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
delay(1);
uint8_t centerY = max((uint8_t)round(HEIGHT / 2.0F) - 1, 0);
uint8_t bottomOffset = (uint8_t)(!(HEIGHT & 1) && (HEIGHT > 1)); // если высота матрицы чётная, линий с максимальной яркостью две, а линии с минимальной яркостью снизу будут смещены на один ряд
for (int16_t y = centerY; y >= 0; y--)
{
CRGB color = CHSV(
45U, // определяем тон
map(modes[EFF_WHITE_COLOR].Speed, 0U, 255U, 0U, 170U), // определяем насыщенность
y == centerY // определяем яркость
? 255U // для центральной горизонтальной полосы (или двух) яркость всегда равна 255
: (modes[EFF_WHITE_COLOR].Scale / 100.0F) > ((centerY + 1.0F) - (y + 1.0F)) / (centerY + 1.0F) ? 255U : 0U); // для остальных горизонтальных полос яркость равна либо 255, либо 0 в зависимости от масштаба
for (uint8_t x = 0U; x < WIDTH; x++)
{
drawPixelXY(x, y, color); // при чётной высоте матрицы максимально яркими отрисуются 2 центральных горизонтальных полосы
drawPixelXY(x, max((uint8_t)(HEIGHT - 1U) - (y + 1U) + bottomOffset, 0U), color); // при нечётной - одна, но дважды
}
}
}
}
// ------------- мигающий цвет (не эффект! используется для отображения краткосрочного предупреждения; блокирующий код!) -------------
#define WARNING_BRIGHTNESS (10U) // яркость вспышки
void showWarning(
CRGB color, /* цвет вспышки */
uint32_t duration, /* продолжительность отображения предупреждения (общее время) */
uint16_t blinkHalfPeriod) /* продолжительность одной вспышки в миллисекундах (полупериод) */
{
uint32_t blinkTimer = millis();
enum BlinkState { OFF = 0, ON = 1 } blinkState = BlinkState::OFF;
FastLED.setBrightness(WARNING_BRIGHTNESS); // установка яркости для предупреждения
FastLED.clear();
delay(2);
FastLED.show();
for (uint16_t i = 0U; i < NUM_LEDS; i++) // установка цвета всех диодов в WARNING_COLOR
{
leds[i] = color;
}
uint32_t startTime = millis();
while (millis() - startTime <= (duration + 5)) // блокировка дальнейшего выполнения циклом на время отображения предупреждения
{
if (millis() - blinkTimer >= blinkHalfPeriod) // переключение вспышка/темнота
{
blinkTimer = millis();
blinkState = (BlinkState)!blinkState;
FastLED.setBrightness(blinkState == BlinkState::OFF ? 0 : WARNING_BRIGHTNESS);
delay(1);
FastLED.show();
}
delay(50);
}
FastLED.clear();
FastLED.setBrightness(ONflag ? modes[currentMode].Brightness : 0); // установка яркости, которая была выставлена до вызова предупреждения
delay(1);
FastLED.show();
loadingFlag = true; // принудительное отображение текущего эффекта (того, что был активен перед предупреждением)
}