プリンターの作り方
How to Build a Printer (nishantjosh.dev)
要約
筆者は、e-inkディスプレイであるXteink X3にMacBookから直接印刷できるように、インターネット印刷プロトコル(IPP)を実装しました。メモリ容量の制約から、受信したデータをディスプレイバッファに直接書き込むことで、ディスプレイ自体をストレージとして活用する巧妙な方法を見つけました。これにより、MacBookからXteink X3への印刷が可能になり、独自の「プリンター」が完成しました。
全文翻訳
プリンターを自作する方法
電子ペーパーディスプレイを自作しようと考えていたところ、オンラインでXteink X3を見つけました。その仕様のどこかに、このデバイスが完全にプログラマブルであることが記されていました。
それに心を奪われました。
届いたX3は、その薄さに惹かれました。手に持つとしっかりとした感触がありました。私はCrossPointファームウェアに機能を追加し始めました。例えば、異なる起動アニメーション、本体を振るとサイコロが振れる機能、SFネットワーキングイベント用のLinkedIn QRコードなどです。
しかし、コンテンツをデバイスに転送するのが面倒でした。そのデバイスのホットスポットに参加し、ブラウザで小さなアップロードサイトを開く必要がありました。
うんざりしました。
もっと良い方法でデバイスにコンテンツを送信する方法を探しているうちに、ある考えが浮かびました。もし紙のように見えるなら、紙のように振る舞うべきだ。
それに印刷できるはずだ。
[画像: プリンターの白黒の描画を表示しているXteink X3。]
プリンターとは何か?
MacBook上で何かを開き、「プリント」ボタンを押し、Xteinkを選択できるようにしたかったのです。そのためには、コンピューターが反対側で何を見つけることを期待しているのかを知る必要がありました。
私はInternet Printing Protocol(IPP)にたどり着きました。これは、コンピューターがプリンターに何がサポートされているかを問い合わせ、ドキュメントを送信し、ジョブの状況を尋ねることができるプロトコルです。メッセージはHTTP経由でやり取りされます。「Get-Printer-Attributes」のような操作は機能について問い合わせ、「Print-Job」は作業を送信します。
私はモノクロ出力、300 dpi、1部、片面印刷を広告しました。ドキュメント形式としては、Apple rasterとPWG rasterを受け入れました。これは、Macがドキュメントを送信前にピクセルに変換する必要があることを意味します。「penguin」は結果を画面に収まるように縮小しました。
A5とレターサイズの用紙、メディアタイプ「stationery」、そして「face-up」という出力トレイを宣言しました。
私はそれを「penguin」と名付けました。色合いがちょうど良かったからです。
Bonjourを使用して、その名前で「_ipp._tcp」サービスをアナウンスしました。アナウンスには、受け入れる形式と、プリントジョブを送信すべきアドレスが含まれていました。
macOSでドライバーレス検出を行うには、「_universal」サブタイプを追加する必要もありました。これは、Arduinoラッパーが公開していなかったため、ESP-IDFのmDNS APIを直接呼び出すことを意味しました。
コンピューターにページを送信させることは、仕事の一部に過ぎませんでした。まだ、このデバイスでそれを受け取る必要がありました。
ページはどこに置く?
300 dpiのレターサイズページは、2,550 × 3,300ピクセルです。グレースケールピクセルあたり1バイトとすると、非圧縮で約8.4 MBになります。
X3は400 KBのRAMを持ち、16 KBがキャッシュ用に予約されています。Wi-Fiを実行し、プリンターサーバーを実行し、どうにかして1ページ全体を受け取る必要がありました。
Wi-Fiを実行し、プリンターのページ画像を割り当てた後、ヒープには6.8 KBしか残っていませんでした。
Linuxのmmapのことを思い出しました。SDカードでそれに似たことをして、より多くのRAMがあるかのように見せかけることはできないだろうか?C3のメモリマッピングサポートはフラッシュ用であり、SDカード上のファイル用ではありませんでした。
しかし、待てよ。ディスプレイをストレージとして使うことはできないだろうか?
受信したページを変換パイプラインに通し、結果を直接ディスプレイに書き込むとしたらどうなるだろうか?ピクセルをデコードし、画面に収まるように縮小し、白黒にディザリングする。行が準備でき次第、それをディスプレイ上の適切な場所に配置し、作業スペースを再利用する。ページができるまでこれを続ける。
ディスプレイにはすでに画面イメージ用にRAMが予約されていました。到着次第、ページをそこに構築できるのです。それまでは、完全に別の画像を構築してからコピーしていました。
私のデコーダーはすでに1行ずつ動作していました。スケーラーが完成した行も引き渡すように変更し、それらをディスプレイの画面イメージに接続しました。最初は、紙がプリンターから送り出されるように、ページが帯状に表示されるようにしていました。各中間リフレッシュは約0.5秒かかったため、完成したページを一度に表示するように切り替えました。
完成したページは、既存のスクリーンショットライターを使用して、SDカードにBMPとして保存しました。
| 画像バッファRAM | 前 | 後 |
|---|---|---|
| チップの400 KBから | 約113 KB | 約62 KB |
これにより、ネットワークスタックにソケットバッファ用のスペースができました。
Previewにペンギンがいた
なぜかMacBookに『無職転生』のマンガのサンプル画像がありました。それをPreviewで開き、印刷に進みました。
プリンターリストに「penguin」がありました。
マジかよ。
それを選択して印刷しました。マンガのページはXteink上で非常に良く見えました。記憶では、表示されるまで約1秒かかりました。それは今もそこにあります。
[画像: 閉じられたMacBookの上に置かれた、Xteink X3に印刷されたマンガのページ。]
最初の印刷ができるまで、一晩かかりました。
プリンターサーバーはリーダー自体で実行されます。Wi-Fiネットワークに参加させることも、独自のホットスポット「literate-penguin」を開始することもできます。
私のペンギンは今、読むことができます。
コードは私のCrossPointフォークにあり、プリンター実装も含まれています。
保存された印刷物はSDカードに残っており、リーダーで閲覧できます。私のプリンターには結局、出力トレイがあります。それはフォルダです。