Nishant Joshi wanted to load documents onto his Xteink X3 e-ink reader without joining its hotspot and clicking through an upload page. His reasoning: if the thing looks like paper, it should accept a print job. That turned into a working printer server, called penguin, running on the reader itself.

Making the host believe there is a printer at the other end:

  • IPP (Internet Printing Protocol) carries operations like Get-Printer-Attributes and Print-Job over HTTP
  • He advertised only what the hardware could do: monochrome, 300 dpi, one copy, one-sided, A5 and Letter
  • Accepted formats were Apple raster and PWG raster, which pushes rasterization onto the Mac
  • Bonjour advertised _ipp._tcp, and macOS driverless discovery additionally required the _universal subtype — obtained by calling ESP-IDF’s mDNS API directly, since the Arduino wrapper did not expose it

The hard part was memory. A Letter page at 300 dpi is about 8.4 MB uncompressed. The chip has 400 KB of RAM, and after Wi-Fi and the page image were allocated, 6.8 KB of heap remained. The C3’s memory mapping covers flash, not SD card files, so swapping to storage was off the table. So he used the display as the buffer instead:

  • Decode each incoming row, scale it, dither to black and white
  • Write the finished row straight into the display’s reserved screen image and reuse the workspace
  • That cut image-buffer RAM from roughly 113 KB to 62 KB, leaving room for the network stack’s socket buffers

Two smaller calls stand out. Rendering the page as it arrived looked like paper feeding out, but every intermediate e-ink refresh cost about half a second, so the finished page now appears all at once. Finished pages are also written to the SD card as BMPs and browsable on the device.

The transferable lesson is not about printers. The naive pipeline buffered a whole second image and then copied it; streaming directly into the destination buffer removed the copy and made the whole thing fit. When a constraint looks disqualifying, the answer is often to change what counts as scratch space.