Превращение читалки в сетевой принтер
- title
- Превращение читалки в сетевой принтер
- type
- summary
- summary
- Нишант Джоши реализовал IPP на читалке Xteink X3: она видна в диалоге печати macOS и умещает страницу 300 dpi в 400 КБ RAM
- tags
- hardware, embedded, e-paper, networking
- sources
- how-to-build-a-printer
- created
- 2026-09-13
- updated
- 2026-09-13
- lang
- ru
- translation_of
- e-reader-ipp-printer
- source_updated
- 2026-09-13
- translated
- 2026-09-14
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Нишант Джоши купил читалку Xteink X3, потому что в описании мелким шрифтом было указано, что она полностью программируема, и начал дорабатывать открытую прошивку CrossPoint (см. freeink): добавил новую анимацию загрузки, игральные кости, выпадающие при встряхивании устройства, и QR-код со ссылкой на профиль в LinkedIn для нетворкинга. Чтобы закинуть на неё файлы, приходилось подключаться к точке доступа читалки и использовать простую веб-страницу загрузки в браузере. Его решение: если устройство выглядит как бумага, оно и вести себя должно как бумага, а значит, на него должна быть возможность печатать. В итоге получился принтер под названием penguin, который отображается в стандартном диалоге печати macOS. Сам пост озаглавлен "How to build a f**king printer".
Как притвориться принтером
Со стороны Mac ожидается поддержка Internet Printing Protocol. IPP работает поверх HTTP: запрос Get-Printer-Attributes опрашивает поддерживаемые возможности, а Print-Job отправляет задание на печать. Принтер Джоши сообщает, что поддерживает монохромную печать с разрешением 300 dpi, одну копию, одностороннюю печать, форматы бумаги A5 и Letter, тип носителя stationery и выходной лоток face-up. Он принимает форматы Apple raster и PWG raster, поэтому Mac сам растеризует документ в пиксели перед отправкой, и читалке не приходится разбирать PDF.
Обнаружение устройства работает через Bonjour как сервис _ipp._tcp, передающий поддерживаемые форматы и URL для отправки заданий. Для печати без драйверов в macOS также потребовался подтип _universal, который обёртка mDNS для Arduino не предоставляет, поэтому эта часть обращается к mDNS API в ESP-IDF напрямую.
Куда поместить страницу?
Страница формата Letter при 300 dpi имеет разрешение 2550 на 3300 пикселей, что при одном байте на пиксель даёт около 8.4 МБ. Чип ESP32-C3 в X3 оснащён 400 КБ RAM, из которых 16 КБ зарезервировано под кэш. При работающем Wi-Fi и выделенной памяти под изображение страницы оставалось всего 6.8 КБ кучи. Отобразить файл с SD-карты в память (memory-mapping), что было его первой мыслью, нельзя: контроллер умеет отображать в адресное пространство только flash-память, но не файлы на SD-карте.
Выходом стало использование самого дисплея в качестве хранилища. Драйвер экрана уже хранит фреймбуфер, а прошивка до этого собирала второе полное изображение в памяти только для того, чтобы потом скопировать его в экранный буфер. Теперь входящий растр декодируется, масштабируется под размер экрана и переводится в чёрно-белый вид через дизеринг построчно, и каждая готовая строка сразу записывается напрямую во фреймбуфер дисплея, освобождая рабочую память для следующей строки. Его декодер уже работал построчно, так что оставалось лишь заставить масштабировщик точно так же отдавать готовые строки по одной.
Расход памяти под буфер изображения снизился со 113 КБ до 62 КБ, чего хватило для сокетных буферов сетевого стека. Сначала он выводил страницу полосами по мере поступления, как при выходе бумаги из принтера, но каждое частичное обновление e-paper занимало около полусекунды, поэтому теперь страница отображается целиком за один проход.
Первая печать
Страница манги, открытая в Preview, принтер penguin в списке устройств, и примерно через секунду (по памяти, как он пишет) изображение появляется на читалке. На реализацию ушёл один вечер. Сервер печати работает на самой читалке - либо при подключении к домашней сети Wi-Fi, либо через собственную точку доступа literate-penguin. Готовые страницы сохраняются в формате BMP через штатную функцию создания скриншотов в прошивке, после чего их можно просматривать на устройстве. Выходной лоток здесь - обычная папка.
Исходный код выложен в его форке CrossPoint. Помимо самого устройства, этот проект - наглядный пример двух практичных приёмов: реализовать протокол, который целевая ОС уже поддерживает из коробки (чтобы обойтись без драйверов и сторонних приложений на стороне источника), и обрабатывать поток через уже имеющийся буфер вместо выделения второго. Кроме того, это реализация идеи paper-computer с противоположной стороны: вместо того чтобы сканировать бумагу в компьютер, компьютер печатает на носителе, который ведёт себя как бумага.