Ночь перед выставкой, я в четвёртый раз переписываю закат на ленте: подобрал руками пять опорных цветов через CRGB(), а между ними лента рисует то грязную зелень, то серую прослойку. Ручное смешивание — рулетка: каждый новый опорный цвет перекашивает соседние переходы, правка одной точки разваливает весь маршрут, и к трём часам ночи ты уже подбираешь не свет, а компромисс. Выход, к которому я пришёл, — отдать маршрут библиотеке: палитра хранит опорные цвета, а промежуточные дорисовывает ColorFromPalette. Устройство палитры изнутри и разбор HeatColors_p уже сделаны в уроке про CRGBPalette16 — не пересказываю его, а решаю соседнюю задачу: готовые наборы, которых хватает на девять случаев из десяти, и критерий выбора.
Три вопроса, которые закрывает урок. Сколько памяти съедает палитра и почему даже на ATmega328 это не аргумент против. Что делает последний аргумент ColorFromPalette и почему без него лента полосит, хотя код скопирован из примера. И главное — по какому признаку я выбираю между PartyColors_p, HeatColors_p, RainbowColors_p и обычным fill_rainbow, чтобы не гадать у макетки в четверг вечером.
Сорок восемь байт и один индекс
CRGBPalette16 — шестнадцать ячеек по три байта, итого 48 байт RAM. Для сравнения: держать отдельный CRGB на каждый из 256 оттенков стоило бы 768 байт — на ATmega328 с двумя килобайтами это самоубийство, и компромисс библиотеки понятен: между ячейками цвет дорисовывает интерполяция. Индекс, который вы передаёте в ColorFromPalette, — байт от 0 до 255, на каждую ячейку приходится по 16 единиц индекса. Правила счёта те же, что у оттенка в уроке про колесо цвета: тип byte, переполнение замыкает круг само, никаких проверок не нужно.
Вызов выглядит так: ColorFromPalette(pal, idx, brightness, blendMode). Яркость — отдельный аргумент, палитру она не трогает: глушить ленту удобно одним числом, не пересчитывая цвета. Последний аргумент — режим смешивания. LINEARBLEND тянет цвет от ячейки к ячейке, NOBLEND отдаёт ближайшую ячейку как есть. В большинстве перегрузок LINEARBLEND стоит по умолчанию, но перегрузок несколько, и однажды вы скомпилируете ту, где смешивания нет: лента рассыпается на 16 чётких полос, хотя код не менялся ни на символ. Передавайте режим явно — одна строка вместо вечера гаданий у макетки.
Бюджет на практике: три палитры в одном скетче — 144 байта из двух килобайт, даже мега это прощает. Я держу в проекте дневную, вечернюю и ночную палитры одновременно и переключаю их по времени суток, потому что хранить маршрут дешевле, чем пересобирать его на ходу.
#include <FastLED.h> #define LED_PIN 6#define NUM_LEDS 16 CRGB leds[NUM_LEDS];byte startIndex = 0; void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.setBrightness(120);} void loop() { for (int i = 0; i < NUM_LEDS; i++) { // 16 пикселей по 16 единиц индекса: вся палитра от края до края leds[i] = ColorFromPalette(PartyColors_p, startIndex + i * 16, 255, LINEARBLEND); } FastLED.show(); EVERY_N_MILLISECONDS(25) { startIndex++; } // окно палитры едет по ленте}Шаг индекса — главная ручка. При 16 пикселях и множителе 16 лента разворачивает всю палитру от края до края: синева на первом пикселе, жёлтый на последнем, пурпур и красный между ними. Поставьте 6 — увидите шестую часть маршрута, растянутую на всю ленту; та же арифметика дельты на пиксель, что в уроке про радугу. Счётчик startIndex едет во времени: EVERY_N_MILLISECONDS(25) двигает окно на единицу сорок раз в секунду — перелив живой, но без мельтешения. Это тот же millis-каркас, который мы собирали руками в уроке про millis вместо delay, только свёрнутый макросом; поменяйте 25 на 60 — и закат станет неторопливым.
Библиотечные наборы: чей маршрут берём
HeatColors_p — маршрут настоящего жара: чёрный, тёмно-красный, оранжевый, жёлтый, белая кромка. Именно она крутится внутри классического Fire2012, где индекс палитры играет роль температуры пикселя: урок про огонь собирает эффект целиком, здесь достаточно понимать, что палитра и есть его краска. PartyColors_p идёт по линии синий → пурпур → розовый → красный → оранжевый → жёлтый — зелёного в наборе нет вовсе, и это не недосмотр, а сюжет: праздничная гамма читается даже на минимальной яркости, а зелёный канал остаётся свободным под статусную индикацию, которая иначе путается под ногами у эффектных цветов.
Дальше по списку: RainbowColors_p — полное колесо без сюжета, и для него честнее fill_rainbow, палитра ничего не добавит. RainbowStripeColors_p — та же радуга с чёрными полосами: штрихи, калейдоскопы, бегущие цифры. Мой критерий за пару лет гирлянд простой: если цвет на ленте рассказывает историю — жар, закат, глубина воды, шкала нагрева, — нужна палитра с маршрутом; свои маршруты из опорных точек собираются в уроке про градиент из опорных цветов. Если цвет просто переливается по кругу без начала и конца — берите fill_rainbow и не создавайте себе структуру, которую потом придётся чем-то наполнять.
Разбор модели. Оттенки party-маршрута собраны из пяти отрезков: 160, 196, 224, 0 через верх колеса, 30, 45 — те же ориентиры, что лежат в PartyColors_p. Переход к красному специально ведён через верх колеса, иначе от розового к красному лента проехала бы зеленоватую середину. Зажатая кнопка прилипляет индекс к ближайшей ячейке — вот он, NOBLEND: пять чётких полос вместо плавной ленты. Честная оговорка: настоящий ColorFromPalette смешивает в RGB, модель — в оттенке, и разница прячется только на очень широких переходах между далёкими цветами; на party-гамме глаз её не ловит.
Отсюда же ответ на вопрос из первого абзаца — где палитра живее ручной смеси. Когда маршрут длинный и опорных точек много, ручной код превращается в таблицу из десятков CRGB с ручной разливкой по диапазонам, а палитра делает то же одним вызовом и без арифметики диапазонов. Когда точек две — смешивайте вручную, палитра там лишний слой. Индекс палитры к тому же удобно носить как величину: температура с датчика, громкость с микрофона — число само становится позицией на маршруте цвета, и шкала читается без единого if.
| Набор | Маршрут цвета | Где он у меня работает |
|---|---|---|
| PartyColors_p | синий, пурпур, розовый, красный, оранжевый, жёлтый — без зелени | дискотека, индикация режимов, всё, где зелёный занят под статус |
| HeatColors_p | чёрный, тёмно-красный, оранжевый, жёлтый, белая кромка | огонь Fire2012, шкала нагрева, индикатор тока по температуре |
| RainbowColors_p | полное колесо оттенков | радуга без сюжета — но тут честнее fill_rainbow |
| RainbowStripeColors_p | радуга с чёрными полосами | штрихи и калейдоскопы, бегущие цифры на витрине |
- 1. Сколько RAM занимает CRGBPalette16?
- 2. Что вернёт ColorFromPalette с LINEARBLEND для индекса 200?
- 3. Скопированный пример рисует 16 чётких полос. Что проверить первым?
- 4. Что входит в маршрут PartyColors_p?
- 5. Куда встроена HeatColors_p в классическом примере?
- CRGBPalette16 — 16 ячеек по 3 байта, 48 байт RAM; три палитры в скетче — 144 байта, мега прощает
- Индекс 0-255 делится на ячейки по 16 единиц; ColorFromPalette с LINEARBLEND смешивает соседей, NOBLEND даёт ступени
- PartyColors_p: синий, пурпур, розовый, красный, оранжевый, жёлтый — зелёного нет, гамма праздничная
- HeatColors_p — краска огня: чёрный через тёмно-красный к белой кромке, движок Fire2012
- Маршрут цвета важен — палитра; цвет по кругу без сюжета — fill_rainbow, палитра ничего не добавит
Частые вопросы по теме урока
- Сколько RAM занимает CRGBPalette16?
- Палитра хранит только 16 опорных ячеек по 3 байта — итого 48. Цвета между ячейками дорисовывает интерполяция, поэтому полный набор из 256 оттенков в RAM не нужен. 48 байт: 16 ячеек по три канала
- Что вернёт ColorFromPalette с LINEARBLEND для индекса 200?
- Индекс 0-255 делится на 16 ячеек по 16 единиц. Индекс 200 ложится между ячейками, и LINEARBLEND возвращает их смесь пропорционально позиции внутри диапазона. Смесь двух соседних ячеек, между которыми попал индекс
- Скопированный пример рисует 16 чётких полос. Что проверить первым?
- Полосы — почерк NOBLEND: ближайшая ячейка без смешивания. Режим передают явно, потому что перегрузок несколько, и не в каждой смешивание стоит по умолчанию. Режим смешивания: нужен явный LINEARBLEND в ColorFromPalette
- Что входит в маршрут PartyColors_p?
- Party-гамма построена на линии от синевы через пурпур и розовый к красному и жёлтому. Зелёного в наборе нет — он остаётся свободным под статусную индикацию. Синий, пурпур, розовый, красный, оранжевый, жёлтый — зелёного нет
- Куда встроена HeatColors_p в классическом примере?
- Fire2012 считает «тепло» каждого пикселя и красит его через HeatColors_p: от чёрного через тёмно-красный и оранжевый к жёлто-белой кромке. В движок эффекта огня Fire2012: индекс палитры играет роль температуры