Мой блог
Как подружить локальную LLM с дешевым дисплеем ESP32: интерактивный экран для каждого вопроса

Большинство умных дисплеев предлагают фиксированный набор интерфейсов, жестко заданный разработчиками прошивки. Обычно это часы, погода, календарь — и на этом выбор заканчивается. За годы использования таких устройств я убедился, что все они работают по единому шаблону и представляют собой типичные стандартные гаджеты. В последние несколько месяцев у меня в непривилегированном контейнере LXC на сервере Proxmox запущен Hermes Agent, работающий на базе такой технологии, как локальная LLM. Я общаюсь с ним в Telegram точно так же, как с обычным человеком. Недавно компания Elecrow прислала мне за 30 долларов устройство CrowPanel 1.46-inch rotary display — небольшой круглый сенсорный экран с разрешением 360х360 пикселей и механическим энкодером. Это навело меня на мысль объединить их. Теперь я отправляю агенту текстовый запрос в мессенджере, и спустя несколько секунд на дисплее отображается именно то, что я просил. По сути, это обычный скриншот: элементы интерфейса не обновляются сами по себе, а для каждого обновления ИИ-агент должен передавать данные по локальной сети. Честно говоря, больше всего времени у меня ушло вовсе не на интеграцию с Hermes, а на то, чтобы заставить саму панель хоть что-то нарисовать, но об этом позже. В итоге всё заработало, причем агент на удивление успешно справляется с задачей компоновки информации для столь скромного экрана.
Особенности железа CrowPanel 1.46
Эта плата построена на базе микроконтроллера ESP32-S3 и оснащена 8 МБ памяти PSRAM и 16 МБ флеш-памяти. Круглая IPS-панель диагональю 1,46 дюйма имеет разрешение 360х360 пикселей. На борту также имеются сенсорный слой CST816T, энкодер с возможностью нажатия и кольцо из восьми адресных светодиодов по периметру. Стоимость устройства составляет 29,99 доллара, в комплект входит корпус из пластика и акрила. Разъем USB-C здесь отсутствует в отличие от других энкодеров компании, поэтому питание и подключение по UART осуществляются через специальный кабель с разъемом USB с одной стороны и ZX-MX 1.25-4P с другой.
Управлять интерфейсом на круглом полуторадюймовом экране с помощью пальца неудобно, так как подушечка пальца закрывает большую часть информации в момент чтения. К счастью, поворотный энкодер полностью решает эту проблему и делает использование гаджета комфортным. Вы вращаете регулятор, содержимое на экране меняется или масштабируется, а рука при этом не загораживает контент.
Почему половина пикселей скрыта за рамкой
Круглые экраны отнимают гораздо больше рабочего пространства, чем кажется на первый взгляд. Самый большой прямоугольник, который целиком помещается в окружность диаметром 360 пикселей, имеет размер всего 254х254. Таким образом, примерно половина всех пикселей скрыта за безелем и бесполезна. Я остановился на рабочей зоне 254х236 в центральной части: она шире упомянутого квадрата, но по высоте подобрана так, чтобы диаметр круга в любой точке оставался больше ширины контента. Как выяснилось, эта кольцевая зона играет ключевую роль в таблице стилей, используемой агентом, и прописана в качестве правила в его файле навыков. В противном случае языковая модель, получившая задачу разработать макет под разрешение 360х360, будет забивать информацией самые углы.
Отказ от HTML на стороне устройства
Когда я отправляю сообщение Hermes Agent в Telegram, он формулирует ответ в виде HTML-блока, основываясь на моем запросе. Работающий в фоновом режиме Chromium рендерит эту страницу ровно в разрешении 360х360 пикселей и делает скриншот. Полученный файл PNG передается по локальной сети на дисплей, который сохраняет изображение, выводит его на экран и позволяет переключаться между ними при вращении ручки. Микроконтроллер ESP32 не занимается разбором тегов. Это сложный путь, требующий больше усилий, чем стандартные протоколы дисплеев, поскольку в цепочке задействован полноценный браузер. Тем не менее, такой подход избавляет от необходимости использовать жесткие фиксированные схемы и открывает возможности, ограниченные лишь способностью нейросети писать CSS. Благодаря этому добавление нового типа экрана никогда не требует повторной прошивки устройства.
Разработчик сохранил два типа карточек, которые устройство способно генерировать самостоятельно, в ситуациях, когда запуск браузера избыточен. Метрическая карточка представляет собой одно крупное число с меткой, единицей измерения и подзаписью, а текстовая карточка содержит заголовок и несколько строк текста под ним. В подобных случаях элементы отрисовываются на ESP32 с использованием библиотеки LovyanGFX, хотя у этого подхода есть минус — встроенные шрифты поддерживают только ASCII. Изображения адресуются по контенту, а каждая карточка идентифицируется хэшем SHA-256 своего отрендеренного PNG-изображения и сохраняется локально. Если агент отправляет набор карточек, ESP32 возвращает дайджесты тех элементов из этого набора, которые ей еще не знакомы, поэтому на устройство загружаются только новые данные. Это позволяет избежать повторной отправки одинаковой информации при двойной передаче того же массива. Благодаря SHA-256 можно легко проверять соответствие пересылаемых данных заголовку, что практически не требует дополнительных ресурсов благодаря изначальной архитектуре. На данный момент количество карточек ограничено десятью из-за наличия 8 МБ PSRAM, при этом каждая карточка представляет собой 16-битный спрайт размером 360 на 360 пикселей. Файл навыков инструктирует агента выбирать наиболее экономичный тип карточки, подходящий для текущей задачи. Всего через несколько минут после развертывания системы появились три набора данных, каждый из которых был сформирован по собственному сценарию без какого-либо вмешательства или подсказок со стороны.
Запрос на сканирование домашней сети привел к появлению восьми нативных текстовых карточек с перечислением устройств онлайн. Следующий запрос свежих заголовков с портала XDA привел к созданию пяти HTML-карточек с миниатюрами статей. Это означало, что локальная LLM смогла задействовать Chromium для загрузки и рендеринга материалов с внешнего ресурса. Когда же пользователь попросил пропинговать сетевой шлюз, хост Proxmox и DNS-сервер 1.1.1.1, агент мгновенно вернулся к использованию простых нативных карточек.
Особенности работы с библиотекой LovyanGFX и аппаратными нюансами
Прошивка собиралась на базе LovyanGFX версии 1.2.7 из репозитория PlatformIO с применением официально задокументированной конфигурации панели от Elecrow, однако экран оставался абсолютно темным. Подсветка при этом работала, функция инициализации возвращала стандартный статус, беспроводная сеть успешно подключалась, HTTP-сервер функционировал, mDNS публиковал устройство, а PSRAM распределяла память без ошибок. Ошибки не фиксировались, поскольку с точки зрения самой ESP32 никаких проблем не возникало. Причину удалось обнаружить в нестандартном подходе производителя к аппаратному обеспечению.
На официальной странице изделия указан контроллер JD9855, однако в примерах кода задействован класс lgfx::Panel_ST77961. Как выяснилось, обе версии правдивы лишь отчасти. Репозиторий производителя содержит собственный форк LovyanGFX, в котором метод инициализации полностью лишен стандартного списка регистров, замененного на последовательность команд для JD9855. Сборка проекта с использованием оригинальной актуальной библиотеки приводит к отправке корректного списка инициализации ST77961 контроллеру, который принимает команды, но не может их обработать. В итоге последовательность инициализации была перенесена в собственный подкласс Panel_LCD вместо использования измененного форка.
После этого возникло еще два небольших затруднения. Пример от Elecrow вызывает функцию gfx.startWrite() сразу после инициализации и никогда ее не закрывает. Копирование этого решения привело к появлению на экране вертикальных столбцов с зависшими пикселями, так как LovyanGFX ожидает завершения DMA только на этапе выполнения внешнего метода завершения записи. Если счетчик никогда не обнуляется, каждая новая передача данных конкурирует с предыдущим процессом. В оригинальном коде производителя эта проблема решалась тем, что функция обратного вызова для отрисовки LVGL закрывала внешнюю транзакцию перед каждой отправкой данных через DMA. Кроме того, конфигурация запускала SPI-шину на частоте 80 МГц, из-за чего биты доставлялись с запозданием на тактовых фронтах. Снижение частоты шины до 40 МГц полностью устранило артефакты изображения.
Ограничения компактного экрана и редактирование контента агентом
Возможность вывода статей с технологического ресурса работала корректно, однако сам метод обработки информации заслуживает внимания. Ограниченная площадь экрана заставила автономного агента самостоятельно заниматься редактированием текстового материала, чтобы заголовки гармонично вписывались в отведенное пространство.
Агент проявил гибкость и переписал длинные заголовки, сократив их до размеров компактных карточек без потери общего смысла. Такой подход демонстрирует способность языковой модели адаптироваться под строгие аппаратные ограничения интерфейса в реальном времени, самостоятельно принимая решения о форматировании данных для вывода на нестандартный круглый дисплей.
ИИ самостоятельно понял, что дисплей небольшой, и сообщил, что сократил пару заголовков, чтобы они нормально уместились на круглом экране без потери исходного смысла. В переданных модели инструкциях не было прямых указаний переписывать текст ради подгонки под размеры — там лишь отмечалось, что карточка с миниатюрой рассчитана на две строчки заголовка. Нейросеть сама догадалась, что при жестких лимитах верстки нужно урезать слова, причем выбрала наименее значимые из них и отчиталась о проделанных изменениях.
Аналогичным образом в более крупном масштабе сработал сетевой сканер. При временном предоставлении доступа к сканированию сети обнаружилось около 75 активных хостов в двух подсетях, однако сам дисплей вмещает всего десять строк. Список не был просто обрезан: модель выделила инфраструктурные элементы, назвав их ключевыми, а остальную информацию перенаправила в ответное сообщение Telegram. В итоге на экран вывелись восемь наиболее важных служб, а остальное ушло в чат. Система не ограничилась простой ARP-таблицей, а проверила хостнейм каждого устройства, распознав OPNsense, TrueNAS, Frigate NVR, Proxmox Backup Server и Jellyfin, после чего вывела именно эти названия на экран.
Удобство использования и неожиданная гибкость устройства
Любая система способна генерировать HTML, но ни одна прошивка на базе ESP32 не умеет динамически принимать заголовок и отсекать лишнее при выводе на миниатюрный экран. Подобные ограничения пошли результатам только на пользу, избавив пользователя от сырого потока данных. Само устройство оказалось очень приятным в повседневном использовании и удивительно функциональным.
Управление и перспективы развития
Короткое нажатие на кнопку на самом дисплее не запускает никаких локальных процессов, кроме вспышки светодиодного кольца длительностью 90 миллисекунд и отправки JSON-события по протоколу WebSocket. Этот клик передает ту строку действия, которую заранее определил агент. Например, если на карточке статьи срабатывает нажатие, система может открыть материал и рассказать о его содержимом. Длительное нажатие возвращает интерфейс к простому циферблату часов. Очереди запросов или механизмы повторной отправки пока не предусмотрены, поэтому утерянные данные не восстанавливаются. На данном этапе система работает по принципу прямого запроса и ответа, хотя агент уже предлагал настроить утреннее обновление панели со свежими заголовками через cron.
В перспективе именно такой автоматический режим видится главной целью, поскольку устройство перестает требовать постоянного осознанного контроля и превращается в самостоятельный источник полезной информации на столе. Хранить на нем конфиденциальные данные не стоит из-за отсутствия аутентификации на стоящем прямо на столе гаджете. Тем не менее, такой подход наделяет доступный тридцатидолларовый модуль интерфейсами, которые не нужно проектировать заранее, что выгодно отличает его от большинства стандартных умных дисплеев. Желающие повторить эксперимент могут найти репозиторий на платформе GitHub.
Источник: xda-developers.com

