#include "FastLED.h" // подключаем библиотеку фастлед
#define NUM_LEDS 168
#define WIDTH 24
#define HEIGHT 7
#define MATRIX_TYPE 1 // тип матрицы: 0 - зигзаг, 1 - параллельная
#define CONNECTION_ANGLE 1 // угол подключения: 0 - левый нижний, 1 - левый верхний, 2 - правый верхний, 3 - правый нижний
#define STRIP_DIRECTION 0 // направление ленты из угла: 0 - вправо, 1 - вверх, 2 - влево, 3 - вниз
#define BALLS_AMOUNT 3 // количество "шариков"
#define CLEAR_PATH 0 // очищать путь
#define BALL_TRACK 1 // (0 / 1) - вкл/выкл следы шариков
#define DRAW_WALLS 0 // режим с рисованием препятствий для шаров (не работает на ESP и STM32)
#define TRACK_STEP 100 // длина хвоста шарика (чем больше цифра, тем хвост короче)
#define PIN 6
CRGB leds[NUM_LEDS];
byte bright = 255; // яркость LED светодиодов
int coord[BALLS_AMOUNT][2];
int8_t vector[BALLS_AMOUNT][2];
CRGB ballColors[BALLS_AMOUNT];
boolean loadingFlag = true;
uint32_t globalColor = 0x00ff00; // цвет при запуске зелёный
void setup() {
FastLED.addLeds <WS2812, PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(bright);
}
// **************** НАСТРОЙКА МАТРИЦЫ ****************
#if (CONNECTION_ANGLE == 0 && STRIP_DIRECTION == 0)
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y y
#elif (CONNECTION_ANGLE == 0 && STRIP_DIRECTION == 1)
#define _WIDTH HEIGHT
#define THIS_X y
#define THIS_Y x
#elif (CONNECTION_ANGLE == 1 && STRIP_DIRECTION == 0)
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y (HEIGHT - y - 1)
#elif (CONNECTION_ANGLE == 1 && STRIP_DIRECTION == 3)
#define _WIDTH HEIGHT
#define THIS_X (HEIGHT - y - 1)
#define THIS_Y x
#elif (CONNECTION_ANGLE == 2 && STRIP_DIRECTION == 2)
#define _WIDTH WIDTH
#define THIS_X (WIDTH - x - 1)
#define THIS_Y (HEIGHT - y - 1)
#elif (CONNECTION_ANGLE == 2 && STRIP_DIRECTION == 3)
#define _WIDTH HEIGHT
#define THIS_X (HEIGHT - y - 1)
#define THIS_Y (WIDTH - x - 1)
#elif (CONNECTION_ANGLE == 3 && STRIP_DIRECTION == 2)
#define _WIDTH WIDTH
#define THIS_X (WIDTH - x - 1)
#define THIS_Y y
#elif (CONNECTION_ANGLE == 3 && STRIP_DIRECTION == 1)
#define _WIDTH HEIGHT
#define THIS_X y
#define THIS_Y (WIDTH - x - 1)
#else
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y y
#endif
void loop() {
if (loadingFlag) {
loadingFlag = false;
for (byte j = 0; j < BALLS_AMOUNT; j++) {
int sign;
// забиваем случайными данными
coord[j][0] = WIDTH / 2 * 10;
random(0, 2) ? sign = 1 : sign = -1;
vector[j][0] = random(4, 15) * sign;
coord[j][1] = HEIGHT / 2 * 10;
random(0, 2) ? sign = 1 : sign = -1;
vector[j][1] = random(4, 15) * sign;
ballColors[j] = CHSV(random(0, 9) * 28, 255, 255);
}
}
if (!BALL_TRACK) // если режим БЕЗ следов шариков
FastLED.clear(); // очистить
else { // режим со следами
fader(TRACK_STEP);
}
// движение шариков
for (byte j = 0; j < BALLS_AMOUNT; j++) {
// отскок от нарисованных препятствий
if (DRAW_WALLS) {
uint32_t thisColor = getPixColorXY(coord[j][0] / 10 + 1, coord[j][1] / 10);
if (thisColor == globalColor/* && vector[j][0] > 0*/) {
vector[j][0] = -vector[j][0];
}
thisColor = getPixColorXY(coord[j][0] / 10 - 1, coord[j][1] / 10);
if (thisColor == globalColor/* && vector[j][0] < 0*/) {
vector[j][0] = -vector[j][0];
}
thisColor = getPixColorXY(coord[j][0] / 10, coord[j][1] / 10 + 1);
if (thisColor == globalColor/* && vector[j][1] > 0*/) {
vector[j][1] = -vector[j][1];
}
thisColor = getPixColorXY(coord[j][0] / 10, coord[j][1] / 10 - 1);
if (thisColor == globalColor/* && vector[j][1] < 0*/) {
vector[j][1] = -vector[j][1];
}
}
// движение шариков
for (byte i = 0; i < 2; 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][0] > (WIDTH - 1) * 10) {
coord[j][0] = (WIDTH - 1) * 10;
vector[j][0] = -vector[j][0];
}
if (coord[j][1] > (HEIGHT - 1) * 10) {
coord[j][1] = (HEIGHT - 1) * 10;
vector[j][1] = -vector[j][1];
}
leds[getPixelNumber(coord[j][0] / 10, coord[j][1] / 10)] = ballColors[j];
}
FastLED.setBrightness(bright);
FastLED.show();
delay(100);
}
// получить номер пикселя в ленте по координатам
uint16_t getPixelNumber(int8_t x, int8_t y) {
if ((THIS_Y % 2 == 0) || MATRIX_TYPE) { // если чётная строка
return (THIS_Y * _WIDTH + THIS_X);
} else { // если нечётная строка
return (THIS_Y * _WIDTH + _WIDTH - THIS_X - 1);
}
}
// функция отрисовки точки по координатам X Y
void drawPixelXY(int8_t x, int8_t y, CRGB color) {
if (x < 0 || x > WIDTH - 1 || y < 0 || y > HEIGHT - 1) return;
int thisPixel = getPixelNumber(x, y);
for (byte i = 0; i < 1; i++) {
leds[thisPixel + i] = color;
}
}
// функция получения цвета пикселя в матрице по его координатам
uint32_t getPixColorXY(int8_t x, int8_t y) {
return getPixColor(getPixelNumber(x, y));
}
// функция получения цвета пикселя по его номеру
uint32_t getPixColor(int thisSegm) {
int thisPixel = thisSegm;
if (thisPixel < 0 || thisPixel > NUM_LEDS - 1) return 0;
return (((uint32_t)leds[thisPixel].r << 16) | ((long)leds[thisPixel].g << 8 ) | (long)leds[thisPixel].b);
}
// функция плавного угасания цвета для всех пикселей
void fader(byte step) {
for (byte i = 0; i < WIDTH; i++) {
for (byte j = 0; j < HEIGHT; j++) {
fadePixel(i, j, step);
}
}
}
void fadePixel(byte i, byte j, byte step) { // новый фейдер
int pixelNum = getPixelNumber(i, j);
if (getPixColor(pixelNum) == 0) return;
if (leds[pixelNum].r >= 30 ||
leds[pixelNum].g >= 30 ||
leds[pixelNum].b >= 30) {
leds[pixelNum].fadeToBlackBy(step);
} else {
leds[pixelNum] = 0;
}
}