PunchMuslims join Christians to celebrate Kaduna church’s anniversaryThe Jerusalem PostPete Hegseth faces bipartisan impeachment push after Republican losses in US midterm electionsRai NewsEgitto: scontro tra treni a Giza, almeno 7 morti e 112 feritiZDF heuteAktuelle Pressemitteilungen des ZDFThe GuardianManchester United v Aston Villa: Premier League – liveVanguardMilitary neutralises 60 terrorists, rescues 42 kidnap victimsCBS NewsICE unlikely to scale back crackdown, sources say, citing Stephen MillerGuardian SportSão Paulo Grand Prix: Formula One 2026 – liveNBC NewsNew billboards urge people to ‘boycott the boycott’ of Jewish-owned businessesESPN DeportesUn show aéreo permite a los Bengals vencer a los Falcons en MadridVilaWebLa maquinista de l’Iryo va intentar reportar insistentment que no podia arrencar sense rebre respostaComplete Sports2027 U-23 AFCONQ: Chelle Names Olympic Eagles Squad For DR Congo Clash
The Daily Newsstand · Free, Always
Sunday, November 8, 2026

Качественная анимация на микроконтроллере с оптимизацией, через мои костыли:

Translate

Привет, Хабр! Возникла необходимость сделать анимации на TFT дисплей ST7789 под управлением ESP32. Учитывая общую нагрузку на МК и требования к «живости кадра», выбор пал на UI-анимацию в противовес GIF-спрайтам и иже с ними.

У вас большие запросы!

vk.ru

Что-то хабр матерится на ссылку, вот если не подгрузится: https://vk.ru/clip-243078440_456239017

Логика была такой: нужен самый дешевый по ресурсам (они нужны для работы остальных модулей проекта) но без потери качества способ анимировать лицо, которое в последствии могло бы например следить зрачками за движущимися объектами в кадре. Конечно, это однозначно UI анимация. И тут выясняется, что в отличие от веба (SVG для CSS и Canvas), выбор для C++ весьма скуден. Помимо прочего, я совсем не кодер и честно говоря, не особо себе представлял, как можно «рисовать кодом». Но при таком энтузиазме нет нерешаемых задач.

Суть моего решения была в том, чтобы написать всё на Canvas (как анимации на сайтах), а затем через ИИ портировать на C++. HTML я могу буквально сразу отрендерить в браузере и посмотреть, что там наклякалось. Этот путь показался гораздо проще прямой записи на C++.

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

1. Первые наброски (DeepSeek в чате)

Первый запрос звучал примерно так:

«Сделай эмулятор дисплея 240×280 в HTML обёртке, нарисуй на нём 2 глаза используя Canvas примитивы, используй цвет “циан”, фон чёрный, примени фильтр заливки черными горизонтальными линиями через строку в стиле Atari аркадных автоматов на площадь всего дисплея поверх всех слоев, добавь ползунки регулировки масштаба/удалённости/высоты глаз и json вывод значений»

Как видно, я прямо на скриншоте в редакторе смартфона прикинул корректировку формы (красным маркером), и ИИ сразу подкорректировал, создав усечённый эллипс и новые ползунки. Немного покрутив которые, мы получили:

Как видно, я прямо на скриншоте в редакторе смартфона прикинул корректировку формы, и ИИ сразу подкорректировал, создав усечённый эллипс и новые ползунки (угол наклона усечения, размер зрачков и т.д.). Немного покрутив которые, мы получили:

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

Да, если использовать примитивы разных цветов, как линии в этом примере, то LLM быстрее въезжает, как именно реализовать объект и сколько фигур для этого использовать. Где-то на этом этапе я понял важную вещь: ИИ отлично понимает не только картинки, но и ASCII-арт.

2. ASCII PixelArt как методология

Вы наверняка видели в сети как некие тролли, во избежание бана публикуют всякое непотребство не изображениями, а как бы картинкой из символов? Оказывается это реальная и очень старая технология — ASCII PixelArt. Мне кажется в эпоху LLM Она может пережить второе дыхание т.к. это не просто «картинка из символов», а полноценный формат описания, где каждый символ — это бит. Например использование o для контура и x для фигуры:

             ooooo     ooooo
           ooxxxxxoo ooxxxxxoo
         ooxxxxxxxxxxxxxxxxxxxoo
        oxxxxxxxxxxxxxxxxxxxxxxxo
       oxxxxxxxxxxxxxxxxxxxxxxxxxo
      oxxxxxxxxxxxxxxxxxxxxxxxxxxxo
      oxxxxxxxxxxxxxxxxxxxxxxxxxxxo
      oxxxxxxxxxxxxxxxxxxxxxxxxxxxo
       oxxxxxxxxxxxxxxxxxxxxxxxxxo
        oxxxxxxxxxxxxxxxxxxxxxxxo
         oxxxxxxxxxxxxxxxxxxxxxo
          oxxxxxxxxxxxxxxxxxxxo
           oxxxxxxxxxxxxxxxxxo
            oxxxxxxxxxxxxxxxo
             oxxxxxxxxxxxxxo
              oxxxxxxxxxxxo
               oxxxxxxxxxo
                oxxxxxxxo
                 oxxxxxo
                  oxxxo
                   oxo
                    o

Добавляя новые символы можно обозначить цвета или "подключаемые" пиксели для спрайта движения.

Плюс этого подхода в контуре «текст — человеко/ИИ-читаемость — изображение»:

  • Компактно: легко вставить в PROGMEM как массив байт (Continuous Bit Packing, 1bpp).

  • Наглядно: можно рисовать прямо в чате с ИИ, сразу видно форму и симметрию.

  • Гибко: легко масштабировать и менять по строкам.

3. Мастерские: каждая эмоция — отдельный стенд

Вот это была основная работа. Писать одну большую HTML сразу — путь в никуда: параметров (ползунков) десятки, они конфликтуют, а правя одно, ломаешь другое, да и файл перестаёт умещаться в 1 инференс LLM.

Поэтому каждая эмоция с сложным сценарием сидела в своём отдельном HTML-стенде. Вот, например, «Идея» — eye_idea.html:

Что здесь есть:

Только две эмоции: neutral и 💡 Idea, ползунки редактируют только IDEA. Можно сначала отредактировать форму обьекта в эталон, а потом переписать слайдеры под сценарии.

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

Кнопки «Моргнуть», «Сканирование», «Жалюзи», «Глитч» — чтобы сразу видеть, как лампочка и глаза реагируют на наложение модификаторов.

«Скопировать JSON» — выплёвывает эталонные значения каждого ползунка для фиксации и последующего порта.

Цикл: покрутил ползунки → увидел, как лампа падает сверху с ease-in, «дышит», лучи бегут по верхним 2/3 контура, глаза сжимаются в точку → сохранил в пресет → JSON пошёл в след промпт: "фиксируй как эталон".

4. Сшивка в единый массив

Когда все эмоции устаканились, наступил момент склейки. Появился единый eye_full_optimized.html (будет в файлах статьи). Были убраны все слайдеры регулировок. Этот файл и стал эталоном для порта. И именно из него потом буквально вставлялись объекты в emotions.cpp.

5. Оптимизация под ESP32

Браузер стерпит плавающую точку, sin/cos на лету и quadraticCurveTo. ESP32 — нет. Чек-лист:

  1. Кривые Безье → дуги окружности. В HTML использовал quadraticCurveTo для некоторых анимаций. На C++ вычислять Безье каждый кадр — дорого. Значит реализуем через ближайший аналог:circle_through_3pts строит окружность через начало, конец и середину кривой, математика запекается один раз, рендер идёт через обычные дуги.

  2. Свечения → LUT-спрайты. Радиальные градиенты на МК невозможны. Сгенерировал альфа-каналы свечений (лампочка, месяц, аура) один раз, сохранил в PSRAM. В рантайме — просто raster_blit_alpha_tinted().

  3. Запекание констант. С отладочными ползунками выкинуты и их переменные, в emotions.cpp только жёсткие float-значения.

  4. Отказ от LVGL и ручной порт шрифта. LVGL тянет зависимости и рисует с антиалиасингом (как я понял какое-то сглаживание), который на сканлайнах вреден и жрёт CPU. Поэтому через ИИ я выдрал глифы Bender Black попиксельно и портировал в бинарный формат 1bpp (Continuous Bit Packing). Рендер буквы — цикл по байтам и blend_pixel().

  5. Дешёвый глитч. Для сбоя матрицы не стал делать сложную логику. Беру случайный бэнд строк и делаю memmove в стороны. Быстро, грязно, киберпанк.

6. Финальный контроль (агент MiniMax)

Написать код — полдела. Главный вопрос: как доказать, что на железе картинка не «поплыла» после всех оптимизаций?

К этому моменту C++ был разбросан по десятку файлов, а простая чат-модель уже не справлялась: контекст переполнялся. Тут пригодился агентный ИИ ( в моем случае MiniMax): в отличие от чат-бота, он сам ходит по проекту, читает файлы, собирает их воедино и отлаживает.

Он собрал дифф-стенд: side-by-side сравнение кадра реальной прошивки и эталонного Chromium рендера из изначального Canvas, Попиксельно сравнил что отрисовывает C++ код с исходным HTML, поправил расхождения до незначительных.

Архитектура анимации: слои

Нельзя жёстко привязывать всё к одному состоянию. Код разделён на слои:

  • База (emotions.cpp) — 16 эмоций с базовыми параметрами глаз и зрачков.

  • Idle-слой (idle.cpp) — 7 микросценариев в режиме простоя, накладываются поверх базы как дельты.

  • Сценарии (scenarios.cpp) — сложные оверлеи

  • Имитация жизни : Дыхание (изменение размеров\пропорций) глаз, саккады зрачков и т.д.

Финал:

Пока железо приехало код уже был готов, осталось разрешить несколько аппаратных вопросов дисплея ( ряды битых пикселей, инверсия цветов, BGR-RGB, сегментированные градиенты, двойной буфер) но и тут Дипсик справился.

Связка «Canvas → LLM (чат) → отдельные стенды по сценариям → сшивка в единый файл → агент (дифф-стенд) → C++» даёт быстрые итерации и прозрачную оптимизацию. Минусы - придется терпеть затупы ИИ т.к. они туго воспринимают стилизацию (всё время пытался сделать пропорциональные и четко разделенные глаза\зрачки\веки, убрать сканлайны как баг и т.д.)

Исходники (эталон HTML, дифф-стенд) в папке: https://drive.google.com/drive/folders/1YLwIt4DilwY0FQsWIU31kAiH7Wt_UI65?usp=drive_link . Открывать в браузере т.к. гугл докс не отображает. Жду вопросы в комментиариях)

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.