#include <SPI.h>
#include <SD.h>
#include <FastLED.h>
// ---------- НАСТРОЙКИ ----------
#define LED_PIN 6 // Пин данных для матрицы
#define LED_COUNT 256 // 16x16 = 256
#define MATRIX_WIDTH 16
#define MATRIX_HEIGHT 16
#define SD_CS_PIN 53 // Chip Select пин для SD-модуля на Arduino Mega
#define STARTING_BRIGHTNESS 150 // Начальная яркость (0-255)
#define EFFECT_CHANGE_INTERVAL 60000UL // 60 секунд = 1 минута
#define NUM_EFFECT_FILES 15 // Количество файлов эффектов (например, 001.out и 002.out)
#define FPS 60 // Частота кадров
#define FRAME_DELAY 1000 / FPS
// -------------------------------
// Создаем массив светодиодов FastLED
CRGB leds[LED_COUNT];
// Глобальные переменные для управления файлами и SD-картой
bool sd_ready = false;
unsigned long lastEffectChange = 0;
int currentEffectIndex = 1;
File currentEffectFile;
// Буфер для одного кадра (256 светодиодов * 3 байта = 768 байт)
uint8_t frameBuffer[LED_COUNT * 3];
// Прототипы функций
void loadNextEffect();
void displayGradient();
uint16_t xyToIndex(uint8_t x, uint8_t y);
void setup() {
// Настройка пина CS для SD-модуля как OUTPUT, это обязательно для Mega
pinMode(SD_CS_PIN, OUTPUT);
digitalWrite(SD_CS_PIN, HIGH);
// Инициализация FastLED
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, LED_COUNT);
FastLED.setBrightness(STARTING_BRIGHTNESS);
// Инициализация SD-карты
if (SD.begin(SD_CS_PIN)) {
sd_ready = true;
loadNextEffect();
}
}
void loop() {
if (sd_ready && currentEffectFile) {
// Читаем данные из файла
if (currentEffectFile.read(frameBuffer, sizeof(frameBuffer))) {
// Отображаем кадр на матрице с учетом змейковой разводки
for (int i = 0; i < LED_COUNT; i++) {
uint8_t r = frameBuffer[i * 3];
uint8_t g = frameBuffer[i * 3 + 1];
uint8_t b = frameBuffer[i * 3 + 2];
// Преобразование индекса из файла в змейковый
uint8_t x = i % MATRIX_WIDTH;
uint8_t y = i / MATRIX_WIDTH;
leds[xyToIndex(x, y)] = CRGB(r, g, b);
}
FastLED.show();
} else {
// Если достигнут конец файла, возвращаемся в начало для зацикливания эффекта
currentEffectFile.seek(0);
}
// Смена эффекта раз в минуту
if (millis() - lastEffectChange >= EFFECT_CHANGE_INTERVAL) {
loadNextEffect();
}
} else {
// Режим градиента, если SD-карта не доступна или файл не найден
displayGradient();
}
FastLED.delay(FRAME_DELAY);
}
// Функция загрузки следующего файла эффекта
void loadNextEffect() {
if (currentEffectFile) {
currentEffectFile.close();
}
// Формируем имя файла (например, "001.out", "002.out")
char filename[9]; // 8 символов + 1 для завершающего нуля
sprintf(filename, "%03d.out", currentEffectIndex);
currentEffectFile = SD.open(filename);
if (currentEffectFile) {
lastEffectChange = millis();
} else {
// Если файл не найден, переключаемся в режим градиента
currentEffectFile.close();
return;
}
currentEffectIndex++;
if (currentEffectIndex > NUM_EFFECT_FILES) {
currentEffectIndex = 1; // Циклическая смена файлов
}
}
// Функция преобразования координат (x, y) в индекс светодиода для змейковой разводки
uint16_t xyToIndex(uint8_t x, uint8_t y) {
if (y % 2 == 0) {
// Четные ряды (начиная с 0) - слева направо
return y * MATRIX_WIDTH + x;
} else {
// Нечетные ряды - справа налево
return y * MATRIX_WIDTH + (MATRIX_WIDTH - 1 - x);
}
}
void displayGradient() {
static uint8_t hue = 0;
fill_rainbow(leds, LED_COUNT, hue);
FastLED.show();
hue++;
}