Ну, я не в курсе, что для вас лично является mauvais ton. Поэтому не реагирую.
А вот на ПРОСЬБУ вполне:
Скетч LEDS_SD_Master.ino
/*
Программа, обеспечивающая управление лентой
на светодиодах WS2812B. Считывание данных выполняется с SD карточки.
Количество светодиодов может быть от 2-х до 300.
Стандарт: 5 метров ленты с плотностью светодиодов 60 штук на метр.
Итого общее количество 300 штук на ленту.
Скетч загружается в плату, обозначенную на схеме как Arduino - Master.
Не забудьте перед загрузкой разомкнуть линии RX/TX, соединяющие плату
с Arduino - Slave.
Скетч работает в комплексе с компьютерной программой LedsImg.exe,
которая поставляется в дистрибутиве. Она, собственно, и создаёт
световые программы, которые затем записываются на SD карточку.
Скетч разработан Юрием Степановым в 2022 году.
e-mail: `ruskuzmich1@gmail.com`
Viber: +380994743972
*/
// Подключаем библиотеки:
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <EasyTransfer.h>
#include <LiquidCrystal_I2C.h>
// Определение пинов платы:
#define pinKey A1 // Пин, к которому подключена клавиатура
#define pinCS 10 // Пин, к которому подключается CS кардритера
#define pinBright A0 // Сюда подключаем движок потенциометра
#define pinPause A2 // Пин, управляющий индикацией режима ПАУЗА
#define pinHide A3 // Пин, управляющий индикацией режима ГАШЕНИЕ
// Константы для работы скетча:
#define _Start 1
#define _Prev 2
#define _Next 3
#define _Pause 4
#define _Stop 5
#define _Hide 6
// Описание специальных символов для LCD1602:
byte mPlay[8] = {B01000, B01100, B01110, B01111, B01110, B01100, B01000, B00000}; // 1 - символ "Воспроизведение"
byte mPause[8] = {B00000, B11011, B11011, B11011, B11011, B11011, B11011, B00000}; // 2 - символ "Пауза"
byte mHide[8] = {B00000, B11111, B11111, B11111, B11111, B11111, B11111, B00000}; // 3 - символ "Гашение"
// Переменная, определяющая режим обработки данных:
bool readMode = false;
// Флаг блокировки работы системы
// (если установлен, то Arduino следует перегрузить):
bool _reboot = false;
// Файлы для работы с SD карточкой:
File Prog;
File root;
File Menu;
// Различные переменные для работы скетча:
uint32_t filePos = 0; // позиция в файле данных
uint32_t fileSize = 0; // размер файла данных
String fileName = ""; // имя файла данных
long filesCount = 0; // количество файлов на SD карточке
bool hasSD = false; // флаг готовности SD карточки
bool doDraw = false; // флаг режима вывода данных на ленту (вернее, на Arduino - Slave)
byte bright = 255; // максимальная яркость светодиодов
float kBr = 1.0; // коэффициент изменения яркости (определяется положением движка потециометра R1)
word workFile = 0; // позиция в файле меню
bool isPause = false; // флаг режима ПАУЗА
bool isHide = false; // флаг режима ГАШЕНИЕ
// Буфер для сглаживания показаний потенциометра яркости:
word bufBright[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Создаём объект EasyTransfer для передачи данных:
EasyTransfer ET;
// Определяем структуру данных:
struct SEND_DATA {
uint8_t Data[3];
};
// Переменная в формате SEND_DATA для заполнения данными:
SEND_DATA ledData;
// Создаём объект для вывода данных на дисплей по протоколу I2C:
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ============================ //
// Начальные установки программы.
// ============================ //
void setup() {
// Назначаем пины для ввода/вывода:
pinMode(pinKey, INPUT_PULLUP);
pinMode(pinCS, OUTPUT);
pinMode(pinPause, OUTPUT);
pinMode(pinHide, OUTPUT);
// Гасим светодиоды индикации:
digitalWrite(pinPause, HIGH);
digitalWrite(pinHide, HIGH);
// Инициализируем библиотеку Wire:
Wire.begin();
// Инициализируем библиотеку LiquidCrystal_I2C:
lcd.init();
// Включаем подсветку экрана:
lcd.backlight();
// Очищаем экран:
lcd.clear();
// Записываем в память LCD1602 специальные символы:
lcd.createChar(1, mPlay);
lcd.createChar(2, mPause);
lcd.createChar(3, mHide);
// Инициализируем SD карточку:
if (!SD.begin(pinCS)) {
// если не удалось, выводим сообщение об ошибке:
lcd.setCursor(0,0);
lcd.print("SD not found!");
} else { // если инициализация успешна, то:
// устанавливаем флаг готовности SD карточки:
hasSD = true;
// выводим сообщение об удачной инициализации:
lcd.setCursor(0,0);
lcd.print("SD done.");
}
// Инициализируем Serial:
Serial.begin(115200);
// Инициализируем библиотеку EasyTransfer:
ET.begin(details(ledData), &Serial);
// Создаём меню программы:
createMenu();
}
// ========================= //
// Основной цикл программы.
// ========================= //
void loop() {
if ((!hasSD) && (!_reboot)) {
// Если есть проблемы с SD карточкой, то выводим
// соответствующее сообщение и блокируем работу:
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SD card error.");
lcd.setCursor(0,1);
lcd.print("Reboot system!");
_reboot = true;
}
if (!_reboot) {
if (doDraw) {
// Если установлен флаг режима вывода данных,
// то считываем данные из файла на SD карточке:
readSD();
} else {
// Если флаг режима вывода данных сброшен,
// то работаем в режиме меню:
doMenu(workFile);
}
}
}
// ======================================= //
// Функция чтения данных из файла
// с последующей передачей на Adruino-Slave:
// ======================================= //
void readSD() {
byte val;
// "Бесконечный" цикл:
while (true) {
// считываем данные о яркости:
setBright();
// Если не включен режим ПАУЗА, то считываем данные
// с SD карточки и передаём их на Arduuino-Slave:
if (!isPause) {
// Если достигнут конец файла данных, то возвращаемся
// к его началу:
if (filePos>=fileSize) {
filePos = 0;
Prog.seek(0);
}
// Считываем из файла бдок из трёх байт и
// записываем в структуру ledData.
// Если включен режим ПАУЗА, что чтение из
// файла не выполняем и данные не передаём:
for (byte j=0; j<3; j++) {
val = Prog.read();
ledData.Data[j] = val;
}
// Если первый байт в тройке не является командой:
// 253 - команда переустановки данных о фактическом количестве светодиодов
// 255 - команда начала блока данных фреймов (в формате HSV).
// (см. информацию о формате файлов программы).
if ((ledData.Data[0] != 253) && (ledData.Data[0] != 255)) {
if (isHide) { // если включен режим ГАШЕНИЕ, то
// переустанавливаем значение байта Value в 0:
ledData.Data[2] = 0;
} else { // в обычном режиме изменяем значение
// байта Value в соответствии с положением регулятора яркости:
ledData.Data[2] = round(ledData.Data[2]*kBr);
}
}
// Отправляем данные на Arduuino-Slave:
ET.sendData();
// увеличиваем значение указателя положения в файле:
filePos += 3;
}
// Проверяем состояние клавиатуры:
byte key = checkButtons();
// Реакция на нажатие клавиш:
switch (key) {
// Если нажата клавиша _Start или _Stop,
// то завершаем чтение данных и выходим в меню:
case _Start:
case _Stop:
doDraw = false; // сбрасываем флаг режима вывода данных на ленту
isPause = false; // сбрасываем флаг режима ПАУЗА
isHide = false; // сбрасываем флаг режима ГАШЕНИЕ
// Формируем команду "Погасить светодиоды":
ledData.Data[0] = 254;
ledData.Data[1] = 0;
ledData.Data[2] = 0;
// Отправляем на Arduuino-Slave:
ET.sendData();
// Закрываем файл данных:
Prog.close();
// Очищаем экран:
lcd.clear();
// Позиционируем указатель в файле меню:
Menu.seek(workFile*14);
// Считываем из файла меню
// название файла данных:
loadFileName(workFile);
// Выходим из функции readSD:
return;
break;
case _Pause:
// Эта клавиша срабатывает только
// если ВЫКЛЮЧЕН режим ГАШЕНИЕ:
if (!isHide) {
// переключаем режим на противоположный:
isPause = !isPause;
// включаем/выключаем индикатор:
digitalWrite(pinPause, !isPause);
// отображаем режим на экране LCD:
lcd.setCursor(15, 0);
if (isPause) {
lcd.print("\2"); // символ "Пауза"
} else {
lcd.print('\1'); // символ "Воспроизведение"
}
}
break;
case _Hide:
// Эта клавиша срабатывает только
// если ВЫКЛЮЧЕН режим ПАУЗА:
if (!isPause) {
// переключаем режим на противоположный:
isHide = !isHide;
// включаем/выключаем индикатор:
digitalWrite(pinHide, !isHide);
// отображаем режим на экране LCD:
lcd.setCursor(15, 1);
if (isHide) {
lcd.print("\3"); // символ "Гашение"
} else {
lcd.print(' '); // удаляем символ "Гашение" пробелом
}
}
break;
}
}
}
// ========================= //
// Функция опроса клавиатуры.
// ========================= //
// Клавиатуру следует предварительно откалибровать.
// Для этого предварительно загрузите тестовый скетч
// LEDS_KEY_TEST, откройте монитор порта и выпишите
// значения, которые отобразятся при нажатии клавиш.
// У меня это были:
// 1010 - нет нажатий
// _Prev = 15, _Start = 210, _Next = 382, _Pause = 542, _Stop = 702, _Hide = 863
// Эти величины будем использовать для расчёта тестовых значений, как это показано ниже:
byte checkButtons() {
byte n = 0;
// считываем данные с пина клавиатуры:
word v = analogRead(pinKey);
// если это значение маньше тестового,
// то есть нажатие:
if (v<940) { //(1010 + 863)/2
// защита от дребезга:
// пауза и повторное считывание:
delay(50);
v = analogRead(pinKey);
// если значение по прежнему маньше тестового,
// то это действительно нажатие.
// Сравниваем это значение с рассчитанными тестовыми
// и делаем вывод, какая клавиша нажата.
// Не нажимайте одновременно 2 или более клавиш,
// результат непредсказуем.
if (v<940) {
if (v<112) { //(210 + 15)/2
n = _Prev;
} else {
if (v<296) { //(382 + 210)/2
n = _Start;
} else {
if (v<462) { //(542 + 382)/2
n = _Next;
} else {
if (v<622) { //(702 + 542)/2
n = _Pause;
} else {
if (v<783) { //(863 + 702)/2
n = _Stop;
} else {
if (v<940) { //(1010 + 863)/2
n = _Hide;
}
}
}
}
}
}
}
}
// Если было нажатие, то ждём отпускания клавиши:
while (v<940) {
v = analogRead(pinKey);
}
// Возвращаем код нажатой клавиши:
return n;
}
// ======================================== //
// Функция чтения имени файла данных из файла
// меню. Параметр fp - позиция в файле меню.
// ======================================== //
String loadFileName(word fp) {
String fn;
String c;
byte p;
fn = "";
// Считываем в строку fn имя файла:
for (byte i=0; i<12; i++) {
char c = Menu.read();
fn += c;
}
// Обрезаем концевые пробелы:
fn.trim();
// Переводим в верхний регистр:
fn.toUpperCase();
// Увеличиваем номер позиции в файле меню.
// Это для того, чтобы номера файлов на дисплее
// начинались не с нуля, а с единицы:
fp++;
// Формируем сроку для вывода на дисплей:
// присваиваем строковой переменной с значение
// номера файла (нумерация файлов определяется
// временем их создания):
c = fp;
// определяем длину имени файла без раширения:
byte k = fn.indexOf(".");
// окончательно формируем строку.
// Должно получиться нечто такое:
// "3. PROJ_5"
// где "3. " - номер файла, а "PROJ_5" - его название
c = c + ". " + fn.substring(0,k);
// Центрируем строку и выводим на экран дисплея:
lcd.setCursor(0,1);
lcd.print(" "); // очистка всей строкм
p = (16 - c.length())/2; // центровка
lcd.setCursor(p,1);
lcd.print(c);
return fn;
}
// =============================== //
// Функция меню выбора файла данных.
// =============================== //
void doMenu(word fpos) {
String fn;
String c;
byte k, b1, b2;
// Открываем файл меню:
Menu = SD.open("Menu.txt", FILE_READ);
// Если файл открылся, то:
if (Menu) {
// Выводим на дисплей приглашение к выбору файла данных:
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Select File:");
// Позиционизуемся в файле меню на последнем выбранном
// файле данных. Вначале это самый первый файл.
Menu.seek(fpos*14);
// Счтываем имя файла и выводим на дисплей:
fn = loadFileName(fpos);
// Циклическая работа меню:
while (true) {
// Опрашиваем клавиатуру:
byte key = checkButtons();
// Реакции на нажатые клавиши:
switch (key) {
case _Prev:
// Клавиша выбора предыдущего файла в списке.
// Если перед этим был выбран первый файл,
// то указатель переместится на последний:
if (fpos>0) {
fpos--;
} else {
fpos = filesCount-1;
}
// Позиционируемся в файле меню:
Menu.seek(fpos*14);
// Считываем имя файла данных и выводим на дисплей:
fn = loadFileName(fpos);
break;
case _Next:
// Клавиша выбора следующего файла в списке.
// Если перед этим был выбран последний файл,
// то указатель переместится на первый:
if (fpos<filesCount-1) {
fpos++;
} else {
fpos = 0;
}
// Позиционируемся в файле меню:
Menu.seek(fpos*14);
// Считываем имя файла данных и выводим на дисплей:
fn = loadFileName(fpos);
break;
case _Start:
// Клавиша запуска выбранного файла данных на воспроизведение.
// Запоминаем номер выбранного файла:
workFile = fpos;
// Если файл данных был открыт, закрываем его:
if (Prog) Prog.close();
// Открываем новый файл (на чтение):
Prog = SD.open(fn, FILE_READ);
// Очищаем дисплей:
lcd.clear();
lcd.setCursor(0,0);
// Если файл успешно открыт, то:
if (Prog) {
// Выводим информацию о файле (имя без расширения):
lcd.print("Out: ");
k = fn.indexOf(".");
c = fn.substring(0,k);
lcd.print(c);
// Выводим символ "Воспроизведение":
lcd.setCursor(15, 0);
lcd.print('\1');
// Считываем размер файла данных:
fileSize = Prog.size();
// На всякий случай позиционируемся в начало:
filePos = 0;
Prog.seek(0);
// Устанавливаем флаг режима вывода данных:
doDraw = true;
// Возврат из функции doMenu в функцию loop^
return;
} else {
// Если файл открыть не удалось, то выводим сообщение об ошибке:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Error open file!");
lcd.setCursor(0, 1);
lcd.print("Press any key.");
// Ждём нажатия на любую клавишу:
key = checkButtons();
while (key == 0) {
key = checkButtons();
}
// Сбрасываем флаг готовности SD:
hasSD = false;
// Выходим из программы:
return;
}
break;
}
}
}
}
// ========================= //
// Функция сглаживания данных
// регулятора яркости.
// ========================= //
// Функция позволяет уменьшить шум,
// неизбежный в работе потенциометра.
word getBufBright(word val) {
word sum = 0;
// перемещаем данные в буфере по принципу стека:
for (byte i=0; i<9; i++) {
bufBright[i] = bufBright[i+1];
// суммируем значения:
sum += bufBright[i];
}
// присваиваем переданное функции значение
// последнему элементу массива:
bufBright[9] = val;
// суммируем значения:
sum += val;
// возвращаем усреднённое значение данных в массиве:
return (sum/10);
}
// ========================= //
// Функция контроля яркости:
// ========================= //
void setBright() {
// Считываем значение потенциометра:
word v = analogRead(pinBright);
// Записываем в буфер и получаем сглаженное значение:
v = getBufBright(v);
// вычисляем долю этого значения от полного
// (= 1023 при подсоединении движка потенциометра к +5v):
float e = v/1023.0;
// Переводим это значение в проценты от полной яркости:
byte n = round(e*100);
// Если значение отличается от прежнего, то:
if (n!=bright) {
// сохраняем новое значение
bright = n;
// вычисляем коэффициент изменения яркости:
kBr = float(bright)/100.0;
// выводим значение яркости (в %) на дисплей
lcd.setCursor(0,1);
lcd.print("Bright=");
lcd.print(bright);
lcd.print("% ");
}
}
// ======================================== //
// Функция создания меню выбора файла данных.
// ======================================== //
void createMenu() {
String nm;
// Устанавливаем счётчик файлов на SD карточке в 0.
// Замечание 1: все файлы должны располагаться в
// корне файловой системы, папки не просматриваются.
// Замечание 2: имена файлов должны быть в формате
// 8.3, т.е. длина имени не должна превышать 8 символов.
// Имена должны быть только на латинице.
// Расширение файлов предопределено, это ".dpf".
filesCount = 0;
// Открываем корневой каталог:
root = SD.open("/");
// Если успешно, то:
if (root) {
// Удаляем прежний файл меню, если таковой имеется:
if (SD.exists("Menu.txt")) {
SD.remove("Menu.txt");
}
// Создаём новый файл меню и открываем для записи:
Menu = SD.open("Menu.txt", FILE_WRITE);
// Открываем файл для просмотра каталога:
File Next = root.openNextFile();
// Если каталог не пуст, то:
while (Next) {
// извлекаем имя файла:
nm = Next.name();
// преобразуем имя в нижний регистр символов:
nm.toLowerCase();
// если расширение файла = ".dpf",
// то это то, что нам нужно:
if (nm.indexOf(".dpf")>0) {
// Если длина имени вместе с расширением
// меньше 12 символов, то дополняем его пробелами.
// Такая операция нужна для того, чтобы стандартизировать
// длину строк, записываемых в файл меню. Она будет
// равна 14 символам - длина имени(12 символов) + cr/lf.
while (nm.length()<12) {
nm = ' ' + nm;
}
// Увеличиваем счётчик обнаруженных файлов:
filesCount++;
// Записываем имя файла данных в файл меню:
Menu.println(nm);
}
// Закрываем файл просмотра каталога:
Next.close();
// Открываем следующий файл в каталоге:
Next = root.openNextFile();
// Повторяем цикл, пока не просмотрим все файлы.
}
// Закрываем файл меню:
Menu.close();
} else {
// Если файл _root не открылся, значит ошибка файловой системы.
// Сбрасываем флаг готовности SD карточки:
hasSD = false;
}
// Если нужных файлов на карточке не обнаружено,
// то сбрасываем флаг готовности:
if (filesCount==0) hasSD = false;
// Закрываем корневой каталог (если есть):
if (root) root.close();
// Если чтение было успешным:
if (hasSD) {
// Выводим сообщение о количестве
// обнаруженных файлов:
lcd.setCursor(0,1);
lcd.print(filesCount);
lcd.print(" Files found");
// Торомозим на 2 секунды, чтобы успеть прочитать
// это сообщение:
delay(2000);
// Считываем имя первого в списке файла данных:
loadFileName(0);
} else {
// Если просмотр каталога был неуспешным,
// то выводим соответствующее сообщение:
lcd.setCursor(0,0);
lcd.print("Files not found!");
}
}
Скетч LEDS_SD_Slave:
/*
Программа, обеспечивающая управление лентой
на светодиодах WS2812B. Считывание данных выполняется с SD карточки.
Количество светодиодов может быть от 2-х до 300.
Стандарт: 5 метров ленты с плотностью светодиодов 60 штук на метр.
Итого общее количество 300 штук на ленту.
Скетч загружается в плату, обозначенную на схеме как Arduino - Slave.
Не забудьте перед загрузкой разомкнуть линии RX/TX, соединяющие плату
с Arduino - Master.
Скетч работает в комплексе с компьютерной программой LedsImg.exe,
которая поставляется в дистрибутиве. Она, собственно, и создаёт
световые программы, которые затем записываются на SD карточку.
Скетч разработан Юрием Степановым в 2022 году.
e-mail: ruskuzmich1@gmail.com
Viber: +380994743972
*/
// Подключаем библиотеки:
#include <EasyTransfer.h>
#include <FastLED.h>
// Создаём объект EasyTransfer для приёма данных:
EasyTransfer ET;
// Определяем структуру данных:
struct RECEIVE_DATA {
uint8_t Data[3];
};
// Переменная в формате RECEIVE_DATA для заполнения данными:
RECEIVE_DATA ledData;
/* Пин, к которому подключена лента.
* Может быть любым цифровым (кроме 0 и 1) или аналоговым (кроме A6 и A7).
* Не забудьте, что между пином Arduino и входом данных ленты
* нужно поставить резистор 100Ом ... 1К! */
#define pinLeds 2
// Режимы работы скетча:
#define _Wait 0 // ожидание команды
#define _Read 1 // приём данных через Serial
int numLeds = 300; // максимальное количество светодиодов
CRGB Leds[300]; // структура, в которую записывается фрейм "картинки" на ленте
byte readMode = _Wait; // текущий режим работы
int _count; // счётчик фреймов
// Фрейм - это массив данных о цвете светодиодов в ленте.
// Размер фрейма равен numLeds * 3, где каждая тройка байт - это
// значение цвета в формате HSV (Hue, Saturation, Value).
// ============================ //
// Начальные установки программы.
// ============================ //
void setup() {
// Подключаем ленту (для других типов лент смотрите
// документацию к библиотеке FastLed):
FastLED.addLeds<WS2812B, pinLeds, GRB>(Leds, numLeds);
/* Устанавливаем максимальную яркость светодиодов.
* Зависит от конкретной ленты и условиях её эксплуатации.
* Если слишком ярко, можно уменьшить. Попутно уменьшится
* и максимальный ток чере ленту. */
FastLED.setBrightness(255);
/* Максимальный ток (при всех горящих белым цветом светодиодах) рассчитывается так:
* На 1м ленты (60 светодиодов) мощность = 14.5Вт
* На один светодиод это 14.5Вт/(5V*60) = 0.048(3)A = 48.3mA
* Ток на выбранное число светодиодов = 43.8 * NumLeds = 24 * 48.3 = 1159.2mA
* Для 300 светодиодов (полная лента) ток будет уже 300 * 48.3 = 14500mA = 14.5A
* Устанавливаем ограничение тока с небольшим запасом: */
set_max_power_in_volts_and_milliamps(5, 48*numLeds);
// Гасим светодиоды:
SetAll(0, 0, 0);
FastLED.show();
// Инициализируем Serial:
Serial.begin(115200);
// Инициализируем библиотеку EasyTransfer:
ET.begin(details(ledData), &Serial);
// Вызываем фунцию циклического опроса порта Serial:
read_Data();
}
// ========================= //
// Основной цикл программы.
// ========================= //
void loop() {
// Здесь ничего не нужно делать,
// всё происходит в функции read_Data.
}
// =========================== //
// Установка одного цвета сразу
// для всей ленты:
// =========================== //
void SetAll(byte red, byte green, byte blue) {
for (int i=0; i<numLeds; i++) {
Leds[i].r = red;
Leds[i].g = green;
Leds[i].b = blue;
}
}
// =========================== //
// Функция обработки данных,
// поступающих из порта Serial.
// =========================== //
void read_Data() {
// "Вечный" цикл:
while (true) {
// Если в порт поступили данные, то:
if (ET.receiveData()) {
// Если первый байт = 253, то это команда
// переустановки количества светодиодов в ленте:
if (ledData.Data[0] == 253) {
// Гасим все светодиоды:
SetAll(0, 0, 0);
FastLED.show();
// Устанавливаем новое число светодиодов:
numLeds = min(ledData.Data[2]*256 + ledData.Data[1], 300);
// Очищаем данные:
FastLED.clearData();
// Подключаем ленту:
FastLED.addLeds<WS2812B, pinLeds, GRB>(Leds, numLeds);
// Устанавливаем максимальную яркость светодиодов:
set_max_power_in_volts_and_milliamps(5, 48*numLeds);
// Устанавливаем режим ожидания следующей команды:
readMode = _Wait;
}
// Если первый байт = 254, то это команда
// гашения всех светодиодов:
if (ledData.Data[0] == 254) {
SetAll(0, 0, 0);
FastLED.show();
readMode = _Wait;
}
// Если первый байт отличается от 253 или 254, то:
switch (readMode) {
case _Wait: // Если режим ожидания следующей команды:
// Если первый байт = 255, то это команда
// начала приёма данных фрейма:
if (ledData.Data[0] == 255) {
// Устанавливаем режим приёма данных:
readMode = _Read;
// Обнуляем счётчик пикселей:
_count = 0;
}
break;
case _Read: // Если режим приёма данных:
if (_count < numLeds) { // если счётчик пикселей меньше, чем их количество, то
// преобразуем триаду данных о цвете пикселя
// из формата HSV в RGB и записываем в структуру Leds:
Leds[_count] = CHSV(ledData.Data[0], ledData.Data[1], ledData.Data[2]);
// увеличиваем счётчик пикселей:
_count++;
} else { // если счётчик пикселей равен их количеству,
// т.е. все данные фрейма приняты, то отправляем их на ленту:
FastLED.show();
// обнуляем счётчик пикселей:
_count = 0;
// и устанавливаем режим ожидания команды:
readMode = _Wait;
}
break;
}
}
}
}