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 (есть в наборе)

