ESPN DeportesCucurella explica intento del Real Madrid por fichar a RodriThe Jerusalem PostCourt adds two years to sentence of man who stole cars from Kibbutz Be'eri after Oct. 7ESPNGame Day Kickoff: Knee-jerk reactions and September lies; what does Week 4 have in store?InquirerBill fixing term of barangay, SK officials for Marcos’ signature – GatchalianBollywood HungamaSalman Khan and Arvind Swamy shoot massive train action sequence for Monster; source reveals details of Mumbai scheduleDaily MaverickTHE TIES THAT BIND US: Ramaphosa uses Heritage Day address to condemn gender-based violence after Ekurhuleni killings한겨레이 대통령 “북한 포로 송환 비공개 합의 어긴 우크라에 아쉬워”Billboard‘Ozzy & Black Sabbath: Back to the Beginning’ Trailer Captures Emotional Punch of Metal Icon’s Final Show: WatchThe Hollywood ReporterBob Iger Reveals That He Wrote a Six-Page Letter to Josh D’Amaro and Left It In His DeskABC NewsFDA commissioner nominee Dr. Heidi Overton appears for confirmation hearingVanguardNigeria, US sign pact to boost $700bn mining investmentInteriaSpotkanie Trump-Xi, emocje w Białym Domu. Doszło do incydentu
The Daily Newsstand · Free, Always
Thursday, September 24, 2026

Монитор на e-ink для сервера за выходные

Translate

Если совсем коротко: плата Waveshare ESP32-S3-ePaper-1.54, один кабель USB и экран 200×200, который висит рядом с сервером и показывает загрузку процессора, температуру видеокарт, память, диск, аптайм, живой пинг и т.д по выбору админа.

Зачем серверу отдельный экран

Сервер с двумя Tesla P100 живёт у меня на кухне, про него была прошлая статья (https://habr.com/ru/articles/1071442/). Он греется и гудит, а знать от него нужно три вещи: не ушла ли температура видеокарты к порогу троттлинга, сколько занято памяти и не кончилось ли место на ссд.

Варианты я перебирал честно. Старый монитор на 15 дюймов отпадает по месту и по нежеланию лишнего потребления энергии. Веб-страница со сводкой выглядит логично, но чтобы её увидеть, надо разблокировать телефон, найти вкладку и дождаться загрузки. Мне нужно, чтобы цифры были видны сразу, когда я прохожу мимо. Электронные чернила подходят ровно этим: не светят, в покое не потребляют энергию, держат картинку без питания.

Теперь я вижу когда локальная нейросетка заканчивает работу

Теперь я вижу когда локальная нейросетка заканчивает работу

Экран висит на USB, раз в минуту перерисовывается и показывает загрузку, температуру и аптайм. А теперь подробности создания данного устройства.

Закупка

Типичный лот на маркетплейсах ESP32-S3 плата разработки e-Paper 1.54"

Типичный лот на маркетплейсах ESP32-S3 плата разработки e-Paper 1.54"

У меня после покупки в коробке лежала пластиковый бокс с ноунейм ESP32-S3-ePaper-1.54 V2, кабель отсутствовал. Экран у неё 200×200 на контроллере SSD1681, внутри модуль ESP32-S3-PICO с восемью мегабайтами флеша и столькими же PSRAM. Ещё на плате есть датчик температуры и влажности SHTC3, часы реального времени, слот для карточки TF, аудиокодек с усилителем и динамик, две кнопки и разъём Type-C. Батарейку внутрь не положили, да и не нужна она, если постоянно будет висеть на юсб.

Так присылают

Так присылают

Для монитора из этого набора нужно чуть больше трети функционала от устройства. Динамик молчит, карточку я не вставлял ни разу.

Написание прошивки

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

С тех пор прибор получил имя inkmetrics и уже доехал до версии 0.7.1 . Ниже цифры, полный список того, что он умеет сегодня, а в конце ссылки на исходники и готовый комплект для заливки.

В первой редакции статьи кадр 200×200 рисовал компьютер: Pillow, шрифт Consolas, 5000 байт по COM-порту, плата только принимала готовый буфер. Сейчас COM-порта в сборке нет вообще. Прибор отдаётся хосту двумя устройствами, сетевой картой и диском, а экран рисует сам, своими шрифтами. Компьютер раз в минуту отправляет только цифры: плоский JSON на POST /ingest по адресу 192.168.7.1. Все поля необязательны, чего нет, то прибор покажет прочерком.

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

Что на экране

Откуда берётся

Загрузка CPU, память, диск, аптайм

штатные счётчики Windows, агент раз в минуту

Состояние диска

SMART, строка OK / WARNING

Аптайм

штатные счётчики Windows

Две видеокарты: загрузка, температура, память

nvidia-smi, а без него счётчики WDDM

Есть ли у компьютера интернет, задержка, сколько из четырёх пингов ответило

агент: четыре ICMP

Температура и влажность в комнате, часы

датчик SHTC3 на плате, время отдаёт браузер при открытии страницы

Актуальность данных

прибор: молчание дольше 90 секунд стирает числа в прочерки

Делаем удобную связь

На Arduino-ядре USB-сеть невозможна: TinyUSB там собран без сетевого класса. Мне же хотелось, чтобы прибор сам отдавал страницу, и для этого я переехал на ESP-IDF.

Первый заход упёрся в описатели. Windows отказалась от режима NCM, я переключился на RNDIS, а библиотека esp_tinyusb подставляет описатели по умолчанию только для CDC, MSC, MTP и NCM. Для RNDIS конфигурацию обязан задать пользователь, и наружу это видно единственной строкой ESP_ERR_TIMEOUT. Полдня я искал причину в PHY и в тактировании, пока регистры не сказали прямым текстом: до подъёма PHY идентификатор ядра DWC2 читается как 0x00000000, после подъёма он 0x4f54400a. То есть железо живое, и виноват не PHY.

Свои описатели подняли сеть за минуту, и тут прибор начал перезагружаться каждые двадцать секунд. Оказалось, я сам его и ронял. esp_netif отдаёт драйверу линейный кадр, обычный буфер с байтами, а я в своём коде приводил его к указателю на pbuf, как это принято в примерах TinyUSB. Длина читалась не оттуда, а из байтов MAC-адреса, и первый же исходящий кадр, ответ DHCP на запрос Windows, уносил плату в панику с причиной LoadProhibited.

Хорошо, что дамп паники к тому моменту уже расшифровывался: по стеку видна вся дорога, от udp_sendto_if через etharp_output и esp_netif_transmit до моего usb_transmit и tud_network_xmit_cb. Правка заняла одну строку. Плата вместо двадцати секунд прожила сто тридцать четыре и останавливаться не собиралась.

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

Адрес хоста выдается прибором жестко

Адрес хоста выдается прибором жестко

Дискета, которая оказалась просто маленькой

Прибор прикидывается флешкой, и с размером этой флешки мы с агентом намучились. Сначала на ней было 2880 секторов, то есть ровно 1,44 МБ. Размер совпал с геометрией дискеты, а Windows для устройства прямого доступа выбирает между классами Disk и SFloppy именно по размеру. Она выбрала драйвер гибких дисков: устройство получило букву A: и подпись «Дискета».

Я успел обвинить в этом бит сменного носителя в ответе INQUIRY и даже написал свой ответ с битом снятым. Не помогло. Таблица разделов внутри образа тоже оказалась ни при чём, она там была с самого начала. Проверено дважды, оба варианта отброшены. Лечится размером: 7552 сектора (3,69 МБ), MBR, FAT16. Windows завела обычный E: и метку INKMETRICS.

Прибор виден как флешка

Прибор виден как флешка

Что прибор умеет сейчас

Экран и кнопки

Экранов два, короткое нажатие PWR переключает их. Сводный собран так: сверху строка про интернет компьютера, ниже два крупных числа, под ними проценты CPU, RAM и DISK, аптайм и датчик. Экран SETUP показывает адрес веб-кабинета, что стоит в крупных числах, поворот, режим диска, узел проверки интернета и версию прошивки.

Строка состояния живёт отдельно, потому что она про хост, а не про прибор. Варианты у неё такие.

Что видно

Когда

PING - 13MS - 4/4

интернет у компьютера есть, 13 мс это среднее

OFFLINE - 0/4

не ответил ни один, интернета нет

NO DATA

агент молчит дольше 90 секунд, числа стёрты

PING - 13MS

агент старой версии, счёт пингов не сообщает

Два крупных числа выбираются на странице настроек двумя списками: загрузка процессора, память, диск, загрузка и температура каждой карты, или пусто. Выбор лежит в NVS и переживает перепрошивку. Есть правило кегля, которое я вывел по замеру, а не на глаз: оба числа не длиннее двух знаков печатаются крупным шрифтом, а если хоть одно трёхзначное, оба уходят в средний. Крупным «100%» занимает 124 пикселя, два таких в 200 не влезают.

Кнопок две, у каждой по два жеста. PWR коротко переключает экран, удержание заставляет перерисовать кадр. На экране SETUP короткое нажатие BOOT поворачивает панель на 90°, а удержание блокирует запись на диск прибора.

Веб-кабинет

Ничего устанавливать не надо: страницы живут на самом приборе по адресу 192.168.7.1.

Адрес

Что делает

/

сводка: то же, что на экране, плюс возраст последнего замера

/setup

настройки: поворот, два крупных числа, узел пинга, блокировка диска

GET /api/state

весь JSON состояния, включая объект ingest с метриками хоста

GET /api/boot

уйти в режим загрузчика: перепрошивка без кнопки BOOT

Первая страница вебсервера

Первая страница вебсервера

Экран настроек

Экран настроек

Установка на компьютер

После втыкания прибора по юсб прибор определяется как флешка и как сетевая карта. На диске прибора лежат четыре файла: установщик, деинсталлятор и две инструкции, русская и английская. Установщик это один файл .cmd с кодом агента внутри. Запускается от администратора, кладёт агента в C:\ProgramData\inkmetrics и заводит задачу планировщика «inkmetrics agent» от SYSTEM с запуском раз в минуту. Лог будет в этой же папке: agent.log, строка вида sent ok (cpu=8.4%, ping YA.RU 16ms 4/4). Ни Python, ни драйверов, ни служб. Удаляется так же, одним файлом.

Содержимое диска прибора

Содержимое диска прибора

Про загрузку видеокарт расскажу отдельно, потому что здесь пришлось поспорить с Windows. На этом компьютере стоит nvidia-smi, и первым делом агент звал его. Но прибор обещал показывать карты и на машинах, где NVIDIA нет, а счётчики производительности в этой Windows сломаны: Win32_PerfFormattedData_* отвечает «Неверный класс», Get-Counter не находит счётчик, а Win32_Processor.LoadPercentage пустой. Поэтому загрузка процессора считается по приросту времени простоя, а для чужих карт пришлось завести второй путь, счётчики WDDM, те же, что показывает диспетчер задач. Адаптеры различаются по LUID, порядок карт по объёму видеопамяти, чтобы нулевой была дискретная. Этим путём получаются только проценты: температура и память чужой карты остаются прочерком, а не красивым нулём.

Заливка прошивки на девборд

Способов три, и все на один кабель.

Способ

Что нужно

Команда

Комплект из релиза

Python и esptool

flash-kit\idf\flash.bat COM5

Flash Download Tool от Espressif

ничего, кроме самой утилиты

inkmetrics-fw-0x0.bin с адреса 0x0, setup-disk-big.img с 0x430000

Прошивальщик в браузере

Chrome

страница на GitHub Pages, ниже ссылка

flash.bat пишет загрузчик, таблицу разделов, прошивку и диск, а в конце сам снимает флаг загрузчика, чтобы прибор запустился без передёргивания USB. Про браузерный вариант скажу честно: страница открывается, сверяет суммы, находит прибор и открывает порт, но завершить запись на нашей плате не удалось, и «работает» я не напишу, пока не проверил. Причина не в странице: загрузчик ESP32-S3 здесь это USB-Serial/JTAG, и дёргание линий DTR/RTS укладывает его порт до физического переподключения кабеля. Воспроизводится обычным esptool из командной строки. Поэтому комплект и переведён на режим без сброса линиями.

При разработке прибор у меня почти не сходил с кабеля. Он висел на USB у рабочего компьютера, уход в загрузчик делался запросом GET /api/boot, заливка шла скриптом, который сам находит порт, пишет прошивку с диском и снимает флаг загрузчика. Кнопку BOOT я нажимал в лучшем случае раз 5 за два дня, счётчик загрузок в «чёрном ящике» перевалил за тысячу ещё в первые сутки, а кабель дёргал только когда порт USB умирал после очередного опыта с режимами загрузчика. Основная часть прошивки так и писалась: прибор висит на USB, агент сам его прошивает и перезагружает, а я посматриваю на экран и говорю, что поправить. Прибор сам отдаёт страницу состояния и сам умеет уходить в загрузчик, этого хватает, чтобы вся петля крутилась без рук.

Прошивальщик на гитхабе в репозитории

Прошивальщик на гитхабе в репозитории

Чего ещё нет

Частичного обновления, чтобы убрать мигание на перерисовке. Обновления прошивки по воздуху, хотя место под второй слот в разделе есть: приложение занимает 642 КБ из 3,2 МБ, а рядом лежит мегабайт под копию. Точки доступа Wi-Fi, чтобы смотреть показания с телефона. Корпуса: плата лежит на подставке как есть. Истории и графиков, поиска аварий и оповещений в телеграм. Про звук отдельная история: кодек с динамиком на плате есть, а сказать ему пока нечего, еще не придумал, что говорить, но есть крамольная мысль записать туда матные выражения на потерю пинга.

Где лежат исходники

https://github.com/isemaster/inkmetrics

Всё публичное, берите и правьте. Ни одну среду разработки теперь рекомендовать не буду, просто говорите своему агенту — репо тут: https://github.com/isemaster/inkmetrics скачай поправь что надо, залей на прибор.

Прошивка и образы версионируются прямо в комплекте, поэтому заливка на чистом компьютере не требует ни ESP-IDF, ни сборки из исходников. Нужен только Python с esptool, а можно и совсем без него, прямо через браузер со страницы репозитория (Прошивальщик в браузере: https://isemaster.github.io/inkmetrics/flash-web/ ) .

Кто это всё писал

Код писал я и агент. Точнее: его писал агент Hermes, а я ставил задачи, ругался на результат и проверял на железе. Прибор при этом висел на USB у рабочего компьютера, прошивку и перезагрузки агент делал сам, дистанционно, а моя роль сводилась к тому, чтобы посматривать на экран прибора и направлять: что не так, где поправить, куда двигаться дальше.

По работе над прошивкой я насчитал 18 часов времени. На разработку прибора ушло двенадцать сессий, 75 подходов, 4767 сообщений, 2407 вызовов инструментов.

Считал так. Подход начинается с моего сообщения и длится, пока идёт переписка. Если я молчал дольше десяти минут, подход заканчивался на том ответе агента, который шёл за моим последним сообщением, а тишина в рабочее время не входила. Обеды, дела и сон отсеклись сами. Средний подход вышел 14,7 минуты, самый длинный 93 минуты.

Дальше про деньги. Облачный DeepSeek принял за проект 5,3 миллиона входных токенов, отдал 4,0 миллиона выходных и ещё 2,2 миллиона на размышления, а из кэша перечитал 782 миллиона. Последнее это не работа, а повторная отправка контекста, которая стоит в десять раз дешевле. Считая по нынешним тарифам, облачная часть проекта стоит около 8 долларов. В базе агента записано 5,9, но она считает по старым ценам, и верить ей нельзя.

Локальный Qwen3.8-27B на видеокарте прочитал 2,1 миллиона входных токенов, а написал всего 168 тысяч: он больше читал, чем сочинял. Стек паники, исходники IDF, мои логи. Платить за это не пришлось ни разу, только электричество.

Что дальше

Хочу частичное обновление, чтобы панель не мигала на перерисовке. Обновление по воздуху меня не торопит, прибор и так живёт на кабеле, но место под него в разделе уже выделено. Возможно, захочется завести прибор по Wi-Fi, чтобы видеть состояние сервера по сети дистанционно.

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.