Эффекты WS2812 на Arduino: радуга и бегущий огонь
WS2812B — светодиод с драйвером в одном корпусе: лента из таких чипов держится на одном проводе данных, а «эффекты» — это просто анимация массива цветов, которая пересылается по цепочке кадром. Радуга, бегущий огонь, комета со шлейфом собираются на одной-единственной функции setPixelColor и понимании, что show() отправляет кадр целиком. Библиотек — две ветки: FastLED с готовыми палитрами и Adafruit_NeoPixel, где эффекты пишут руками; руки полезнее для понимания.
В нашем симуляторе лента — карточка «Адресная лента NeoPixel 16 шт» из палитры «Макетка»: один пин данных, 16 пикселей на стенде. Рантайм исполняет Adafruit_NeoPixel по-настоящему — begin, setPixelColor, show, setBrightness, clear, fill, Color; после show() картинка на стенде меняется, и оба эффекта ниже видны без единого диода на столе. Ниже — разбор колеса цвета, проверенный скетч и честные границы.
Кадр, а не пиксель: как устроен любой эффект
Ключ к эффектам — одна механика: setPixelColor(i, цвет) лишь заполняет буфер в памяти, show() одним пакетом отправляет его в ленту, и только после show() картинка меняется. Забыли show() — лента стоит, скетч «работает» впустую: первая граблина всех новичков. Второй закон: эффект — это цикл по пикселям + время. Каждые 100-150 миллисекунд пересчитываем массив и показываем кадр; скорость анимации — это интервал между кадрами, плавность — шаг изменения цвета.
Колесо цвета: радуга без готовых функций
В Adafruit_NeoPixel нет готовой радуги — её пишут через колесо цвета (Wheel): параметр 0-255 бежит по кругу красный-зелёный-синий-красный. Классическая реализация — три сегмента по 85 позиций с линейной интерполяцией; она же даст и радугу по индексу пикселя (сдвиг фазы от номера), и переливание бегущей точки. Позиция в колесе — это, по сути, hue, сжатый из 360 градусов в один байт.
#include <Adafruit_NeoPixel.h>#define LED_PIN 6#define LED_COUNT 16Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);unsigned long kadrov = 0;unsigned long poslednij = 0; int Wheel(int pos) { // колесо цвета: 0-255 по кругу pos = pos % 256; if (pos < 85) return strip.Color(pos * 3, 255 - pos * 3, 0); if (pos < 170) { pos -= 85; return strip.Color(255 - pos * 3, 0, pos * 3); } pos -= 170; return strip.Color(0, pos * 3, 255 - pos * 3);} void setup() { strip.begin(); strip.setBrightness(120); // 0-255, экономит и глаза, и БП Serial.begin(9600);} void loop() { if (millis() - poslednij < 120) return; // кадр каждые 120 мс poslednij = millis(); kadrov++; strip.clear(); if (kadrov % 40 < 20) { // полпериода — комета for (int i = 0; i < 6; i++) // голова яркая, хвост гаснет strip.setPixelColor(7 - i, Wheel(120 + i * 20)); } else { // полпериода — радуга for (int i = 0; i < strip.numPixels(); i++) strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + kadrov * 4) % 256)); } strip.show(); // без show() лента не меняется!}Прогон в симуляторе: комета бежит по 16 пикселям карточки, затем лента переливается радугой — на стенде видно ровно то, что нарисовал скетч, включая яркость setBrightness(120). Своя функция Wheel компилируется и исполняется, numPixels() возвращает 16. Гамма-коррекцию gamma32() рантайм пропускает (возвращает цвет без изменений) — на глаз на 16 пикселях разницы нет; числовые палитры и шум Перлина из FastLED — это уже другой стек, у нас про него отдельный раздел уроков.
Честная граница: что исполняет симулятор
| Проверка | Симулятор | Только железо |
|---|---|---|
| setPixelColor / show / clear / fill | исполняется, стенд перерисовывается | то же самое |
| setBrightness, Color, numPixels | исполняется | то же самое |
| Своя функция Wheel в скетче | компилируется и работает | то же самое |
| gamma32() | заглушка: цвет проходит без гаммы | чуть глубже оттенки |
| Лента длиннее 16 | конструктор принимает до 256, карточка рисует 16 | да |
| Ток 60 мА/пиксель и питание | нет: электричества в модели нет | да — БП на 16 пикселей уже 1 А |
Грабли: питание, порядок цветов и провод данных
- Питание: белый кадр на полной яркости — 60 мА на пиксель, 16 штук просят почти ампер. От 5V платы Uno светят максимум на пониженной яркости, для полной ленты — отдельный БП и общий GND.
- Порядок цветов: зелёный с красным местами (задали красный — светит зелёный) — это тип ленты NEO_GRB против NEO_RGB, укажите третий аргумент конструктора правильно.
- Резистор 330 Ом в линию данных и конденсатор 1000 мкФ поперёк питания защищают первый пиксель от выброса при подаче питания.
- Каждый кадр без clear() оставляет старые цвета: комета превращается в сплошную полосу — гасите хвост явно.