世界を動かす技術を、日本語で。

USB/IPを介してWebUSBに接続されたブラウザ内Linux仮想マシンで古いプリンターを救う

概要

  • Canon SELPHY フォトプリンターを偶然入手し、Linux環境で再活用した体験談
  • WebUSBv86エミュレーター を活用したクロスプラットフォーム印刷アプリ開発
  • CUPS, Gutenprint, Avahi などのOSS技術によるAirPrint共有
  • Claude Code と協働し、プリンター復活のためのネイティブ・Webアプリ開発
  • 今後の展望 や課題、オープンソース化への思い

古いCanon SELPHYプリンターの再活用ストーリー

  • Canon SELPHY フォトプリンターを友人Markから譲り受けた経緯
    • 3Dプリンターと交換で入手
    • eBay で消耗品より安価に流通する理由
  • MacやWindows ではドライバが未対応
    • Markは Linux ユーザーのため問題なし
  • 手元の Manjaro Linux マシンで動作確認
    • CUPSGutenprint で印刷成功
    • AvahiAirPrint 共有を実現
  • 家族全員が Mac/iPhone から写真印刷を楽しめる環境構築
    • 物理写真の手軽な印刷体験
    • CMY分解印刷 の工程観察

プリンターの「非オタク化」への挑戦

  • 両親にも同様の体験を提供したい思い
    • Raspberry Pi サーバー案はコスト・配線面で断念
  • ソフトウェアだけで簡単に使える方法の模索
    • Claude Code (AI)と共に開発開始
    • Macネイティブアプリ でLinux VMを仮想化しUSB転送
      • App Storeの制約やMac限定の課題

Webアプリへの転換と技術的工夫

  • ChromeのWebUSB に着目しWebアプリ化を決断
    • v86 エミュレーターでx86 Linux仮想環境をブラウザ内で実現
      • Alpine Linux, CUPS, Gutenprint を内蔵
    • プリンターの型番自動検出とドライバ自動インストール
    • ファイルアップロード→仮想Linuxで印刷コマンド発行
  • WebUSB 経由でプリンターへ直接バイナリデータ送信

CUPSとWebUSBの橋渡し

  • 最初は CUPSバックエンド を自作
    • v86のTTY経由でデータ転送
    • stty raw 設定でデータ化け解消
  • 2回目は v86の9pファイルシステム で大容量転送
    • Linux側で ファイル同期 の重要性を学ぶ
  • 課題:プリンターからのエラー情報が取得できない一方通行通信

双方向通信への進化とスキャナー応用

  • USB/IPtcpip.js を用いた双方向通信
    • v86内のLinuxで USB/IP、JS側で tcpip.js (lwIPをWASM化)
    • CUPS がプリンター状態を正確に把握可能
  • SANE を使ったスキャナー復活アプリも試作
    • yes-we-scan.app として公開予定

実用化への細かな工夫

  • JPEG埋め込みPDF 生成で印刷サイズ問題を解決
    • EXIF回転・ICCプロファイル 対応
  • HEIC→JPEG変換パイプライン もAIで自動生成
    • libheif-js, wasm-mozjpeg 連携
  • 消耗品アフィリエイトリンクや簡易テレメトリも実装
    • Neon Postgres DB でセッション・プリンター情報管理

今後の展望とオープンソース化

  • Gutenprint対応機種 なら他モデルでも動作可能性
    • 問題があれば連絡を呼びかけ
  • PPD追加 (brlaser, splix等)の余地
  • オープンソース化は未定
    • 商用連携 やホワイトラベル化の希望
    • 無料でforkされるよりも企業連携を優先

まとめ

  • 古いプリンターの再活用Web技術による新たな価値創出
  • Claude Code の活用による開発効率化
  • プリンター消耗品企業 との連携を模索する今後の展開

Hackerたちの意見

LLMを使ってるなら、関連するCUPSドライバーを見つけるか、USBトラフィックをキャプチャして、Goとかネイティブなもので書き直す方がずっと簡単だったんじゃない?(システムの印刷フレームワークを扱う必要もないし、目標はJPEG入力を受け付けるアプリだけだったし。)

面白い提案だね。確かにそれは可能だったかも。でも、これはもっと一般的な解決策だと思うし、無駄に車輪を再発明することも最小限に抑えられてるよ。

それか、エージェントにDockerfileを書かせて、CUPSやその周りのものをWASMで直接ビルドするようにしてもらうのもいいかもね。x86をターゲットにしてからWASMでエミュレートするんじゃなくて。

確かに、CUPS-rasterデータを入力として受け付けるシンプルなドライバー(CUPSフィルター)にはそれが可能だね。古いプリンターのドライバーの大半がそうだよ。

ありがとう、これめっちゃ良かった!思わず「なるほど!」って感じ。家に古めのSamsungレーザープリンターがあって、Linuxファイルサーバーもあるんだけど、そのプリンターはもうAirPrintをサポートしてないんだ。LinuxボックスをAirPrintサーバーとして使うなんて考えたこともなかった!これで子供たちからの変な印刷リクエストから解放されるかも!(多分)

まだ手放せないSamsung ML-1740があるんだ。ずっとRasPi化しようと思ってるんだけど、これがまた深い穴にハマりそうなプロジェクトなんだよね。

ドライバーをちゃんと揃えられれば、うまくいくよ。12年前のブラザーのレーザープリンターを何回か試してみたけど、毎回投げ出しちゃった。いつかLLMの助けを借りて再挑戦してみようかな。

サムスンのプリンターとCUPSを使って似たようなことを何年もやってるけど、めっちゃ便利だよ。設定ファイルを生成するのに役立つのを見つけたよ: https://github.com/tjfontaine/airprint-generate

これ、かなりの天才的アイデアだね。周りには古すぎたりニッチすぎて現代の何にも対応してないUSBデバイスが結構あるんだ。例えば、GameBoy Advanceのフラッシュカートリッジとか。

あ、そういえばv86は古いDOS/Windowsのバージョンもたくさんサポートしてるから、正しいポートを通せれば(USBなら多分簡単、他のものでも可能かも?)古いドライバーを使えるかもしれないね:)

これまでにオープンソースにしていない部分について謝らなきゃいけないね。主に、これがプリンター消耗品の会社が自社の販売サイトに統合するための素晴らしいウェブプロパティになると思ってるから。単にリポジトリをフォークして無料で手に入れるより、彼らが私にホワイトラベルで作らせてくれた方がずっといいな。彼らが自社のプリンターの使いやすさに少しでも興味があれば、きっと興味を持つだろうね。

それは、プリンターのOEMじゃなくて、アフターマーケットのインクカートリッジ会社のことを言ってるんだよ。

プリンターメーカーはソースコードを持っていて、ファームウェアを書く能力もある。もしWebUSBをサポートしたいと思えば、できるし、やるはずだよ。

一番難しいのは、ほとんどの重要なデザイン選択や実装が、知らない開発者が£18のClaude Codeサブスクリプションを促してるものに対して、誰かにホワイトラベルのためにお金を払わせることかもね。

俺は古いCanon Selphyのフォトプリンターを使ってるけど、Windows 11でも問題なく動いてるよ。Windows 7の64ビットドライバーを使ったら、ほぼそのままで動いた。公式にはサポートされてないけど、今のところ全然問題ないね。

へぇ、面白いね。ドライバーはWindowsに組み込まれてるの?それともCanonからダウンロードしたの?正直、Windowsのウェブアプリの話はあんまり理想的じゃないよね。Zadigをインストールしないと動かないし。

確かに重たい解決策だけど、古いドライバーやデバイス、アプリケーションには役立つ場合もあると思う。WinNT以前のOSにしか対応してない古いハードウェアがあるから、エンドユーザーにとってシンプルな解決策を提供するために似たようなことができるかも。

そんな古いハードウェアで、これに必要な現代のブラウザが動くのかな?

ここで評価してるのは、古いハードウェアを面倒なものじゃなくて、救う価値があるものとして扱ってるところだね。完璧に機能してるデバイスの寿命を延ばすソフトウェアには、隠れた価値がたくさんある。特に、代わりにエコシステムの変化で交換する理由が多い時はね。これが良い意味での不条理だよ。

「ラズベリーパイをプリントサーバーとして設定できるかも?でもそれだと安くはなくなるなぁ。そもそも、追加のプラグや配線を使うのに彼らが乗り気になるかも疑問だし。」35ドルで手に入るよ。技術サポートやCUPS/SANEの開発資金も含まれてるし、全部オープンソースだよ: https://printserver.ink 追加のプラグは「IEC320 3ピン C14 TO メス C13+2」ケーブルを使えばなくせるかも: https://ae-pic-a1.aliexpress-media.com/kf/S4c8681fb1283499b8...

1 GiB LPDDR4 画像や2ページ以上の印刷を試みると、これって大きな問題にならない?

ラズベリーパイでこれやったよ。問題のプリンターはWiFiとUSBしかなくて、イーサネットはないんだ(WiFiは不安定だし、TCP/IPスタックも信用できないしね)。だから、USBでラズベリーパイに接続して、リビングルームにイーサネットでつなげたんだ。AirPrintとbrscanを追加して、デバイスはusbip経由で共有できるようにしたよ。例えば、USB SDRを使ってusbip経由で接続できるんだ。最初はスキャナー部分にqemu x86-64を使わなきゃいけなかったけど、あれは理想的じゃなかったな。使ってるUIコンピュータはApple製(iPhoneやiPad)だけだし。ネットワークがコンピュータになる世界では、usbipとiscsiはすごくクールな技術だよ。ラズベリーパイを選んだ理由は、すでにValetudoのインターフェースとして使ってたから。だから、もう使ってたんだよね。それに、IoTスキャン用にBluetoothを追加したいと思ってて、Home Assistantを動かすことも考えてる。でも、確かにUIがないのが欠点だね。リバースプロキシを追加しようかなとも考えたけど、何をするか全然わからないし、このプロジェクトは自宅にあるわけじゃなくて、母のアパートにあるんだ。

もう数年、ラズベリーパイのプリントサーバーを使ってるよ。出荷ラベルプリンターや、2000年代初頭の優れたHPのカラープリンター、他にもいくつか変わったプリンターを動かしてる。

実際のハードウェア(USB HID経由のラズベリーパイPico W)を制御するLangGraphエージェントで自動化パイプラインを運用するのは、サンドボックス化が必須だよ。端末上の不正なエージェントは面倒だし、間違ったHIDコマンドをデバイスに送ると壊れちゃうからね。だから、こうしたよ:エージェントはJSONアクションプランを出力するだけで、直接何かを実行することはない。別のバリデーターが、送信する前にホワイトリストにあるUSBコマンドとプランを照合するんだ。要するに、エージェントを信頼できない外部の貢献者として扱うってこと。それがまさに彼らの役割だから。記事のSSH/VMアプローチは純粋なコーディングにはうまくいくけど、ハードウェアが絡むセットアップでは、「エージェントがXをやりたい」と「実際にXが起こる」の間にその追加のバリアが必要なんだ。