Гребёнку отопления в мастерской я месяц контролировал цифрами в Serial: восемь термисторов, строка чисел раз в секунду. Числа я честно смотрел ровно неделю, потом перестал — глаз отказывается сравнивать восемь значений каждые десять секунд. Выходом стала лента под трубами: восемь пикселей против восьми датчиков, чёрная — холодная, красная — греет, жёлтая — горячо. Теперь тревога видна через комнату, а взгляд на щиток занимает полсекунды.
Палитры мы уже прошли: маршруты PartyColors_p и выбор между палитрой и fill_rainbow — в уроке про готовые палитры, свои градиенты из опорных точек — в уроке про свои палитры. Здесь угол другой: карта значений, где величина сама становится позицией на палитре. Один датчик — один пиксель, и вместо восьми if вы получаете одну строку ColorFromPalette.
Значение в цвет: маршрут HeatColors_p
HeatColors_p — палитра-термометр: от чёрного через тёмную вишню и красный к оранжевому, жёлтому и белому на пике. Работает она как линейка с делениями: аналоговое значение с датчика вы превращаете в индекс 0-255 и берёте цвет по этому индексу. map(adc, 380, 640, 0, 255) переводит показания делителя с термистором в позицию на маршруте, constrain прижимает выход за границами — map считает линейно и без зазрения уедет и в минус, и за 255.
Три детали, на которых карта живёт или умирает. Первое — сглаживание: сырой analogRead дёргается на десяток единиц, и пиксель стробит; формула (old*3 + raw)/4 прижимает показание к среднему без float. Второе — граница калибровки: 380 и 640 — это мои замеры делителя 10к/10к при 24 и 64 °C, свои числа снимите стаканом воды и термометром. Третье — тревога: индекс выше 210 означает перегрев, и лента мигает яркостью через millis, а не через delay, чтобы не вставать на паузу.
#include <FastLED.h>#define LED_PIN 6#define NUM 8const byte PIN_NTC[NUM] = {A0, A1, A2, A3, A4, A5, A6, A7}; // Nano A0-A7// на Uno A6/A7 нет: уберите их и возьмите NUM = 6int smooth[NUM]; void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM); FastLED.setBrightness(160); for (int i = 0; i < NUM; i++) smooth[i] = analogRead(PIN_NTC[i]);} void loop() { bool alarm = false; for (int i = 0; i < NUM; i++) { int raw = analogRead(PIN_NTC[i]); smooth[i] = (smooth[i] * 3 + raw) / 4; // сглаживание без float int v = map(smooth[i], 380, 640, 0, 255); // 24..64 °C в индекс v = constrain(v, 0, 255); leds[i] = ColorFromPalette(HeatColors_p, v, 255, LINEARBLEND); if (v > 210) alarm = true; // жарче ~57 °C } if (alarm) { // тревога: мигаем яркостью, не delay FastLED.setBrightness(millis() / 150 % 2 ? 160 : 60); } else { FastLED.setBrightness(160); } FastLED.show(); delay(100);}Разбор. Каждая итерация — самостоятельная точка: чтение, сглаживание, перевод в индекс, цвет. Никакой связи между пикселями нет — это не волна, а именно карта: гаснет один сегмент трубы, гаснет один пиксель. LINEARBLEND смешивает соседние ячейки палитры, без него лента полосит ступенями на переходах; где ступени нужны намеренно, там и NOBLEND. Тревога у меня одна на всю гребёнку: любой пиксель выше 210 — мигает вся лента; если нужен другой характер, соберите тревогу по пикселю, но через комнату читается и общий.
Пара подставок под ваши условия. A6 и A7 на Nano работают только как analogRead, pinMode им не нужен — на Uno их нет, карта ужимается до шести точек или переезжает на ADS1115. Держите темп скромным: 100 мс между кадрами хватает глазу и не грузит АЦП; чаще — только дрожь показаний. И не забывайте, что палитра на пике уходит в белый: жёлто-белый пиксель — это не «сломалось», это жарче плана.
| Индекс v | Цвет | Смысл на моей гребёнке |
|---|---|---|
| 0-50 | чёрный, тёмная вишня | до 30 °C — фон, контур холодный |
| 50-130 | красный | 30-45 °C — рабочий нагрев |
| 130-210 | оранжевый | 45-57 °C — горячо, слежу |
| 210-255 | жёлтый к белому + мигание | 57+ °C — тревога, вентилятор |
- 1. Что меняет LINEARBLEND в ColorFromPalette?
- 2. Зачем после map стоит constrain(v, 0, 255)?
- Один пиксель = одна точка нагрева; величина с датчика сама становится позицией на палитре
- map(adc, minADC, maxADC, 0, 255) + constrain — маршрут в индекс HeatColors_p
- Сглаживание (old*3 + raw)/4 убирает строб от шума АЦП без float
- Границы map калибруются по своим термисторам: два прогрева с термометром
- Порог v > 210 — тревога миганием яркости на millis, читается через комнату
Частые вопросы по теме урока
- Что меняет LINEARBLEND в ColorFromPalette?
- Индекс 200.7 без смешивания даёт ячейку 200 — ступень. LINEARBLEND интерполирует между 200 и 201, и лента не полосит на переходах. Смешивает соседние ячейки палитры вместо ступенчатого перехода
- Зачем после map стоит constrain(v, 0, 255)?
- map не ограничивает: показание за верхней границей даст индекс 300 и больше. У палитры таких нет — constrain прижимает край. map считает линейно и за границами диапазона отдаёт числа меньше нуля и больше 255