Что делает принтер принтером?

Нужно было открыть что-нибудь на MacBook, нажать Print и выбрать Xteink. Это означало разобраться, что именно компьютер ожидает найти на другом конце.

Пришлось изучать Internet Printing Protocol, или IPP. Этот протокол позволяет компьютеру узнать о возможностях принтера, отправить документ и проверить статус задания. Сообщения передаются по HTTP. Операции типа Get-Printer-Attributes запрашивают характеристики; Print-Job отправляет работу на печать.

Объявлены: монохромный вывод, 300 dpi, одна копия, односторонняя печать. Для форматов документов выбраны Apple raster и PWG raster. Это значит, что Mac должен был превратить документ в пиксели перед отправкой. penguin (так назвал устройство разработчик) сжимал результат под размер экрана.

Объявлены форматы бумаги A5 и Letter, тип медиа stationery и выходной лоток с названием face-up.

Выбрано имя penguin — оно соответствовало цветовой схеме устройства. Использовался Bonjour для объявления сервиса _ipp._tcp с этим именем. Объявление включало поддерживаемые форматы и адрес, на который отправлять задания печати.

Для бездрайверного обнаружения на macOS потребовалось добавить подтип _universal. Это потребовало прямого вызова mDNS API библиотеки ESP-IDF, так как обёртка Arduino не предоставляла доступ к этой функции.

Отправить страницу с компьютера — это только половина работы. Нужно было ещё получить её на устройстве.

Где хранить страницу?

Страница размером Letter при 300 dpi — это 2550 × 3300 пиксели. При одном байте на пиксель в градациях серого получается примерно 8,4 МБ без сжатия.

X3 имеет 400 КБ оперативной памяти, 16 КБ из которых зарезервированы для кеша. Нужно было запустить Wi-Fi, запустить сервер принтера и как-то получить целую страницу.

С Wi-Fi и буфером для страницы оставалось 6,8 КБ свободной памяти на куче.

Вспомнилась функция mmap в Linux. Можно ли сделать что-то похожее с SD-картой и притворяться, что памяти больше? Поддержка отображения памяти на C3 работала только с flash, не с файлами на SD-карте.

Но погодите. А что если использовать сам дисплей как хранилище?

Что если пропустить входящую страницу через конвейер преобразований и сразу записать результат на дисплей? Декодировать пиксели, сжать под размер, преобразовать в чёрное и белое. Как только строка готова, поместить её на место на дисплее и переиспользовать рабочую память. Продолжать, пока не будет готова вся страница.

На дисплее уже была зарезервирована память для изображения экрана. Можно было собирать страницу прямо там, при получении данных. До этого собиралось второе полное изображение только для копирования.

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

Готовая страница сохранялась в BMP на SD-карту с помощью встроенного инструмента скриншотов.

Буфер изображения в памяти До После
Из 400 КБ микроконтроллера ~113 КБ ~62 КБ

Это освободило место для буферов сокетов сетевого стека.

Пингвин появился в Preview

На MacBook была манга-страница из Mushoku Tensei. Открыл её в Preview и выбрал печать.

В списке принтеров появился penguin.

Отправил на печать. Страница манги выглядела отлично на Xteink. Появилась примерно за секунду. И по-прежнему там.

Страница манги, напечатанная на Xteink X3, лежит на закрытом MacBook

На первую печать ушёл вечер.

Сервер принтера запускается на самом читающем устройстве. Можно присоединить его к Wi-Fi сети или запустить собой точку доступа literate-penguin.

Пингвин теперь может печатать.

Код находится в ветке CrossPoint, включая реализацию принтера.

Сохранённые отпечатки остаются на SD-карте, и можно просматривать их на устройстве. У принтера есть выходной лоток. Это папка.