⏱️ Lectura: 13 min
Un lector de tinta electrónica con apenas 400 KB de RAM total ahora puede recibir trabajos de impresión reales desde el diálogo Imprimir de una Mac, sin instalar ningún driver. Nishant Joshi, desarrollador y dueño de un Xteink X3, implementó el protocolo IPP (Internet Printing Protocol) directamente en el firmware del dispositivo para lograrlo.
📑 En este artículo
- TL;DR
- Introducción
- Qué pasó: el protocolo IPP entra al firmware
- Contexto e historia
- Detalles técnicos y rendimiento
- Cómo empezar a probarlo
- Impacto y análisis
- Qué sigue
- Preguntas frecuentes
- ¿Qué es el protocolo IPP y en qué se diferencia de imprimir por USB?
- ¿Necesito instalar un driver para usar una impresora IPP?
- ¿Por qué usar Bonjour/mDNS en vez de configurar una IP fija?
- ¿Qué es el subtype _universal y por qué importa en macOS?
- ¿Puedo replicar esto en un ESP32 normal sin pantalla e-ink?
- ¿Qué limitaciones tiene un servidor IPP casero como este?
- Referencias
El resultado se comporta como una impresora de verdad: aparece solo en la lista de impresoras del sistema y hasta imprimió un manga desde Vista Previa en cerca de un segundo, según relata el propio autor. Lo interesante no es tanto el protocolo, sino cómo resolvió meter una página de 8,4 MB en un chip que apenas tiene RAM para una fracción de eso.
TL;DR
- Nishant Joshi convirtió un e-reader Xteink X3 en impresora real usando el protocolo IPP (Internet Printing Protocol).
- El e-reader se anuncia por Bonjour/mDNS como servicio _ipp._tcp con el subtype _universal para funcionar sin drivers en macOS.
- Acepta Apple Raster y PWG Raster a 300 dpi, monocromo, una copia, papel A5 o Letter y bandeja face-up.
- Una página Letter a 300 dpi pesa cerca de 8,4 MB sin comprimir (2.550×3.300 px), pero el Xteink X3 solo tiene 400 KB de RAM.
- Con WiFi y servidor IPP activos, quedaban apenas 6,8 KB de heap libres para recibir la página entera.
- La solución fue decodificar, escalar y ditherizar fila por fila directo sobre el buffer de pantalla, sin copia extra.
- Esa técnica bajó el buffer de imagen de aproximadamente 113 KB a 62 KB sobre los 400 KB totales del chip.
- El código completo quedó publicado en el fork de CrossPoint del autor en GitHub.
Introducción
La mayoría de los desarrolladores nunca piensa en cómo llega una página desde su computadora hasta el papel. Apretás Cmd+P o Ctrl+P y algo, en algún lugar, se encarga del resto. Ese “algo” casi siempre habla IPP, un protocolo que corre sobre HTTP y que tu Mac, tu impresora de oficina y hasta tu celular hablan sin que lo notes.
Joshi decidió abrir esa caja negra. Tenía un Xteink X3, un lector de tinta electrónica programable con un firmware de terceros llamado CrossPoint, y quería una forma más cómoda de mandarle contenido que conectarse a su hotspot y subir archivos por una web casera. Se le ocurrió una pregunta simple: si el dispositivo parece papel, ¿por qué no puede comportarse como papel? Es decir, por qué no imprimirle directamente.
Qué pasó: el protocolo IPP entra al firmware
Joshi implementó un servidor de protocolo IPP completo dentro del firmware del Xteink X3. IPP le permite a una computadora preguntarle a una impresora qué soporta con la operación Get-Printer-Attributes, enviarle un documento con Print-Job y consultar el estado del trabajo. Todo viaja como mensajes HTTP normales.
Declaró que su impresora, bautizada penguin, soporta salida monocromática, 300 dpi, una copia y una sola cara. Como formatos de documento aceptó Apple Raster y PWG Raster, lo que significa que es la propia Mac la que convierte el documento en píxeles antes de enviarlo: el Xteink X3 solo tiene que recibir y mostrar una imagen ya rasterizada. También declaró tamaños de papel A5 y Letter, tipo de medio stationery y una bandeja de salida llamada face-up.
Para que apareciera automáticamente en la lista de impresoras sin configurar nada, anunció el servicio vía Bonjour bajo el tipo _ipp._tcp, incluyendo en el anuncio los formatos aceptados y la dirección donde debían llegar los trabajos. Para lograr el descubrimiento sin drivers en macOS necesitó agregar además el subtype _universal, lo que lo obligó a llamar directamente a la API de mDNS de ESP-IDF porque el wrapper de Arduino no la exponía.
Contexto e historia
El nombre “penguin” no es casual: hace referencia al esquema de color del dispositivo. El Xteink X3 llegó al radar de Joshi porque, según la publicidad del producto, era “completamente programable”. Eso lo llevó a instalar CrossPoint, un firmware alternativo para este tipo de lectores, sobre el que empezó a experimentar con una animación de arranque distinta, un dado que se tira agitando el lector y un código QR de LinkedIn para eventos de networking en San Francisco.
El problema apareció con el flujo normal para cargar contenido: había que conectarse al hotspot del propio lector y abrir una pequeña web de carga en el navegador. Funcional, pero tedioso para algo que se usa todos los días. De ahí nació la idea de usar IPP en lugar de un flujo de carga manual: aprovechar un protocolo que cualquier sistema operativo moderno ya sabe hablar, en vez de inventar uno nuevo desde cero.
Detalles técnicos y rendimiento
El límite real del proyecto no fue el protocolo, fue la memoria. Una página tamaño Letter a 300 dpi mide 2.550 por 3.300 píxeles. A un byte por píxel en escala de grises, eso son cerca de 8,4 MB sin comprimir.
El Xteink X3 tiene 400 KB de RAM en total, con 16 KB reservados para caché. Con el WiFi corriendo, el servidor de impresión activo y la imagen de página ya reservada en memoria, al programa le quedaban apenas 6,8 KB de heap libres antes de recibir un solo byte de la página.
⚠️ Ojo: la primera idea de Joshi fue algo parecido a mmap de Linux, tratar la tarjeta SD como si fuera RAM extra. No sirvió: el soporte de memory-mapping del ESP32-C3 es para flash interna, no para archivos en la SD. Un gotcha típico de portar ideas de sistemas operativos grandes a un microcontrolador.
La solución fue otra: en vez de simular más RAM, reutilizar la que ya existía para otra cosa. La pantalla del Xteink X3 ya reserva memoria para la imagen que muestra en cada momento. Joshi construyó la página ahí mismo, a medida que llegaba, en lugar de armar una copia completa aparte y transferirla después.
Su decodificador ya procesaba la imagen fila por fila. Modificó el escalador para que también entregara filas terminadas, y las conectó directamente a la imagen de pantalla del dispositivo. Al principio la página aparecía en bandas, como papel saliendo de una impresora real; cada actualización parcial tardaba cerca de medio segundo, así que terminó mostrando la página completa de una sola vez. El resultado final se guarda como BMP en la tarjeta SD usando el mismo componente que ya usaba para capturas de pantalla.
| Buffer de imagen | RAM usada | % del total (400 KB) |
|---|---|---|
| Antes (copia completa aparte) | ~113 KB | ~28% |
| Después (escritura directa a pantalla) | ~62 KB | ~15,5% |
flowchart TD
A["Mac imprime documento"] --> B["Rasteriza a Apple Raster o PWG Raster"]
B --> C["HTTP POST Print-Job al Xteink X3"]
C --> D["Decodifica la imagen fila por fila"]
D --> E["Escala y aplica dither"]
E --> F["Escribe la fila en el buffer de pantalla"]
F --> G[("Guarda la pagina final como BMP en la SD")]
Los dos formatos de raster que acepta no son intercambiables en la práctica: cada uno lo genera un sistema operativo distinto.
| Formato | Cuándo lo usa | Ventaja | Limitación |
|---|---|---|---|
| Apple Raster (URF) | Imprimir desde macOS o iOS | Nativo en el diálogo Imprimir de Apple, no requiere driver | Formato propietario, poco documentado fuera del ecosistema Apple |
| PWG Raster | Imprimir desde Linux/CUPS o clientes IPP genéricos | Estándar abierto del Printer Working Group | El cliente tiene que soportarlo de forma explícita |
Cómo empezar a probarlo
No hace falta un e-reader para experimentar con IPP. Cualquier laptop puede anunciar y descubrir servicios _ipp._tcp en la red local con herramientas de mDNS/Bonjour que ya trae el sistema operativo o que se instalan en un minuto.
Descubrir impresoras IPP en tu red:
# macOS (dns-sd viene incluido con el sistema)
dns-sd -B _ipp._tcp
# Linux (Debian/Ubuntu)
sudo apt install avahi-utils
avahi-browse -r _ipp._tcp
# Windows (requiere Bonjour Print Services, incluido con iTunes o descargable aparte)
dns-sd.exe -B _ipp._tcp
Este comando lista cualquier impresora IPP (real o casera) visible en la red, junto con la dirección donde escucha.
Para anunciar tu propio servicio de juguete, sin escribir un firmware completo, alcanza con Python y la librería zeroconf:
# Linux / macOS / Windows (Python 3)
pip install zeroconf
# ipp_anuncio.py
from zeroconf import ServiceInfo, Zeroconf
import socket
info = ServiceInfo(
"_ipp._tcp.local.",
"mi-impresora-casera._ipp._tcp.local.",
addresses=[socket.inet_aton("192.168.1.50")],
port=631,
properties={"rp": "ipp/print", "ty": "Impresora de prueba"},
)
zeroconf = Zeroconf()
zeroconf.register_service(info)
print("Servicio _ipp._tcp anunciado, Ctrl+C para salir")
try:
input()
finally:
zeroconf.unregister_service(info)
zeroconf.close()
Al correrlo, el servicio aparece en dns-sd -B _ipp._tcp o en avahi-browse sin que hayas escrito una sola línea de código de impresión: eso es exactamente lo que aprovechó Joshi para que macOS detectara a “penguin” sin drivers.
Para enviar un trabajo de impresión de verdad a un servidor IPP desde Node.js, el paquete ipp hace de cliente:
// Linux / macOS / Windows (Node.js)
npm install ipp
// enviar-trabajo.js
const ipp = require("ipp");
const fs = require("fs");
const printer = ipp.Printer("http://192.168.1.50:631/ipp/print");
const datos = fs.readFileSync("pagina.pwg");
const mensaje = {
"operation-attributes-tag": {
"requesting-user-name": "desarrollador",
"job-name": "prueba-desde-node",
"document-format": "image/pwg-raster",
},
data: datos,
};
printer.execute("Print-Job", mensaje, (error, respuesta) => {
if (error) throw error;
console.log("Trabajo enviado:", respuesta["job-attributes-tag"]);
});
Este script arma un mensaje IPP mínimo con el nombre del trabajo y el formato del documento, y lo manda por HTTP al puerto 631, el mismo que usa CUPS y que declaró Joshi en su firmware.
Impacto y análisis
Lo que hace interesante este proyecto no es que alguien haya “hackeado” un e-reader, sino que muestra cuánto protocolo real cabe en muy pocos kilobytes cuando se implementa a mano, sin la pila completa de CUPS ni un sistema operativo de propósito general debajo. IPP corre sobre HTTP, un protocolo que cualquier microcontrolador con WiFi ya puede hablar; lo que falta casi siempre no es la capa de red, sino la memoria para los datos que esa red transporta.
💭 Clave: el cuello de botella no estuvo en el protocolo (HTTP + un puñado de atributos IPP) sino en el dato en sí: una imagen rasterizada. Reescribir el pipeline para no duplicar esa imagen en memoria fue lo que hizo viable el proyecto completo.
Hay un límite honesto que vale la pena marcar: un servidor protocolo IPP casero como este implementa apenas dos operaciones (Get-Printer-Attributes y Print-Job). No maneja colas de trabajos, autenticación, cancelación ni impresión concurrente de varios clientes, cosas que sí resuelve CUPS en un servidor de impresión compartido de oficina. Sirve perfecto para un dispositivo personal de un solo usuario, no para reemplazar una impresora de red compartida.
Qué sigue
El código quedó publicado en el fork de CrossPoint del propio autor, lo que abre la puerta a que otros dueños de lectores con el mismo firmware repliquen el servidor de impresión o lo extiendan con soporte para más tamaños de papel, impresión a color en dispositivos que lo permitan, o reporte de estado de trabajo más detallado vía IPP. El propio Joshi ya usa el dispositivo para navegar los archivos impresos guardados en la tarjeta SD, que funciona como una especie de bandeja de salida permanente.
📖 Resumen en Telegram: Ver resumen
Si tenés un ESP32 con soporte de ESP-IDF y ganas de experimentar, probá anunciar tu propio servicio _ipp._tcp con el script de Python de arriba esta misma tarde y confirmá que aparece en dns-sd -B _ipp._tcp o avahi-browse -r _ipp._tcp.
Preguntas frecuentes
¿Qué es el protocolo IPP y en qué se diferencia de imprimir por USB?
IPP (Internet Printing Protocol) es un protocolo que corre sobre HTTP y permite que una computadora consulte las capacidades de una impresora, le envíe un documento y consulte el estado del trabajo, todo por red. A diferencia de USB, no requiere un cable ni un driver específico del fabricante: alcanza con que ambos extremos hablen el mismo protocolo.
¿Necesito instalar un driver para usar una impresora IPP?
En la mayoría de los casos no. Si la impresora anuncia correctamente sus capacidades y formatos soportados, sistemas como macOS pueden generar un driver genérico automáticamente a partir de esa información, sin descargar nada del fabricante.
¿Por qué usar Bonjour/mDNS en vez de configurar una IP fija?
Bonjour permite que el dispositivo se anuncie solo en la red local, con su nombre, dirección y capacidades, para que las computadoras lo detecten sin que el usuario tenga que teclear una IP ni instalar software adicional.
¿Qué es el subtype _universal y por qué importa en macOS?
Es un subtype adicional del servicio _ipp._tcp que macOS usa específicamente para ofrecer descubrimiento e impresión sin drivers (AirPrint-style). Sin declararlo, el dispositivo puede seguir siendo visible por red pero no calificar para ese flujo sin configuración.
¿Puedo replicar esto en un ESP32 normal sin pantalla e-ink?
Sí, la parte de protocolo (servidor HTTP + atributos IPP + anuncio mDNS) no depende de tener una pantalla. Lo que cambia es dónde guardás o mostrás la página recibida: sin pantalla, tendrías que escribirla directo a la tarjeta SD o a flash en vez de al buffer de pantalla.
¿Qué limitaciones tiene un servidor IPP casero como este?
No maneja colas de trabajos, autenticación ni impresión simultánea de varios clientes. Es apto para un dispositivo personal de un solo usuario, no para reemplazar una impresora de red compartida en una oficina.
Referencias
- Blog de Nishant Joshi: publicación original donde documenta la implementación completa del servidor IPP en el Xteink X3.
- RFC 8011 (IETF): especificación del modelo y la semántica del Internet Printing Protocol.
- Printer Working Group: organización que mantiene el estándar PWG Raster usado por impresoras IPP.
- Bonjour (Apple Developer): documentación del protocolo de descubrimiento de servicios usado para el anuncio _ipp._tcp.
- Internet Printing Protocol (Wikipedia): contexto general e historia del protocolo IPP.
📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.
Imagen destacada: Foto de Nicolas Thomas en Unsplash
0 Comentarios