RGB контроллер ленты/матрицы
Задача
- Разработать контроллер RGB светодиода/ленты/COB матрицы
- Управление с энкодера
- Индикация – 7 сегментный дисплей
- Режимы работы: выбор цвета и цветовой теплоты + яркость
- Запоминать настройки в EEPROM
- Опционально: управление 4-пин вентилятором по датчику температуры (термистор)
Базовые уроки
- Энкодер
- RGB светодиод
- Термистор
- 7 сегментный дисплей (урока пока что нет)
Подключение
Вариант с тремя транзисторами и RGB лентой или матрицей, а также термистором и выходом на вентилятор:
Второй вариант схемы – без вентилятора, управляется RGB светодиод. Эту схему можно собрать в рамках набора:
Библиотеки
Для дисплея используется непубличная библиотека SevSeg.h, которую нужно разместить рядом со скетчем
Программа
Полный код проекта есть на GitHub
Первым делом обозначим пины
// ИК, вентилятор, термистор #define P_IR 2 #define P_FAN 11 #define P_NTC 6 // энкодер #define P_ENC_A 4 #define P_ENC_B 5 #define P_BTN 6 // светодиод #define P_LED_R 3 #define P_LED_G 9 #define P_LED_B 10 // сегменты #define P_SSA 7 #define P_SSB A0 #define P_SSC A1 #define P_SSD A2 #define P_SSE A3 #define P_SSF A4 #define P_SSG A5 // DIGи #define P_G1 0 #define P_G2 1 #define P_G3 8 #define P_G4 12
Подключаем библиотеки и всё настраиваем
// энкодер #include <EncButton.h> EncButton<EB_TICK, P_ENC_A, P_ENC_B, P_BTN> enc(INPUT); // термистор #include <GyverNTC.h> GyverNTC therm(P_NTC, 10000, 3950); // дисплей #include "SevSeg.h" const uint8_t digs[] = {P_G4, P_G3, P_G2, P_G1}; const uint8_t segs[] = {P_SSA, P_SSB, P_SSC, P_SSD, P_SSE, P_SSF, P_SSG}; SevSeg<4, SS_CATHODE> disp(digs, segs); // светодиод #include <GRGB.h> GRGB led(COMMON_CATHODE, P_LED_R, P_LED_G, P_LED_B); // настройки для eeprom struct Settings { uint8_t mode = 0; // 0 hue, 1 kelvin uint8_t hue = 0; // цвет uint8_t hueB = 0; // яркость при цвете uint8_t temp = 0; // температура uint8_t tempB = 0; // яркость при температуре }; Settings settings; // менеджер eeprom #include <EEManager.h> EEManager memory(settings, 10000); // 10 сек
В блоке setup() запускаем менеджер EEPROM, настраиваем пины и обновляем дисплей и светодиод:
void setup() { pinMode(P_FAN, OUTPUT); // запуск епром с адреса 0, ключ 2 (любой 0-255) // https://github.com/GyverLibs/EEManager memory.begin(0, 2); // принудительно обновляем updateLED(); updateDisp(); }
Функции обновления дисплея и светодиода содержат следующий код:
void updateDisp() { // буквы http://www.uize.com/examples/seven-segment-display.html disp.clear(); if (!settings.mode) { // hue if (dispMode) { disp.setOneByte(3, 0x1f); disp.setInt(settings.hueB); } else { disp.setOneByte(3, 0x17); disp.setInt(settings.hue); } } else { // temp if (dispMode) { disp.setOneByte(3, 0x1f); disp.setInt(settings.tempB); } else { disp.setOneByte(3, 0x0f); disp.setInt(settings.temp); } } }
void updateLED() { // setWheel принимает 0-1530, у нас 8 бит - умножаем на 6 (1530 == 255 * 6) if (!settings.mode) led.setWheel(settings.hue * 6, settings.hueB); else led.setKelvin(settings.temp * 100, settings.tempB); }
Для схемы с вентилятором у нас также есть опрос датчика и регулирование по простому линейному закону
void coolingTick() { // таймер на 1 сек static uint32_t tmr; if (millis() - tmr >= 1000) { tmr = millis(); static float temp = 25; // фильтрованная температура temp += (therm.getTemp() - temp) * 0.2; // фильтр // линейное регулирование int duty = map(int(temp), 30, 45, 10, 255); duty = constrain(duty, 10, 255); analogWrite(P_FAN, duty); } }
Также понадобится глобальная переменная byte dispMode;
, которая задаёт текущий режим отображения и настройки: 0 цвет, 1 яркость
В основном цикле программы loop() опрашиваем менеджер памяти, обновляем дисплей, вызываем функцию охлаждения, а также опрашиваем энкодер. Для оптимизации будем опрашивать действия с энкодером только если его опросная функция возвращает значение больше 0:
void loop() { memory.tick(); // менеджер епром disp.tick(); // динамо дисплея coolingTick(); // регулирование вентилятора if (enc.tick()) { // опрос, если было событие // разбор действий энкодера } }
И соответственно разбираем действия с энкодера, в конце обновляем память и дисплей
if (enc.tick()) { // опрос, если было событие // по клику меняем режим вывода if (enc.click()) dispMode = !dispMode; // по удержанию меняем режим настройки величина/яркость if (enc.held()) { settings.mode = !settings.mode; dispMode = 0; } // поворот - меняем величину if (enc.turn()) { int val = enc.getDir(); // направление поворота if (enc.fast()) val *= 5; // быстрый поворот - в 5 раз быстрее if (dispMode) { if (!settings.mode) settings.hueB += val; else settings.tempB += val; } else { if (!settings.mode) settings.hue += val; else settings.temp += val; } } // обновляем updateLED(); updateDisp(); enc.resetState(); // сбрасываем флаги (очищаем остальные события) memory.update(); // откладываем обновление епром }
Полный код программы можно посмотреть под спойлером:
Возможные доработки
- Использовать 7 сегментный дисплей со встроенным контроллером TM1637 (есть в наборе)