Как ноутбук увидел принтер там, где был ридер
Читаю статью и ловлю себя на мысли: вот человек купил электронную книжку с e-ink дисплеем, а потом решил сделать из неё принтер. Не принтер для книги — а принтер как устройство. Который принимает задания на печать от MacBook через стандартный диалог Print.
Идея: если выглядит как бумага — пусть и работает как бумага
Автор — Nishant Joshi — купил Xteink X3, электронную книгу с программируемым дисплеем. Поставил свою прошивку CrossPoint и добавил несколько фишек: анимацию при загрузке, виртуальные кости и QR-код для LinkedIn.
Но загружать контент было неудобно: нужно было подключаться к хотспоту ридера и открывать веб-интерфейс. Тогда автор подумал: раз дисплей выглядит как бумага, почему бы не сделать так, чтобы на него можно было печатать?
Как заставить ноутбук увидеть «принтер»
Чтобы Mac увидел устройство как принтер, автор ушёл в Internet Printing Protocol, или IPP. Это стандарт, который позволяет компьютеру:
- спросить принтер о его возможностях — какие форматы и какое разрешение он поддерживает;
- отправить документ на печать;
- узнать статус задания.
IPP работает поверх HTTP — того же протокола, что браузеры используют для сайтов. Это удобно: не нужно изобретать велосипед.
Автор объявил, что его «принтер» поддерживает монохромную печать с разрешением 300 dpi, понимает форматы Apple raster и PWG raster, знает размеры A5 и Letter и называется penguin.
Оставалось сделать так, чтобы ноутбук вообще нашёл это устройство в сети.
Служба обнаружения: bonjour и mdns
Чтобы Mac увидел принтер без драйверов, нужно было объявить о нём через Bonjour — систему автоматического обнаружения сервисов в локальной сети от Apple. Внутри она использует mDNS: устройство рассылает сигнал «я тут!», и компьютеры его слышат.
Нужно было зарегистрировать сервис _ipp._tcp с подтипом _universal. Автор пишет, что стандартная Arduino-обёртка для mDNS не давала доступа к этой функции, поэтому пришлось вызывать API ESP-IDF напрямую.
После этого в диалоге печати на MacBook появился penguin. Оставалась одна маленькая проблема: 400 килобайт оперативной памяти.
Проблема: как распечатать страницу, не имея памяти для страницы
Вот в чём загвоздка. Страница формата Letter при разрешении 300 dpi — это 2550 на 3300 пикселей. Если хранить каждый пиксель как один байт, получается около 8,4 мегабайта. Устройство в сумме имеет 400 КБ, часть из которых занята кэшем и Wi-Fi. После инициализации сетевого стека и буфера изображения остаётся всего 6,8 КБ.
Классический подход — принять всю страницу в память, потом её отобразить — здесь невозможен.
Решение оказалось элегантным: использовать оперативную память дисплея как хранилище для страницы. Дисплей уже содержит в памяти изображение, которое показывает. Значит, страницу можно строить прямо там, по мере поступления пикселей.
ПОТОК ДАННЫХ ПРИ ПЕЧАТИ
────────────────────────
MacBook ──HTTP/IPP──▶ ESP32-C3
│
▼
┌───────────────────────┐
│ Декодер (построчно) │
│ • Расшифровка потока │
│ • Масштабирование │
│ • Dithering (ч/б) │
└───────────┬───────────┘
│ готовая строка
▼
┌───────────────────────┐
│ RAM дисплея E-ink │
│ (вместо буфера в RAM) │
│ ─────────────────────│
│ ▓▓▓░░▓▓░░▓▓▓░░▓▓░░▓▓▓ │
│ ░▓▓░░▓▓▓░░▓▓░░▓▓▓░░▓▓ │
│ ... весь экран ... │
└───────────────────────┘
│
▼
E-ink display
Декодер уже умел работать построчно. Автор добавил возможность сразу отправлять готовые строки в буфер экрана, минуя промежуточное хранилище. Параллельно шло масштабирование — картинка с ноутбука больше, чем экран ридера, — и дизеринг, то есть преобразование градаций серого в чёрно-белые точки по алгоритму Флойда-Стейнберга.
Изначально автор показывал страницу полосами, как настоящий принтер выдаёт бумагу. Но каждая промежуточная отрисовка занимала полсекунды, и результат раздражал. В итоге он переключился на мгновенный показ готовой страницы.
Результат
Первая тестовая печать — страница манги. Открыл Preview на MacBook, выбрал penguin в списке принтеров, нажал Print. Через секунду картинка появилась на экране ридера.
РАСПРЕДЕЛЕНИЕ ПАМЯТИ ESP32-C3
─────────────────────────────
До оптимизации:
┌──────────┬───────────────────┬──────────┐
│ Wi-Fi │ Изображение │ Сеть │
│ стек │ (8.4 MB → │ буферы │
│ ~200 KB │ не влезает) │ ~80 KB │
└──────────┴───────────────────┴──────────┘
❌ Недостаточно RAM
После (потоковая обработка):
┌──────────┬──────────┬────────────────────┐
│ Wi-Fi │ Сеть │ E-ink буфер │
│ стек │ буферы │ (встроен в │
│ ~200 KB │ ~62 KB │ дисплей) │
└──────────┴──────────┴────────────────────┘
✅ 6.8 KB на работу
Распечатки сохраняются на SD-карту как BMP-файлы — автор шутит, что у его принтера всё-таки есть выходной лоток. Это папка.
Принтер может подключиться к существующей Wi-Fi сети или создать свою точку доступа — literate-penguin. Исходный код доступен в форке CrossPoint на GitHub.
Моя оценка
Это не инженерный подвиг в классическом смысле. Здесь нет сложной механики, навороченных деталей или космического бюджета. Есть переосмысление: дисплей выглядит как бумага — значит, должен вести себя как бумага. Остальное — следствие этого решения.
Меня зацепило другое. Автор не стал покупать 3D-принтер или делать что-то «правильное». Он взял существующее устройство и заставил его решать задачу, для которой оно формально не предназначено. IPP, Bonjour, потоковая обработка — всё это существующие решения, просто никто не складывал их так.
Практический вывод простой: если ты застрял на «правильном» способе сделать что-то, попробуй спросить себя: а что если это уже и есть принтер?
Ссылки
- Оригинальная статья на блоге Nishant Joshi — рассказ автора о том, как он превратил e-ink ридер в принтер
- CrossPoint на GitHub — исходный код прошивки и связанные изменения
Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.