Каждый раз, когда я вывожу текст на TFT-дисплей через TFT_eSPI, приходится смотреть на одни и те же встроенные шрифты: жирные, зубчатые и будто прямиком из 1980-х. И это особенно обидно, потому что библиотека уже много лет умеет отрисовывать красивые сглаженные шрифты (smooth fonts). Проблема никогда не была в самой отрисовке — она была в инструментах. Официальный способ создать гладкий шрифт предполагает установку Processing IDE, редактирование Java-скетча и повторный запуск для каждого шрифта и каждого размера. В итоге в большинстве проектов с этим просто не связываются.

Поэтому я сделал инструмент, который решает эту проблему.

fonts.atomic14.com — бесплатный браузерный инструмент, который преобразует любой из более чем 1900 шрифтов Google Fonts или ваш собственный TTF-файл в шрифты, готовые для использования с TFT_eSPI, M5Stack/M5GFX или LVGL. Там же можно увидеть попиксельно точный предпросмотр того, как шрифт будет выглядеть на вашем дисплее. Всё работает прямо в браузере: файлы шрифтов никуда не загружаются.

Дальше разберём, что на самом деле находится внутри этих файлов шрифтов и что стоит знать перед их использованием.

Что на самом деле представляет собой «гладкий шрифт»

Гладкие шрифты в TFT_eSPI — это файлы VLW, формат которых достался библиотеке от проекта Processing. VLW устроен до неприличия просто: небольшой заголовок с количеством глифов, размером, верхним и нижним выносами, таблица метрик каждого глифа, а затем необработанные 8-битные альфа-маски — по одному байту на пиксель. Никаких векторных контуров, хинтинга или кернинга. Шрифт заранее растеризуется на компьютере, а микроконтроллеру остаётся лишь копировать готовые пиксели на экран.

«Сглаживание» обеспечивает именно альфа-канал. В каждом пикселе хранится степень покрытия глифом — от 0 до 255. Во время отрисовки библиотека смешивает цвет текста с цветом фона в цветовом пространстве RGB565:

pixel = (fg × alpha + bg × (255 − alpha)) / 256

Именно поэтому для сглаженных шрифтов в tft.setTextColor(TFT_WHITE, TFT_BLACK) нужно указывать оба цвета: прозрачности здесь нет, есть только смешивание с заранее известным фоном. Если вывести текст поверх фотографии, по краям букв появятся цветные ореолы. Это не ошибка шрифта, а особенность формата.

PROGMEM или LittleFS?

Загрузить VLW на микроконтроллер можно двумя способами, и инструмент поддерживает оба:

Заголовочный файл .h, в котором шрифт хранится как массив байтов в PROGMEM. Это самый простой вариант: подключаете файл через #include, вызываете tft.loadFont(MyFont28); — и готово. Шрифт хранится во флеш-памяти вместе с кодом.

Файл .vlw в LittleFS или на SD-карте, который загружается вызовом tft.loadFont("MyFont28", LittleFS);. Этот вариант удобнее, если у вас несколько шрифтов или вы хотите менять их без перекомпиляции, но придётся повозиться с загрузкой файловой системы.

В любом случае не забудьте добавить #define SMOOTH_FONT в User_Setup.h. Его отсутствие — самая частая причина ошибок компиляции в духе «почему loadFont не существует».

Как уменьшить размер файлов

На один глиф приходится 28 байт метаданных плюс ширина × высота байт растрового изображения. При размере 28 пикселей полный набор Basic Latin занимает примерно 25–30 Кбайт, а шрифт только с цифрами для вывода показаний датчика может весить меньше 2 Кбайт. Каждый исключённый символ — это сэкономленная флеш-память. Инструмент в реальном времени показывает итоговый размер при изменении набора символов, а ненужные символы можно убирать из таблицы по одному. Если вам нужны только знак °C и десять цифр, оставьте только знак °C и десять цифр.

Особенности, которые лучше увидеть в предпросмотре

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

Текст переносится у правого края. Если глиф не помещается, при отрисовке сглаженного шрифта библиотека переходит на следующую строку и начинает с x=0. По границе текст не обрезается.

Для пробелов используется не реальная ширина пробела из шрифта. Вместо неё библиотека вычисляет значение по формуле (ascent + descent) × 2/7 пикселя. Поэтому межсловный интервал на устройстве никогда полностью не совпадёт с тем, как тот же шрифт выглядит на компьютере. Зато предпросмотр совпадает с устройством — а это здесь главное.

Если какой-то символ на дисплее отображается пустым, почти всегда причина в том, что он не вошёл в выбранное подмножество. Классический пример — знак градуса: его нет в Basic Latin, поэтому показания температуры незаметно превращаются в 23.5C. Достаточно добавить его в поле дополнительных символов. Предпросмотр покажет пропуск ещё до того, как вы увидите его на дисплее.

Попробуйте без реального железа

Любой созданный шрифт можно проверить на эмулируемых ESP32 и дисплее ILI9341 в Wokwi. Одним кликом инструмент копирует готовый тестовый скетч со встроенным шрифтом и открывает заранее настроенный проект. Вставляете код, нажимаете Play — и видите, как шрифт отрисовывается настоящей библиотекой TFT_eSPI на эмулируемом дисплее.

Экспортированный шрифт Orbitron на эмулируемых ESP32 и ILI9341 в Wokwi
Экспортированный шрифт Orbitron на эмулируемых ESP32 и ILI9341 в Wokwi

В архив также входит готовый к прошивке тестовый проект для Arduino, если вы предпочитаете проверить шрифт на реальном устройстве.

И LVGL тоже

Тот же конвейер формирования подмножества шрифта и растеризации умеет экспортировать шрифты для LVGL: массивы на C, совместимые с результатом lv_font_conv, с глубиной 1, 2, 4 или 8 бит на пиксель. Если ваш проект использует LVGL или SquareLine Studio, скачивайте этот файл.

Что происходит под капотом

Для тех, кому интересны подробности: генератор VLW побайтово совместим с PFont.save из Processing — это проверено по эталонным файлам. Результат работы средства отрисовки сверяется с TFT_eSPI, запущенной в эмуляторе, а генератор LVGL откалиброван по реальному выводу lv_font_conv.

Всё работает на стороне клиента: opentype.js разбирает файл шрифта, Web Worker растеризует глифы в альфа-маски, а модули на TypeScript формируют выходные байты.

А если вы ещё выбираете дисплей для своего проекта, в базе плат с дисплеями для ESP32 собраны все популярные модели в одной таблице с фильтрами. Буду рад узнать, что у вас получилось.

Работа со шрифтами и дисплеями быстро выводит проект за пределы одной графической библиотеки: приходится разбираться с периферией ESP32, интерфейсами обмена и особенностями оцифровки сигналов. Продолжить погружение можно на бесплатных уроках (все стартуют в 20:00):

  • 27 июля: «Разработка Embedded-устройств для IoT». Записаться

  • 12 августа: «Тайный язык общения чипов. Подключаем всё к ESP32». Записаться

  • 24 августа: «АЦП в ESP32. Оцифровать сигнал, а не „погоду на Марсе“: нюансы, особенности, повышение точности». Записаться

Пройдите вступительное тестирование по курсу «Embedded-разработчик», чтобы оценить свои знания и навыки.

Комментарии (4)


  1. n0isy
    20.07.2026 16:09

    Я настрадался с преобразованием шрифтов в формат термо принтера и дело не в коде конвертера. Оказывается все либы по разному рендерят шрифт. К примеру у виндового GDI+ свои особенности. Но задача была чуть сложнее вашей, так как монохром.


  1. kpmy
    20.07.2026 16:09

    Микропитонистам на заметку

    https://github.com/peterhinch/micropython-font-to-py


  1. NutsUnderline
    20.07.2026 16:09

    вы только не удумайте там прямо онлайн шрифты подгружать (при включении устройства) А то у нас тут типа wifi, интернеты - что может пойти не так :) :)


  1. OSabuser
    20.07.2026 16:09

    Есть ещё одна годная утилита, весьма известная в узких кругах:

    LcdImageConverter