HP Sprocket 200のBLEプロトコルを解析してPCから印刷した
目次
HP Sprocket 200を中古で手に入れた。
ZINKペーパーを使うモバイルフォトプリンターで、スマホから専用アプリ経由で印刷するタイプ。
前にサーマルプリンターをPCから操作したのと同じく、今回もPCから印刷できるか試す。
なぜこれにしたか
ZINKペーパーはHP SprocketでもCanon iNSPiCでも使える互換性がある。
ただHP用の純正ペーパーは流通が少なく、探すのが大変。
Canon iNSPiC用のほうが圧倒的に手に入りやすい。
じゃあ安い中古のSprocketを買って、紙はCanon用を使えばいいかというと、そう単純でもない。
ZINKペーパーは互換性があっても、純正品パックの一番下に入っているスマートシート(青い紙)がないと大抵印刷できない。
このシートがプリンターのキャリブレーションに使われるらしく、他社の紙だけ突っ込んでも動かないケースが多い。
つまり狙い目は「純正ペーパーが付属している中古品」。
この事前情報をもとにSofmapのジャンクコーナーを漁っていたら、約2,000円で純正ZINKペーパー付きのSprocket 200があったので即決した。
なぜかこの個体だけ純正品の紙がまるまる付いていた。
開封

買ったのはHP Sprocketのほうだけ。
Canon iNSPiC用のZINKフォトペーパー20枚入りは、HP用の紙が手に入らなくなったときの保険。

Sprocket 200の箱を開けたところ。
本体、USBケーブル、ZINKフォトペーパーのサンプル、安全情報シートが入っていた。
本体は手のひらサイズで、表面にスペックル模様が入っている。
ZINKペーパーのセット

HP純正(10枚入り)とCanon iNSPiC用(10枚入り)のZINKペーパー。
中身は同じZINK方式だがパッケージと付属のスマートシートが異なる。

上がHP純正のスマートシート、下がCanon用。
スマートシートはパック内の一番下に1枚入っている青い紙で、紙をセットしたときに最初に通してキャリブレーションに使われる。

左がZINKフォトペーパー、右がスマートシート。
今回はスマートシートだけHP純正を使い、紙本体はCanon iNSPiC用を入れた。
キャリブレーションさえHP純正のスマートシートで通れば、紙自体は他社製でも動くはず。

白い面が印刷面で、ブランドロゴが入っている面が裏。
裏面を下にしてプリンターにセットする。
よく見るとHP用とCanon用で紙の色味が微妙に違う。同じZINK方式でも製造ロットや仕様が少し異なるのかもしれない。

HP純正のスマートシートとCanon用の紙をセットして電源を入れると、最初にスマートシート(青い紙)が自動で排出された。
キャリブレーション処理は問題なく完了。他社製の紙でも受け付けてくれた。
PCから印刷できるか
HP SprocketにはPC/Mac用のドライバーもデスクトップアプリも存在しない。
HP Smart(HP汎用プリンターアプリ)もSprocket非対応。
公式にはスマホアプリ一択ということになっている。
ただし非公式な方法の報告がある。
方法1: Bluetooth OBEXファイル送信(Windows/Mac)
一番手軽な方法。
WindowsのBluetoothメニューから「ファイルの送信」で画像を直接送る。
- Sprocket 200の電源を入れてPCとペアリング
- タスクバーのBluetoothアイコン → 「ファイルの送信」
- 送信先としてSprocketを選択
- 画像ファイルを指定して送信
アプリのフィルターや余白調整は使えず、画像をそのまま送るだけ。
MacでもBluetooth設定から同様にファイル送信できるとの報告がある。
ただしSprocket 200はBluetooth 5.0搭載で、ファームウェアによってはOBEX Push Profile(OPP)が無効化されている可能性がある。
初代Sprocket(Bluetooth 3.0)では比較的安定して動いたらしいが、200では試してみないとわからない。
方法2: obexftpコマンド(Linux/Raspberry Pi)
Raspberry Piのフォトブースプロジェクト「pibooth」で動作実績がある方法。
sudo apt-get install bluetooth libbluetooth-dev obexftp
obexftp --nopath --noconn --uuid none --bluetooth [MACアドレス] --channel 4 -p image.png
チャンネル4がHP Sprocket固有のようで、CUPSプリンターとしては認識されないためobexftpで直接叩く形になる。
方法3: Androidエミュレーター経由(動かない)
BlueStacksなどでHP Sprocketアプリを動かすという情報がネットに多いが、主要なAndroidエミュレーターはBluetoothパススルーに対応していないので動作しない。
方針
方法1のOBEX送信をまず試す。
ダメなら方法2のobexftpをMac/Linuxから試す。
前回のサーマルプリンターはBLEのリバースエンジニアリングで制御できたが、Sprocketについては専用プロトコルのリバエン情報がほぼ見つからなかったので、OBEXで通らなければ厳しいかもしれない。
BLE接続を試す
OBEXの前に、まずBLEで直接つないでみることにした。
紙は入れずに接続だけ試す。
なおこの個体、バッテリーが死んでいるらしくmicroUSB給電なしでは起動しない。
ジャンク品なので仕方ない。USB挿しっぱなしで進める。
デバイス検出
bleakでBLEスキャンすると HP Sprocket 200 (8C:2D) として検出された。
Advertisementデータに独自サービスUUID 6822d239-7b61-4718-bdc1-189221946209 が含まれており、ペイロードは 62 49 06 00 06 00 の6バイト。
サービス構造
接続してGATTサービスを列挙した結果、カスタムサービスが1つだけ見えた。
| UUID(末尾) | プロパティ | 用途(推測) |
|---|---|---|
...051e | write, write-without-response | データ送信(画像・コマンド) |
...3658 | read, notify, indicate | レスポンス受信 |
...2eee | read | ステータス? |
前回のサーマルプリンター(Mini Printer Sugar)はISSSC Transparent UARTサービスでESC/POSコマンドを流せたが、Sprocket 200は完全に独自プロトコル。
サービスUUIDもキャラクタリスティクスUUIDも既知のBLEプロファイルに一致しない。
読み取り結果
Readableなキャラクタリスティクス2つを読んだが、どちらも空データが返ってきた。
Notifyを5秒間購読しても何も飛んでこない。
Char ...3658 (read/notify): 空
Char ...2eee (read): 読み取りエラー
Notifications: 5秒間なし
おそらく初期化コマンド(ハンドシェイク)をWrite側に送らないとプリンターが応答を返さないプロトコル設計になっている。
コマンドプローブ
紙を消費せずにプロトコルを探るため、Writeキャラクタリスティクスに色々なバイト列を送ってNotifyの反応を見た。
結果:
0x00〜0x7F(MSBが0): 何も返さない。コマンドとして認識されていない0x80〜0xFF(MSBが1): 全部同じ81 01 00 02を返す- 2バイト以上の構造化コマンド(ヘッダー+長さ+ペイロード形式)も全部
81 01 00 02 - ESC/POSシーケンス、
HPのASCIIマジックバイトなども同様
送信: 0xFF → 応答: 81 01 00 02
送信: 0x80 0x01 → 応答: 81 01 00 02
送信: 0x80 0x0A 0x00 0x00 (FW ver?) → 応答: 81 01 00 02
送信: 0x1B 0x40 (ESC @) → 応答: なし
81 01 00 02 はおそらく汎用エラーレスポンス。
コマンドの内容に関わらず同じ応答ということは、デバイスが認証かセッション確立を要求していて、それが通るまで個別のコマンドを処理しない設計と考えられる。
BLE単体でのブラインドプローブではこれ以上の情報が得られない。
Bluetooth Classic(RFCOMM)接続
GitHubで見つけた tylerhahn/ivy2-sprocket2(Canon IVY 2ドライバーのHP Sprocket 2フォーク)がRFCOMM接続を使っていたので、BT Classicでも試した。
SDP(サービス検出)
blueutil でペアリング後、IOBluetooth経由でSDPレコードを取得。
| サービス名 | RFCOMMチャンネル | UUID |
|---|---|---|
| (カスタム) | 1 | 00000000-DECA-FADE-DECA-DEAFDECACAFF |
| SPP SERVER | 2 | 0x1101(SerialPort) |
RFCOMM接続結果
Channel 1(カスタムUUID): 接続成功。MTU 1006。
接続した瞬間から約1秒間隔で FF 55 02 00 EE 10 の6バイトが送られてくる。
<< FF 55 02 00 EE 10 (約1秒おき、永続的に送信)
これはステータスビーコン。何を送っても変化しない。
ivy2プロトコルのスタートコード 0x430F を含むコマンドを送っても無反応で、ビーコンの内容は変わらなかった。
FF 55 がメッセージヘッダーで、残りの 02 00 EE 10 はステータス情報と思われる。
紙なしの状態なので、EE や 10 がエラーコード(紙なし等)を示している可能性がある。
Channel 2(SPP SERVER): 接続成功。MTU 1006。
何を送ってもデータ受信なし。
ivy2プロトコルとの互換性
ivy2-sprocket2のREADMEには「May need protocol adjustments」とあり、実機テストされた形跡がない。
実際にSprocket 200は 0x430F スタートコードに反応せず、独自の FF 55 プロトコルで通信している。
Canon IVY 2とはプロトコルレベルで互換性がないと考えられる。
プロトコル解析
BLEパケットキャプチャと既存のリバースエンジニアリング情報から、HP Sprocket 200のBLE通信プロトコルを解析した。
BLEフレーミング層
GATTの上にBLE独自のフレーミング層がある。
各BLEパケットの先頭1バイトがフレームヘッダーで、残りがペイロード。
byte[0] = [last_flag:1][sequence:7]
byte[1:] = HPLPPメッセージの断片
last_flag(bit 7): 1なら最終パケットsequence(bit 0-6): 1から始まるシーケンス番号- MTU内に収まるメッセージは単一パケットで
0x81(seq=1, last=true)始まり
これで先ほどの 81 01 00 02 の謎が解ける。
81 → BLEフレーム: seq=1, last=true
01 00 02 → HPLPPメッセージ: コマンド=ERROR(0x01), ペイロード=00 02
デバイスはセッション確立前のコマンドにすべてERRORを返していた。
HPLPPプロトコル
HP Sprocket 200は内部コードネーム「IBIZA」で、HPLPPプロトコルを使用する。
旧機種(初代Sprocket等)が使うMantaパケット(スタートコード 0x1B2A)やivy2プロトコル(0x430F)とは別物。
HPLPPメッセージの先頭1バイトがコマンドコード。主要なコマンドは以下の通り。
| コマンド | コード | 方向 | 用途 |
|---|---|---|---|
| IF_CONFIG_REQ | 0x0A | → | BLEインターフェース設定要求 |
| IF_CONFIG_RSP | 0x0B | ← | MTU・ACK周期を返す |
| RD_STATUS_REQ | 0x08 | → | ステータス読み取り |
| RD_STATUS_RSP | 0x09 | ← | バッテリー・印刷状態等 |
| RD_SYS_ATT_REQ | 0x04 | → | デバイス属性読み取り |
| RD_SYS_ATT_RSP | 0x05 | ← | FW版・シリアル等 |
| PRINT_START_REQ | 0x0C | → | 印刷開始(ファイル長を送信) |
| PRINT_START_RSP | 0x0D | ← | ジョブID・ファイルハンドルを返す |
| FILE_WRITE_REQ | 0x0E | → | 画像データ送信(チャンク) |
| FILE_WRITE_RSP | 0x0F | ← | 書き込み確認 |
| ERROR | 0x01 | ← | エラーレスポンス |
セッション確立
BLE接続後、最初に IF_CONFIG_REQ を送る必要がある。
これがないとデバイスは全コマンドに ERROR を返す。
送信: 81 0A 01 00
^^ BLEフレーム (seq=1, last)
^^ IF_CONFIG_REQ (0x0A)
^^ BLEインターフェース (0x01)
^^ ACK周期 = 0
受信: 81 0B 01 02 02 00
^^ BLEフレーム
^^ IF_CONFIG_RSP (0x0B)
^^ ^^ MTU = 258
^^ 上り ACK周期 = 2
^^ (パディング)
デバイス情報の読み取り
セッション確立後、ステータスとデバイス属性を読み取れる。
送信: 81 08 01 02 03 06 09 (RD_STATUS_REQ + フィールドID)
| フィールド | 値 |
|---|---|
| SYSTEM_FLAGS | 0x05000000 |
| PRINT_STATUS | 16(紙なし) |
| BATTERY_LEVEL | 100(USB給電) |
| BATTERY_STATUS | 1(充電中) |
| NUM_HOSTS | 0 |
送信: 81 04 01 02 03 05 09 0A 0B 0C (RD_SYS_ATT_REQ + フィールドID)
| フィールド | 値 |
|---|---|
| SW_VERSION | M1L1FA1839AR |
| PROTOCOL_VERSION | 256 |
| NAME | HP Sprocket 200 (8C:2D) |
| SERIAL | TH8B93225J |
| SUPER_MODEL | 0x6249 |
| SUB_MODEL | 0x0600 |
| BT_MAC | F4:39:09:D2:8C:2D |
| HW_VERSION | 4138 |
PRINT_STATUS = 16 は紙なしのエラーコード。
紙を入れればこの値が変わり、印刷可能状態になるはず。
印刷フロー
通信キャプチャから推測される印刷シーケンスは以下の通り。
graph TD
A[BLE接続] --> B[IF_CONFIG_REQ]
B --> C[IF_CONFIG_RSP<br/>MTU=258]
C --> D[RD_STATUS_REQ]
D --> E{PRINT_STATUS<br/>確認}
E -->|紙なし| F[エラー: 印刷不可]
E -->|OK| G[PRINT_START_REQ<br/>ファイル長を送信]
G --> H[PRINT_START_RSP<br/>ジョブID + ファイルハンドル]
H --> I[FILE_WRITE_REQ<br/>JPEGチャンク送信]
I --> J[FILE_WRITE_RSP]
J -->|残りあり| I
J -->|完了| K[印刷実行]
画像はJPEG形式、解像度668x1002ピクセル、quality=90で圧縮してチャンク分割で送る。
紙なしで確認できたこと
| 項目 | 結果 |
|---|---|
| BLE接続 | OK |
| セッション確立(IF_CONFIG) | OK |
| デバイス情報読み取り | OK(FW、シリアル、MAC等) |
| ステータス読み取り | OK(PRINT_STATUS=16 = 紙なし) |
| プロトコル特定 | HPLPP(HP独自、コードネーム IBIZA) |
| 印刷コマンド | 特定済み(PRINT_START → FILE_WRITE) |
紙を入れてPRINT_STATUSが変わることを確認できれば、あとはJPEGを送るだけ。
印刷テスト
テスト用の画像を用意した。

Sprocketの印刷仕様に合わせてJPEG 668x1002px、quality=90に変換済み。
紙をセットしてステータス確認
Canon iNSPiC用のZINKペーパーをHP純正のスマートシートと一緒にセットした。
PRINT_STATUSが16(紙なし)から1(印刷可能)に変化。
PRINT_STATUS: 1(印刷可能)
BATTERY_LEVEL: 94
BATTERY_STATUS: 1(充電中)
バイトオーダーの罠
最初の送信は失敗した。
HPLPPプロトコルの整数値はすべてリトルエンディアンで、ファイル長をビッグエンディアンで送っていたのが原因。
# NG: ビッグエンディアン
struct.pack('>I', 146002) # → 00 02 3A 52
# プリンターはLE読みで 0x523A0200 = 1.3GB と誤解
# OK: リトルエンディアン
struct.pack('<I', 146002) # → 52 3A 02 00
# プリンターは正しく 146002 バイトと認識
ファイル長が間違っていたため、プリンターは全データ受信後もSTATUS_COMPLETEを返さず、永遠に続きのデータを待ち続けていた。
転送成功
バイトオーダーを修正した結果、FILE_WRITE_RSPで正しく進捗が返るようになった。
FILE_WRITE 146002 bytes (chunk=500B)
15000/146002 (10%) recv=15000 total=146002
30000/146002 (20%) recv=30000 total=146002
...
135000/146002 (92%) recv=135000 total=146002
COMPLETE! 146002/146002
Transfer: 293 chunks
STATUS_COMPLETE(ステータスコード2)が返り、プリンターが全データの受信完了を認識した。
印刷結果

転送完了後、プリンターが自動で印刷を開始した。
紙が排出口からゆっくり出てくる。

PCからBLE経由でHP Sprocket 200への直接印刷に成功した。
Canon iNSPiC用の紙でも問題なく印刷できたが、色味が若干くすんでいる。
これは他社製ZINKペーパーを使った場合のあるあるらしく、スマートシートによるキャリブレーションが純正紙に最適化されているため、互換品だと発色がずれるとのこと。
印刷自体は問題ないので実用上は許容範囲だが、色にこだわるならHP純正紙を使ったほうがいい。

同じ画像を同じプリンターで印刷した比較。左がHP純正紙、右がCanon iNSPiC用紙。
並べてみるとなんとなく色味が違う。純正のほうが若干落ち着いた発色に見える。
劇的な差ではないが、スマートシートのキャリブレーションが純正紙向けに調整されている以上、互換品で多少ずれるのは仕方ない。
転送速度について
143KBのJPEGを500Bチャンク×293回で送信しているので、転送速度はかなり遅い。
チャンクごとにFILE_WRITE_RSPの応答待ちが入るため、体感で1分弱かかる。
チャンクサイズを大きくすれば改善するはずだが、BLEのMTU上限やACK周期との兼ね合いがあるのでチューニングが必要。
とはいえ2x3インチのZINKペーパーに印刷するような用途でスピードが問題になることはあまりない。
ちなみにこの個体、「バッテリーが死んでいるのでUSB給電必須」と思い込んで実験していたが、何枚か印刷したらバッテリー単独で動くようになった。
充電しながら使い続けたおかげでバッテリーが復活したらしい。ジャンク品の当たり個体だった。