惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
Vercel News
Vercel News
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
N
News and Events Feed by Topic
Application and Cybersecurity Blog
Application and Cybersecurity Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 【当耐特】
WordPress大学
WordPress大学
Google Online Security Blog
Google Online Security Blog
博客园 - Franky
Attack and Defense Labs
Attack and Defense Labs
Help Net Security
Help Net Security
V
Visual Studio Blog
Jina AI
Jina AI
H
Heimdal Security Blog
小众软件
小众软件
O
OpenAI News
腾讯CDC
The Last Watchdog
The Last Watchdog
雷峰网
雷峰网
Cloudbric
Cloudbric
量子位
博客园_首页
The GitHub Blog
The GitHub Blog
L
LangChain Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Last Week in AI
Last Week in AI
V2EX - 技术
V2EX - 技术
Security Archives - TechRepublic
Security Archives - TechRepublic
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 司徒正美
M
MIT News - Artificial intelligence
大猫的无限游戏
大猫的无限游戏
T
Tor Project blog
C
CERT Recently Published Vulnerability Notes
W
WeLiveSecurity
B
Blog
C
Check Point Blog
TaoSecurity Blog
TaoSecurity Blog
T
Threatpost
Hugging Face - Blog
Hugging Face - Blog
Recent Announcements
Recent Announcements
Project Zero
Project Zero
Hacker News: Ask HN
Hacker News: Ask HN
U
Unit 42
Simon Willison's Weblog
Simon Willison's Weblog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как проект на лето растянулся на полгода: Делаем свою светодиодную вывеску с управлением по Wi-Fi (ч.1)
Андрей Коломиец · 2026-06-02 · via Все публикации подряд на Хабре

Давайте знакомиться!

Меня зовут Андрей, и я учусь на 1-м курсе по направлению радиоэлектроники. В этой статье я поделюсь своим путём разработки очередной железки, с важной деталью: я попытаюсь сделать не просто проект, а настоящий продукт, который не стыдно кому-нибудь подарить.

Так как статья получилась довольно объёмной, она будет состоять из 2-х частей: В первой части я расскажу, как появилась идея, какие я ставил перед собой цели, вместе разберёмся, как управлять адресной светодиодной лентой, изогнутой в сложную форму, и напишем несколько режимов анимации. Во второй части Вас ждёт рассказ о написании с нуля своего веб-интерфейса для управления вывеской, продумывании UX, решении возникающих трудностей и реализация своего способа хранения пресетов в памяти микроконтроллера, разводка печатной платы и финальная сборка.

Ну что, начинаем!

Как появилась идея?

Вот у вас есть любимые музыканты или, может быть, актеры? А вам не хотелось подарить им какую-нибудь памятную вещь? Мне иногда в голову приходят именно такие мысли.

Вот, например, лет в 12-14 я очень любил сериал «Стар против сил зла». После его просмотра я захотел как-то выразить своё восхищение мультиком его автору. В то время, я только недавно купил 3D-принтер и решил, что сделаю фигурку главной героини. Логично, что воплотить в жизнь задумку у меня не получилось, но зато я прокачал свои навыки в 3D-моделировании.

Так произошло и в этот раз, только сейчас одна маленькая безумная идея переросла в нечто большее — не просто проект, а целый продукт, который хочется дальше развивать и улучшать.

Отмотаем плёнку на 2025. Весна. Я в 11 классе готовлюсь к сдаче ЕГЭ. Очень эмоционально сложное для меня время, силы на исходе, есть проблемки в жизни, и вот, слушая «Мою волну» по пути в подземку, я залипаю на трек «Антенны», а затем «Зима» и другие. Они как-то отзывались во мне, давали силы двигаться дальше, несмотря на усталость от постоянной учёбы 24/7. Именно так для себя я открыл такого исполнителя, как OMNIXX. С того времени я успел поступить в ВУЗ, проблем больше никаких нет, а треки я до сих пор слушаю и подпеваю, пока еду в метро.

После сдачи ЕГЭ наступает лето. Время, когда нужно определиться, куда поступать, и хорошенько отдохнуть перед первым курсом. Но сидеть я на месте не люблю, мне нужна была электроника, какой-нибудь проект. Желательно, который я смогу довести до MVP, а то меня уже изрядно бесят проекты, которые лежат на полке и ждут, когда я к ним вернусь и доделаю.

Именно в этот момент я складываю идеальный пазл проекта в голове:

  • Мозгом должна быть ESP. Если использовать жёсткую логику (как я бы хотел), то вероятность неудачи и очередного недоделанного проекта стала бы 100%.

  • Проект должен иметь продуманный UI/UX, чтобы быть понятным для людей, не связанных с электроникой.

  • Было бы круто использовать адресную светодиодную ленту, потому что я с ней ни разу не работал.

И тут я понимаю, что самый лучший вариант — светодиодная вывеска с управлением по Wi-Fi & Bluetooth. Но что же писать на этой вывеске? А ответ не заставил себя долго ждать. Пишем OMNIXX. А потом, может, и получится ему как-нибудь подарить, ну или отправить СДЕКом.

Буквы и неон

Идея есть. Дело за малым — реализовать её. Для начала определимся с физической частью нашей вывески. В качестве ленты будем использовать гибкий неон. Важно, чтобы он был именно адресным, ведь иначе мы не сможем сделать крутые эффекты. Но обычной светодиодной ленте с маркетплейса ещё нужно как-то придать форму. Решил, что самый простой и аккуратный способ — напечатать формы букв для неона с отверстиями для проводов, куда будет вставляться лента и держаться там даже без клея.

Сначала я думал написать текст без разрывов, одним куском. Пересмотрел кучу различных «неоновых» шрифтов, попытался нарисовать сам, но ни один из вариантов мне не понравился. Поэтому пришлось вручную вырисовывать форму всех букв, учитывая максимально возможный изгиб ленты. Так я пришел к следующим элементам: паз для ленты шириной 5 мм, стенка толщиной 2 мм и «юбка» 3 мм, в целом, я её добавил для красоты, но это оказалось крайне удачным решением, ведь именно на ней я сделал площадки для крепления букв к основе для вывески.

Напечатал одну букву, запихнул ленту — всё идеально, только изначальная высота паза для ленты оказалась недостаточной: сбоку были видны вырезы в резине неона для проводов. Я увеличил высоту прямо до начала прозрачной части ленты. И такой вариант меня устроил. Отлично, с буквами разобрались, осталось только все их напечатать.

Но эти буквы надо к чему-то прикреплять. Для опытной модели, которая должна была использоваться для понимания общей картины проекта во время его разработки, я решил особо не заморачиваться и распечатал монтажные рейки всё на том же 3D-принтере. Так как единая рейка не умещалась на столе моего принтера, мне пришлось разделить её на 2 части. Способ крепления букв прост: на оборотной стороне монтажной рейки находятся пазы для гаек М3, которые удерживают их от проворачивания в момент затяжки винта М3. Не забыл я и про кабель-менеджмент. Чтобы провода не торчали во все стороны с обратной стороны, я сделал маленькие прорези для стяжек.

Монтажная рейка для букв, напечатанная на 3д принтере

Монтажная рейка для букв, напечатанная на 3д принтере

Собранная "опытная модель" вывески (пока что без контроллера)

Собранная "опытная модель" вывески (пока что без контроллера)

Начало работы над проектом

Когда физическая опытная модель готова, начинается самый интересный и долгий этап: программирование микроконтроллера. Делать то, не зная что, — сомнительная затея, поэтому я сначала поставил перед собой задачи. Я отучился в Московской школе программистов, где прошел курс объектно-ориентированного программирования. И знаете, мне очень это понравилось, поэтому я хотел не просто реализовать вывеску, но и сделать это красиво и правильно, по ООП-шному, с классами, методами, разбиением кода на файлы и составляющие, а не одним длинным main.cpp с обычными функциями и сотней глобальных переменных. Первый пункт нашего воображаемого ТЗ есть, что же ещё нужно? Ну как минимум различные режимы работы, ну и реализовать их настройку и переключение. Изначально я думал сделать мобильное приложение в конструкторе и управлять вывеской по Bluetooth, но быстро понял, что для MVP это слишком труднореализуемая цель, так что остановимся на WEB-странице. Ну и добавим мой самый важный пунктик: «сделать красиво». Я считаю, что устройство должно быть не только технологичным и навороченным, а ещё и приятным в использовании. Прямо как у Apple. Это означает, что нужно предусмотреть каждую мелочь, дать возможность пользователю настраивать почти всё, а работа самой вывески должна быть максимально стабильной и отказоустойчивой, ну и, конечно же, всё должно быть стильно.

Получилось как-то так:

  1. Реализовать различные режимы и эффекты подсветки

  2. Сделать ВЕБ-страницу для управления

  3. Написать красивый код по ООП стилистике

  4. Продумать UX и сделать всё стильно

Зажигаем светодиоды

Прежде всего начнём с эффектов. Долго думая, какие вообще анимации нужно добавлять, я остановился на вот таком вот базовом наборе:

  1. FLASHING LIGHT — бегущий вправо блик

  2. RAINBOW — радуга

  3. COLOR — заливка цветом

  4. GRADIENT — градиент

  5. FILLIGN LINE — заполнение цветом, а потом такое же постепенное угасание

  6. STROBE — стробоскоп

Каждый из режимов ещё должен по-своему настраиваться, так что возможностей для полёта фантазии пользователя точно хватит.

Итак, что мы имеем:

  • адресную светодиодную ленту;

  • анимации для ленты;

  • контроллер ESP8266, который будет управлять всем этим делом.

Для начала нужно сделать так, чтобы лента могла светиться. Тут мы сталкиваемся с первыми сложностями: нужно узнать тип этой ленты, чтобы подобрать подходящую библиотеку, и повысить логический уровень, ведь ESP работает на 3-вольтовой логике, а светодиодная лента питается от 5 В. Проблему согласования решим с помощью обычного транзистора, подключённого по вот такой схеме:

Схема есть, значит, пора время взять в руки паяльник и сделать первую версию контроллера: припаять к макетке Wemos, 2 резистора, транзистор, немного перемычек и гребёнок для более удобного подключения.

Получившийся «бутерброд» (модуль bluetooth припаял, когда думал делать управление через него)

Получившийся «бутерброд» (модуль bluetooth припаял, когда думал делать управление через него)

С этим разобрались, теперь, проведя вскрытие маленького кусочка неона, определяем, что перед нами обычные WS2812. Для управления будем использовать распространённую библиотеку FastLED. Мы уже можем загрузить пример и увидеть мигание одного светодиода, но… это как-то мало, нам нужно больше!

А как рулить?

Взаимодействие с лентой максимально простое. У нас есть массив CRGB leds[NUM_LEDS], в котором хранятся цвета наших светодиодов. В setup мы инициализируем библиотеку, давая ей понять, с какой лентой мы работаем, сколько у нас есть светодиодов и ещё по мелочи настроек, а уже в основной функции loop присваиваем значения цветов светодиодов и обновляем вывод. Всё просто!

void setup() {
    FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
    FastLED.setBrightness(60);
    FastLED.setCorrection(TypicalLEDStrip);
    FastLED.clear();
}

void loop() {
	for (int indx = 0; indx < NUM_LEDS; indx++){
		leds[indx].setHue(value); // можно так или [тип value - byte]
		leds[indx] = color; // или вот так [тип color - CRGB]
	}
	FastLED.show();
}

Уже зная это, можно написать кучу анимаций и эффектов, но есть одно но… Для контроллера все светодиоды идут последовательно друг за другом, а у нас нет. Конечно, есть стандартные функции для светодиодных матриц, но даже они нам не подойдут: светодиоды в буквах располагаются хаотично. Для заливки цветом это неважно, но для остальных эффектов последовательность зажигания светодиодов крайне важна. Что ж, придётся делать собственную матрицу. Для этого нужно разбить нашу надпись сеткой. Количество строк определим по наибольшему количеству светодиодов на одной вертикали, а ширину столбцов прикинем «на глаз», примерно по ширине свечения одного пикселя. А дальше идём с левого края и заполняем индексами светодиодов. Но что же делать с пустым местом?* «0» *туда не поставишь, ведь это индекс первого светодиода. Делаем все наоборот: ставим большое число, в моём случае я поставил 100, но можно и 200, и 999 и если такой индекс будет попадаться, то просто игнорируем его.

Визуализация разбиения надписи на матрицу. Оранжевые точки - светодиоды.

Визуализация разбиения надписи на матрицу. Оранжевые точки - светодиоды.

int order[18][4] = {
	//...
	// пример матрицы для буквы N. 100 - пустое пространство
	{49, 48, 57, 56},
    {100, 100, 100, 55},
    {51, 52, 53, 54},
    //...
}

Теперь, проходясь по строчкам такой матрицы, мы будем последовательно зажигать столбцы на нашей вывеске. Это позволяет нам реализовать нашу самую первую анимацию: бегущий блик.

Для этого мы будем проходить в цикле по всему нашему массиву order и присваивать нужным светодиодам из leds значение цвета color, а остальные очищать, т. е. «выключать». Не забудем, что между итерациями цикла нужна задержка (переменная speed), ведь скорость работы МК большая, и без неё наш глаз не успеет увидеть пробегающую полоску. Для большей вариативности сделаем ещё одну задержку, только уже между циклами нашей анимации. Обязательно делаем на millis(), чтобы не останавливать работу нашей программы. Это окажется критически важным дальше.

byte led_lines_number = 18;

void flash_light(CRGB* leds, CRGB color, unsigned int speed, unsigned int cool_down) {
    static byte l = 0;
	
	// задержка мажду циклами анимации на время cool_down + время на 1 цикл
    if (millis() - time_stamp >= cool_down + speed*led_lines_number) { 
	    // задержка между сдвигами вертикальной полоски
        if (millis() - secondary_time_stamp >= speed) {
	        // Если долши до конца, то обнуляем итератор L
            if (l == led_lines_number) {
                time_stamp = millis();
                l = 0;
            } else {
                FastLED.clear();
                
                // проходимся про строчке матрицы и зажигаем вертикальную линию на вывеске
                for (int indx = 0; indx < 4; indx++) {
	                // игнорируем пустоту с индексом 100
                    if (order[l][indx] != 100) {
                        leds[order[l][indx]] = color;
                    }
                }
                
                
                l++; // двигаемся дальше
                secondary_time_stamp = millis(); 
            }
        }
    } else {
        FastLED.clear(); // выключаем все светодиоды.
    }
}

Ну вот и всё! Первая анимация готова! Не так уж это было сложно. На реализации каждой анимации я останавливаться не буду, иначе Вы точно уснёте, а вот про особенность регулировки яркости, которая учитывается у меня в проекте, расскажу.

Когда простое оказывается сложным

Давайте вместе реализуем ещё один простенький режим. Пусть это будет плавное мигание всей ленты выбранным цветом. На первый взгляд тут все тоже легко: в библиотеке яркость ленты задаётся значением от 0 до 255, просто в цикле присваиваем то возрастающее, то убывающее значений яркости значение, и всё!

Но если мы будем линейно увеличивать яркость, то в какой-то момент нашему глазу покажется, что яркость как-то неравномерно увеличивается. И глаз будет прав! Дело в том, что он воспринимает яркость экспоненциально, а не линейно. Получается, нам необходимо перевести линейные значения к экспоненциальным. На просторах интернета найдём уравнение вида:

x'=\frac{255 x^k}{255^k}

где k - коэффициент гамма-коррекции, обычно принимается в диапазоне 2.0-3.0. Чтобы не вычислять каждый раз новое значение и не нагружать этим микроконтроллер, запишем значения, полученные для каждого уровня яркости от 0 до 255, в массив:

const uint8_t light_gamma[256] = {
  0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
  2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5,
  5, 5, 5, 5, 6, 6, 6, ..... 202, 205, 207, 209, 211, 214, 216,
  218, 220, 223, 225, 226, 230, 231, 235, 236, 240, 241, 245, 246, 250, 251, 255,
};

Теперь, если изменять яркость с гамма-коррекцией, то мы просто обращаемся к значению из массива, находящемуся по индексу, соответствующему желаемому уровню яркости.

Ну вот теперь, наверное, всё? Нет, это до сих пор не конец.

Для понимания, чего нам ещё не хватает, предлагаю представить такую ситуацию: Пользователь настроил яркость вывески в тёмной комнате. Она не слепит, но и не светит тускло. Идеально, одним словом. Пусть этот уровень яркости будет равен 20 с учётом гамма-коррекции. Пользователь включает нашу анимацию плавной пульсации. Вывеска плавно потухла… «Красота!» — подумал пользователь. Затем она начала потихоньку светиться, потом ещё ярче, ещё, ещё и ещё, пока не дойдёт до уровня 255 и не ослепит пользователя в тёмной комнате.

Чтобы такого не происходило, обязательно добавляем проверку, что яркость светодиодов не оказалась выше указанной пользователем (BRIGHT).

Предлагаю посмотреть на конечный вид режима. У меня реализована ещё возможность выбрать, как будет изменяться яркость: линейно или в соответствии с гамма-коррекцией.

void falshing_color(CRGB* leds, CRGB color, unsigned int speed, bool by_gamma){
	static short index = 0; // индекс яркости из массива гаммы (яркость от 0-255)
    static bool increasing = 0; // флаг, отвечающий за увеличение или уменьшение яркости

	// задержка между шагами уменьшения яркости
    if (millis() - time_stamp >= speed) {

		// определяем, как мы уменьшаем яркость
        if (by_gamma) {
            FastLED.setBrightness(light_gamma[index]);
        } else {
            FastLED.setBrightness(index);
        }

        // определение: увеличение или уменьшение индекса
        if (!increasing) {
            index--;
            if (index < 0) {
                increasing = !increasing; // меняем направление
                index = 0;
            }
        } else {
            index++;
            // проверка, что мы достигли нужного уровня. в обоих режимах
            if ((by_gamma && light_gamma[index] >= BRIGHT) || (!by_gamma && index >= BRIGHT) || index == 255) {
                increasing = !increasing; // меняем направление
            }
        }
        time_stamp = millis();
    }
}

Вот так мы вместе с вами разработали режим с нуля, и при этом обнаружили много занимательных мелочей, которые надо учитывать и не упускать из виду. Так я реализовывал и продумывал все 23 анимации, которые получились различными вариациями 6 основных режимов.

Фуф, вы там не устали? А ведь это только начало!

Рефакторинг — наше всё

Где-то на этом моменте в нашу историю врывается объектно-ориентированное программирование. Помните я о нём говорил в начале? Так вот, его время настало.

Для тех, кто не знает, вот краткий ответ ИИ, что же это такое:

ООП (Объектно-Ориентированное Программирование) — это подход к написанию программ, в котором код организован вокруг объектов и классов, а не вокруг отдельных функций и переменных.

Писать огромный проект в один файл — ужасная идея, так что самое время быстренько разбить текущий проект на файлы.

Давайте разберемся, как это работает, на примере знакомых функций анимации. После рефакторинга структура проекта становится следующей:

src
|-- animations
|   |-- animations.h
|   |-- animations.cpp
|-- main.cpp

В файле .h инициализируем наши функции.

void flash_light(CRGB* leds, CRGB color, unsigned int speed, unsigned int cool_down);
void falshing_color(CRGB* leds, CRGB color, unsigned int speed, bool by_gamma);

Именно его мы будем импортировать во все места, где он будет требоваться. А уже само определение функций будет находиться в .cpp файле. Это нужно, чтобы не происходило переопределение при импорте. Ещё важно помнить про иерархию наследования и заранее продумывать, куда какой файл и с какими функциями, классами и переменными будет подключаться, чтобы не получить ошибку циклического импорта.

Итак, мы уже проделали большую часть работы:

  • сделали 3D-модели и распечатали их, а потом собрали опытную версию вывески;

  • спаяли контроллер для управления;

  • сделали несколько анимаций;

  • провели небольшой рефакторинг, чтобы не запутаться во всем коде. Но впереди еще много работы: нужно придумать, как управлять всеми режимами, чтобы это было удобно для пользователя.

Спасибо за прочтение! Увидимся в следующей части!

P.S. Так как все предметы, связанные с электроникой, начинаются со 2-го курса, то я самоучкой изучаю схемотехнику, программирование и разводку печатных плат, поэтому какие-то решения и рассуждения могут быть нелогичными или неправильными. Так что, если вы вдруг заметите ошибки или знаете, как можно было лучше решить возникшие проблемы, — буду рад любым комментариям!