Ночник-«закат» для полки я красил таблицей: шестнадцать строк, в каждой три числа, от тёмно-синего (10, 0, 40) у основания к оранжевому (255, 120, 0) наверху. Строки я честно подобрал глазами, перенёс в массив — а через месяц захотел сменить нижний цвет на фиолетовый, то есть пересчитать всю таблицу заново. Сосед по кружку глянул в скетч и показал одну строку: fill_gradient_rgb(leds, 16, CRGB(10, 0, 40), CRGB(255, 120, 0)). Библиотека считает переход сама, для любого числа пикселей, для любых двух цветов — и даже для трёх или четырёх опорных точек. Ручные таблицы с шестнадцатью строками чисел с тех пор у меня не заводятся.
План урока: познакомимся с fill_gradient_rgb — линейным градиентом FastLED для реальной платы (в симуляторе этой функции нет, и я покажу честную замену), разберём, почему RGB-переход между далёкими цветами темнеет в середине, и соберём HSV-градиент вручную — циклом с CHSV, который движок курса рисует живьём. Главный навык урока — выбор модели под задачу: закаты, лёд и пламя красят в RGB, радуги и переливы — в HSV.
fill_gradient_rgb: два цвета и одна строка
Сигнатура, которой я пользуюсь девять раз из десяти: fill_gradient_rgb(leds, NUM_LEDS, цвет1, цвет2). Функция идёт по буферу и линейно интерполирует каждый из трёх каналов от первого цвета ко второму: пиксель 0 получает цвет1, последний — цвет2, между ними равномерные ступени, при 16 пикселях шаг канала — шестнадцатая часть разницы. Есть и расширенные варианты: три опорных цвета — fill_gradient_rgb(leds, 16, c1, c2, c3) — дорога с одним перекрёстком, четыре — с двумя. Три точки — мой любимый формат для заката: синий внизу, оранжевый в середине, глубокий красный сверху — переход получается богаче двухцветного без единого нового понятия. Про саму структуру цвета и остальные заливки мы подробно говорили в уроке про CRGB и fill_solid.
Оговорка, из-за которой в уроке два скетча: fill_gradient_rgb в симуляторе курса не поддержан — движок умеет fill_solid и fill_rainbow, а градиент между произвольными цветами мы соберём вручную циклом, чуть ниже, и он будет крутиться прямо на странице. Скетч ниже — целиком рабочий для реальной платы: вставили, загрузили, полка светится закатом. Две практические детали. Первая: стоп-цвета попадают в крайние пиксели включительно, и если переход нужен не на всю ленту, а на её часть — сузьте число в вызове, а остаток докрасьте fill_solid: две строки вместо таблицы цветов. Вторая — привычка из первого урока раздела: каналы подставляем только через CRGB или CHSV, сырые байты мимо конвертера упираются в GRB-порядок ленты, о котором был отдельный разговор в уроке про подключение WS2812.
#include <FastLED.h> #define LED_PIN 6#define NUM_LEDS 16 CRGB leds[NUM_LEDS]; void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.setBrightness(100); // пиксель 0 — глубокий синий, верхний — тёплый оранжевый fill_gradient_rgb(leds, NUM_LEDS, CRGB(10, 0, 40), CRGB(255, 120, 0)); // вариант с тремя опорными цветами: синий — оранжевый — красный // fill_gradient_rgb(leds, NUM_LEDS, CRGB(10, 0, 40), CRGB(255, 120, 0), CRGB(200, 0, 0)); FastLED.show();} void loop() { // градиент статичен; анимировать можно стартовые цвета по таймеру}У RGB-градиента есть характер, который полезно знать заранее: он честно усредняет каналы, и когда цвета далеки друг от друга, середина получается в полсилы. Разберу на числах: переход от красного (255, 0, 0) к синему (0, 0, 255) в середине ленты даёт (128, 0, 128) — красный канал уже упал вдвое, синий ещё не набрал, и глаз видит грязный тёмный фиолетовый, а не красивый светлый мостик. Оба конца насыщенные и яркие, середина — полупустая. Это не брак библиотеки, это свойство линейной интерполяции трёх независимых каналов, и оно же делает RGB-градиент идеальным там, где полутон и нужен: закат через сумеречный красно-фиолетовый, угасающий вечер, темнеющий лёд. Мой ночник из начала урока — живой пример: синий (10, 0, 40) и оранжевый (255, 120, 0) встречаются посередине примерно в (132, 60, 20) — тёплом сумеречном цвете, который и делает «закат» закатом, а не переключателем двух красок.
Отсюда рабочее правило выбора. RGB-градиент — для близких оттенков и «атмосферных» переходов: закат (синий — оранжевый — красный), лёд (тёмно-синий — белый), пламя (бордовый — оранжевый). HSV-градиент — для противоположных по кругу цветов и для всего, что должно переливаться: он идёт по цветовому кругу, где каждая точка — насыщенный чистый цвет, без тёмных ям. Красный к синему в HSV добирается по кругу через жёлтый и зелёный или через фиолетовый — и обе дороги сплошь чистые. Сводка двух моделей — в таблице ниже, а о том, как устроены оттенки и насыщенность, подробнее в уроке про HSV-цвета и палитры.
| RGB-градиент | HSV-градиент | |
|---|---|---|
| Чем считаем | три канала интерполируются независимо | одно число — оттенок — идёт по кругу |
| Середина | между далёкими цветами темнеет: (128, 0, 128) | всегда насыщенный цвет с круга оттенков |
| Что красить | закат, лёд, пламя, шкалы-индикаторы | радуга, переливы, полосы спектра |
| Как пишем | fill_gradient_rgb(leds, N, c1, c2) на реальной плате | цикл с CHSV(startHue + i * delta, 255, 255) — везде |
HSV-градиент: одна ручка на всю ленту
Ручной HSV-градиент — тот же цикл заливки, только цвет каждого пикселя считается формулой: leds[i] = CHSV(startHue + i * delta, 255, 255). Стартовый оттенок плюс номер пикселя, умноженный на шаг. Шаг — единственная художественная ручка, и его шкалу легко держать в голове через произведение: дельта, умноженная на число пикселей, — сколько круга ляжет на ленту. Дельта 16 при 16 пикселях раскладывает ровно полный круг (16 × 16 = 256); дельта 8 — половину круга, от красного через жёлтый и зелёный до аквы; дельта 2 — узкий клин из тридцати двух шагов от красного к жёлтому, готовый «рассвет»; дельта 32 — два нахлёста круга, спектр повторится дважды.
Направление обхода — знак: минус в формуле, CHSV(startHue - i * delta, ...), разворачивает градиент в другую сторону. Переполнение не страшно, потому что оттенок живёт в byte: стартовый 250 плюс i * 3 заворачивается через ноль сам, без единого if — тем же свойством, которым замыкала круг радуга fill_rainbow в уроке про радугу и оттенок. И последний шаг к живому эффекту: подвижный старт. Заведите byte h, прибавляйте единицу между кадрами, подставляйте в формулу — и весь спектр поползёт по ленте, не меняя формы: тот же трюк, что оживлял fill_rainbow, только теперь вы управляете и шириной, и направлением спектра сами.
Практическое применение, которое я собирал чаще всего, — шкала-индикатор: влажность грунта, заряд батареи, громкость. Логика двухчастная, и в симуляторе выше она вся видна: цвет деления красится один раз по его позиции — CHSV(96 - i * 4, ...) даёт зелёное основание и тёплый верх, — а уровень решает, сколько делений горит, простым сравнением i < level. На реальной плате уровень приходит из analogRead, и перевод его в деления — одна строка map(): готовый рецепт с числами лежит в уроке про потенциометр и map — диапазон 0–1023 превращается в 0–16 делений. Получившаяся шкала читается с трёх метров без легенды: чем выше и теплее столбик, тем ближе тревога, — вся семантика в градиенте, а не в подписях. И настройка на месте: полную яркость шкале не давайте — при 255 слепит и в темноте, и при дневном свете; 120–140 через setBrightness — мой рабочий диапазон для индикаторов, цвета остаются читаемыми, глаза целы.
Про направление обхода на реальной плате — последний камень в стену выбора. У HSV-версии библиотеки, fill_gradient с CHSV-цветами, есть четвёртый аргумент-направление: SHORTEST_HUES ведёт от цвета к цвету коротким путём по кругу, LONGEST_HUES — длинным, через все промежуточные оттенки. Красный к синему коротким путём — через фиолетовый, длинным — через жёлтый и зелёный: концы одинаковые, полосы совершенно разные. В ручном цикле тем же управляют знак дельты и стартовый оттенок: развернули минус, подвинули старт — получили вторую дорогу. Направление обхода — такой же параметр градиента, как и сами цвета, и его стоит выбирать сознательно, а не обнаруживать в готовом виде.
- Трёхцветный закат вручную: первые 5 пикселей — дельта 6 от синего оттенка, середина — оранжевая зона, верх — красный; соберите из трёх участков с разными стартами.
- Спектр-пловец: заведите byte h, прибавляйте единицу между кадрами и подставляйте в CHSV(h + i * 16, 255, 255) — полный круг поползёт по ленте.
- «Лёд»: CHSV(160 + i * 2, 180, 255) — узкая синяя полоса с приглушённой насыщенностью; сравните с насыщенностью 255.
- Термометр на 20 пикселях: посчитайте дельту полного круга (256 / 20) и проверьте сомкнувшийся хвост — byte должен закрыть стык сам.
- 1. Что делает fill_gradient_rgb(leds, 16, c1, c2)?
- 2. Почему RGB-переход от красного к синему темнеет в середине?
- 3. Дельта 8 при 16 пикселях — сколько круга оттенков ляжет на ленту?
- 4. analogRead вернул 512 из диапазона 0–1023. Сколько делений из 16 загорится после map(level, 0, 1023, 0, 16)?
- 5. Переход красный — синий «длинным путём» по кругу оттенков пройдёт через…
- fill_gradient_rgb(leds, N, c1, c2) — линейный градиент каналов; есть варианты на три и четыре опорных цвета.
- RGB-середина между далёкими цветами темнеет: каналы остаются на полсилы. Далёкие цвета — только через HSV.
- Ручной HSV-градиент: CHSV(startHue + i * delta, 255, 255); delta × N = сколько круга на ленте.
- Направление обхода — знак дельты вручную или SHORTEST_HUES / LONGEST_HUES у HSV-версии fill_gradient.
- Шкала-индикатор: цвет деления красится один раз, уровень решает, сколько делений горит.
Частые вопросы по теме урока
- Что делает fill_gradient_rgb(leds, 16, c1, c2)?
- Функция идёт по буферу и на каждом пикселе считает промежуточное значение всех трёх каналов: пиксель 0 получает c1, последний — c2, между ними равномерные ступени. Линейно интерполирует каждый канал от c1 к c2 по всей ленте
- Почему RGB-переход от красного к синему темнеет в середине?
- Середина линейного RGB-перехода — (128, 0, 128): оба задающих канала на половине. Насыщенность концов теряется, глаз видит тёмный грязный фиолетовый. В середине каждый канал на полсилы: красный уже упал вдвое, синий ещё не набрал
- Дельта 8 при 16 пикселях — сколько круга оттенков ляжет на ленту?
- 16 × 8 = 128 шагов, а полный круг — 256. Половина: от стартового оттенка ровно до противоположной точки круга. Половина круга
- analogRead вернул 512 из диапазона 0–1023. Сколько делений из 16 загорится после map(level, 0, 1023, 0, 16)?
- 512 — ровно половина диапазона, map переводит её в 8 из 16. Именно так уровень датчика превращается в длину шкалы-индикатора. 8
- Переход красный — синий «длинным путём» по кругу оттенков пройдёт через…
- Длинная дорога от красного к синему идёт через тёплую половину круга — жёлтый и зелёный. Короткая — через фиолетовый. У fill_gradient это SHORTEST_HUES и LONGEST_HUES. жёлтый и зелёный