К ленте · Россия

РоссияСтатьяМодели

Я сделал экран для мониторинга моих квот LLM провайдеров

Начну с того, что еще недавно мои познания в схемотехнике балансировали на уровне непонимания различия между транзистором и резистором. Из всего арсенала, хоть как-то применимого к embedded, за душой был только Rust. Но когда на радаре замаячил так называемый встриваемый AI (машинное обучение и компьютерное зрение прямо на копеечных микроконтроллерах), - спортивный интерес взял верх. Не тратя недели на теорию, я просто доехал до радиорынка и вернулся с джентльменским набором первопроходца: ESP32, первым попавшимся экраном и энкодером. Амбиции по запуску нейросетей моментально разбились о суровую реальность первого вечера: как вообще заставить этот кусок текстолита ожить и вывести на дисплей хотя бы пару цифр?

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

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

Что получилось

Система состоит из трёх частей:

  • Windows-приложение получает квоты Claude, Codex, Grok и Kimi и отдаёт их в виде одного JSON.
  • ESP32 раз в 30 секунд забирает этот JSON по Wi-Fi и рисует его на экране. Энкодер переключает подписки и страницы.
  • Телефон открывает тот же адрес и видит адаптивную страницу.

Windows-приложение, прошивка ESP32 и общая структура данных между ними написаны на Rust (страница dashboard - обычные HTML и JS). Для меня это было главным аргументом начать: единственное, что я умел, должно было заработать с обеих сторон.

Железо: чему пришлось научиться

Список деталей короткий:

  • плата на ESP32 с USB-мостом CH340, в Windows она появилась как COM-порт;
  • цветной экран 1.8" SPI, 128×160;
  • энкодер KY-040;
  • светодиод и резисторы.

Первая неожиданность возникла с экраном. Исходный план рассчитывал на распространенный контроллер ST7789, но на полученном модуле красовалась маркировка 1.8 TFT SPI 128×160 V1.1, по которой точную модель чипа определить невозможно. Методом проб и ошибок я выбрал драйвер ST7735S - это рабочее предположение, подтвержденное практикой. Шелкография на плате тоже преподнесла сюрприз: вместо привычных для SPI обозначений DC и MOSI там были A0 и SDA, так что распиновку пришлось сопоставлять с даташитами вручную.

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

  • подсветке экрана нужен токоограничивающий резистор номиналом порядка 220 Ом;
  • индикаторному светодиоду связи нужен свой резистор;
  • сигнальные входы нельзя подключать к 5 В, к счастью у ESP32 логика 3,3 В.Экран TFTESP32НазначениеVCC, GND3V3, GNDПитание модуляCSGPIO27Chip SelectRESETGPIO25Сброс дисплеяA0 (DC)GPIO26Data / CommandSDA (MOSI)GPIO23Линия данных SPISCKGPIO18Тактовая частота SPI

Энкодер подключён к GPIO2 (CLK), GPIO4 (DT) и GPIO16 (SW) с внутренними подтяжками. Отдельный светодиод на GPIO22 горит, когда нет связи с приложением на ПК.

Rust на ESP32: что выяснилось по пути

Прошивка крутится на esp-idf-svc и esp-idf-hal, то есть с полноценной поддержкой стандартной библиотеки std. За графику отвечают крейты embedded-graphics и драйвер mipidsi. Звучит прямолинейно, но сборка Rust под Xtensa на Windows с первого раза превращается в увлекательный квест:

  • Тулчейн. Для архитектуры Xtensa требуется отдельный форк компилятора Rust, который накатывается через утилиту espup. Дополнительно в системе должны стоять CMake, Ninja и Microsoft C++ Build Tools.
  • Генератор проекта. Свежий cargo-generate отказался собираться моим системным компилятором — пришлось явно фиксировать версию постарше.
  • Длинные пути. Сборка ESP-IDF в глубинах моих каталогов внезапно упала с фатальной ошибкой Too long output directory и вежливой рекомендацией уложиться в десять символов. Инженерное решение выглядит забавно, но безотказно: корень сборки переехал в C:\t.
  • Порт занят. Access is denied при прошивке означало не режим BOOT, а то, что старый монитор порта ещё открыт в другом окне терминала =).
  • BOOT. Периодический Failed to connect to ESP32 лечится классическим зажатием аппаратной кнопки BOOT на плате в момент инициализации соединения.

Когда пыль улеглась, плата успешно подняла Wi-Fi стек и приняла по сети первый JSON.

Зачем отдельное приложение на Windows

Соблазн стучаться в API провайдеров языковых моделей напрямую с ESP32 был велик, но от этой идеи я отказался почти сразу. Сложные цепочки OAuth2, постоянное TLS-рукопожатие, ротация refresh-токенов и парсинг четырех разнородных JSON-схем быстро сожрут скудные ресурсы микроконтроллера.

Поэтому токены живут только на Windows и шифруются пользовательским DPAPI. Микроконтроллер получает отфильтрованный открытый payload, где нет ничего, кроме цифр:

{
 "updated_at": "2026-10-29T12:17:16Z",
 "age_seconds": 8,
 "stale_after_seconds": 900,
 "providers": {
 "kimi": {
 "status": "ok",
 "source": "Kimi Code OAuth usage",
 "windows": [
 { "name": "5h", "remaining_percent": 0, "resets_at": "2026-10-29T10:30:54Z" }
 ]
 }
 }
}

Откуда берутся квоты

У каждого сервиса своя схема, и все они разные:

  • Claude отдаёт utilization, то есть использованную долю, поэтому остаток приходится вычислять инверсией. Учитываются скользящие окна на 5 часов и 7 дней, а также кастомные лимиты моделей.
  • Codex отдает структурированные окна с точной длительностью и таймстемпом сброса. Помимо прямого входа, я предусмотрел режим работы через локальный read-only балансировщик codex-lb.
  • Grok держит метрики внутри биллинга: недельный процент потребления либо месячную квоту при общем плане.
  • Kimi отдаёт окно на пять часов и месячные лимиты. Недельного окна у новых планов, как следует из документации, больше нет, поэтому экран показывает то, что реально пришло.

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

  • Неизвестное не равно нулю. Если провайдер процент не сообщил, показывается --, а не 0%.
  • Время сброса не выдумывается. Если его нет в ответе, на экране RST unknown.
  • Устаревшее помечается. Если время сброса уже прошло или источник давно не отвечал, статус становится STALE, а цифры не подаются как свежие.
  • Одно исключение, которое я ввёл сам. Если исчерпан глобальный недельный пул (7d = 0), остаток 5-часового окна принудительно зануляется (0%), поскольку фактически отправка запросов заблокирована до общего сброса недели.

Вход без паролей

Кнопки входа открывают системный браузер. Для Claude и Codex это авторизация с PKCE и возвратом на локальный порт, для Grok и Kimi device flow с кодом на странице. Пароль в окно я не ввожу никогда. Токены уходят в DPAPI, а новый refresh-токен записывается атомарно до следующего запроса: потерять его при ротации значит потерять вход.

Здесь была и честная неприятность. Claude уже отвечал мне 429, и приложение показывает это как ошибку, но сохраняет последние известные цифры помеченными как устаревшие. Чтобы это было видно сразу, строка провайдера с ошибкой запроса подсвечивается красным:

Экран и энкодер

На экране 160×128 помещаются четыре строки. Обзор строится только из тех провайдеров, которые реально пришли в ответе, без заготовленных пустых мест. Подключу пятого, он появится сам. Для каждого видны остатки первых двух окон, статус и время их сброса по UTC.

Нажатие энкодера открывает детали провайдера, вращение листает страницы окон и провайдеров:

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

const DELTAS: [i8; 16] = [0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0];

Кнопка декодера получила программный дебаунс на 20 мс, а сам опрос уехал в изолированный поток с интервалом в 1 мс чтобы не ронять системный стек Wi-Fi. После комфортного мира веб-разработки это отдельная ломка парадигмы: быстро выясняется, что ядра здесь - не абстрактные облачные vCPU, а вполне осязаемый кремний, где каждая задача должна тикать строго в своем ритме и на своем ядре, иначе плата мгновенно напомнит о себе паникой или таймером.

Dashboard в браузере

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

На широком экране карточки выстраиваются в ряд:

Сколько это весит

Мне хотелось, чтобы мое приложение не жрало системные ресурсы и просто весело в трее, поэтому десктопная часть написана на чистом Win32 API - без монструозных Electron, CEF или системных WebView, новомодных Tauri и бла-бла-бла. Замеры release-сборки с тремя активными подписками (Claude, Codex, Grok; до добавления Kimi):

  • память: около 17,9 МиБ working set и 2,8 МиБ private bytes;
  • CPU: за минуту наблюдения прирост составил 0 мс;
  • размер exe-шника: около 3,7 МиБ.

Естественно, нулевое потребление процессора фиксируется в покое - во время выполнения сетевых TLS-запросов и шифрования всплески активности есть, но они мгновенны и не заметны.

Для полноты картины приложение обзавелось собственной лаконичной иконкой в трее:

Что пошло не так

Остальные ограничения, о которых стоит помнить:

  • Эндпоинты неофициальные. Квоты читаются теми же адресами, что и агентные CLI. Они могут измениться или закрыться.
  • Правила сервисов. Сессии подписок переиспользуются сторонней утилитой. В документации того же Claude Code прямо указано, что OAuth-авторизация предназначена для их нативного софта, а интеграторам рекомендуется работать через классические API-ключи.
  • HTTP в локальной сети без шифрования. ESP32 забирает данные по открытому HTTP-протоколу. В пакетах передаются только проценты и даты без токенов и персональных данных, но выставлять такой сервис за пределы NAT не следует.

Что дальше

  • Докупить экран побольше!
  • Спроектировать и напечатать на 3D-принтере компактный корпус для рабочего стола.

Полный исходный код проекта, схемы соединений и пошаговое руководство по сборке опубликованы в репозитории: Тык.

Источник: Хабр · статьиПубликуется с разрешения правообладателя, подтверждённого редакцией.Оригинал: Я сделал экран для мониторинга моих квот LLM провайдеров ↗

Есть кому отправить?

Скопируется постоянная ссылка на этот материал.

Комментарии

0

Обсудите материал с другими читателями.

Загружаем комментарии…

Ваш комментарий

Комментарии и оценки — через Яндекс ID