Welcome to telecotele.com » Projects

家庭用コーヒーメーカーの解析とHTCPCPの実装

家庭用コーヒーメーカー内部で使われるI2Cの通信を解析しHTCPCPで操作します。またBLEから操作可能な後継機のプロトコルも解析します。

UPDATE

tags: barista

家庭用コーヒーメーカーの解析とHTCPCPの実装のカバー画像

この記事は下記で寄稿した記事を2024年に再構成したものです。

はじめに

皆さんはコーヒーという飲み物をご存知でしょうか? 情報技術との関わりとしては、Trojan Room Coffee Pot (Wikipedia)こんな色をした#C0FFEECoffeeと名がつくスクリプト言語が存在すると聞いたことがあります。 ちなみに、コーヒーでは原料の豆を動物の排泄物から得ることや、それ自体を浣腸に利用することもあるそうです。 そう聞くとなんとなく色もそれらしい感じになっていそうです。 もしかして…茶色や黒色っぽい色をしているんじゃないでしょうか?

こうしたコーヒーを簡単に淹れるには、コーヒーメーカーと呼ばれる製品が使われるそうです。 中でも家庭用コーヒーメーカーとしてはバ○スタという製品があります。 さらに、このようなコーヒーメーカーをコンピュータネットワークから制御するためのプロトコルとしてHTCPCPがあります。 ネットワークからマシンを制御なんてIoTがDXしているんじゃないでしょうか? バ○スタでHTCPCPを実装できると面白いと思いました。

そこで本稿では、登場人物の紹介として概要を述べ、続いてバ○スタにおける内部構造の解析、それを踏まえたHTCPCPの実装について述べていきます。

登場人物の紹介

バ○スタとHTCPCPについて概要を述べます。

バ○スタについて

バ○スタとは何でしょう?武器?保護素子?うさぎになる人?ば○☆すた?左室部分切除術? 漆黒のカーテン引きちぎり?違う空を見てみたい? いろいろ思いつきますが、ここではネスカフェ バリスタのことを指します。 これは水と専用のインスタントコーヒーパックをセットし、ボタンを押せば全5種のうちいずれかのコーヒー1杯が抽出できるマシンです。 2016年時点で、以下の4機種が販売されています。

  • PM9630
    • 初期のマシンで、主電源ボタンがある。海外ではオフィス向けにNESCAFÉ Alegria A510として販売されていた。
  • PM9631
    • PM9630の後継機、また後述するバリスタ TAMAの廉価版となる。
  • バリスタ TAMA
    • PM9630/PM9631よりも丸みを帯びたデザインになった。操作パネルも導電ゴム製のパッドからタッチパネルに変更されている。
  • バリスタアイ
    • バリスタ TAMAを基に、Bluetooth 4.0から操作できるよう改良された。

このうち、今回はPM9631バリスタアイについて注目します。 理由は自宅にあったからです。結論!

HTCPCPについて

HTCPCP (Hyper Text Coffee Pot Control Protocol)についてです。 仕様はRFC2324として公開されています。 このRFCは1998年4月1日のエイプリルフールに公開されたジョークRFCです。

RFC2324によれば、HTTP1.1 (RFC2068)を拡張した仕様とすることが述べられています。 その抜粋を次に示します。

  • URIスキームはcoffee:
    • コーヒーは国際的な飲み物なので、29ヶ国語のURIスキームもサポートする。これには、日本語の%E3%82%B3%E3%83%BC%E3%83%92%E3%83%BC(コーヒー)も含まれる。
  • BREWメソッドを追加する
    • サーバに、BREW(抽出)メソッドかPOSTメソッドを用いてContent-Type: application/coffee-pot-commandというヘッダを付けたリクエストを送るとコーヒーが抽出される。このBREWメソッドはHyper Text Brewery醸造所 Control Protocolでも用いられる可能性がある。
  • PROPFINDメソッドも追加する
    • WebDAVで用いられるPROPFINDメソッドを用い、コーヒーのメタデータが得る。
  • WHENメソッドも追加する
    • コーヒーにミルクが注がれる場合、「そこまで」と伝えるためWHENメソッドを用いる。
  • HTTPステータスコード418の追加
    • 418 I'm a teapot. HTTPステータスコードに自分はティーポットだと示すコードを追加する。

以上です。

バリスタ (PM9631) の解析

HTCPCPを実装するにあたって、バリスタを電子的に操作することが必要です。

まず思いつくのは、ソレノイドやサーボモータのようなアクチュエータを用いて人間をまね物理的にボタンを押す、そのアクチュエータはプログラムから操作する、といった方法です。 とはいえ、これはそんなにうまくいくでしょうか? 実際やるとなれば、アクチュエータ自体の選定、固定方法、ボタンを押す強さ、アクチュエータが故障した場合の対応…など考える必要があります。

SwitchBotのアレを粘着テープで張り付ければ?動かなかったら誰か代わりにボタン押してもらったらええんや! というのもアリかも知れませんが、より簡単・確実に操作できる方法があるかも知れません。 そこで、まずはバリスタがどのように動作しているのか調べます。

バリスタの内部構成

コーヒータンクの下にある4本のトルクスネジを外し、ツメで止まっているサイドパネルをバキバキと鳴らしながら開けると、次の図に示すメイン基板があります。

バリスタ (PM9631) のメイン基板 バリスタ (PM9631) のメイン基板

基板の汚れ、左右反転1は私のせいです。 このメイン基板ではSTM8S105K6T6Cマイコンによってヒーターやバルブの制御を行っています。 STM8Sの電源電圧は5Vで、電源回路からXHコネクタを通じて供給されるDC12Vを三端子レギュレータによって降圧しています。 また、DEBUGという直球の端子がありますが、STM8Sに備わったSWIMインターフェースと思われます2。 後述する操作パネルとも、PHコネクタによって接続されています。

さらに、操作パネルをベコッと外すと、導電ゴムで作られたボタンの下に次の基板があります。

バリスタ (PM9631) の操作パネル バリスタ (PM9631) の操作パネル

基板の汚れ3と画像加工はやはり私のせい、R1が実装されていない4のも私のせいです。 この基板では、どのボタンが押されたかをメイン基板へ伝えています。 また、LEDを備えておりどのボタンが押せる状態か、また何かあった場合にはLEDを点滅するなど人間に機器の状態を伝える役割もあります。 ここでもSTM8Sが用いられデバッグ機能が封じられたSWIMインターフェースもありますが、型番はSTM8S003K3T6Cでメイン基板と異なります。 メイン基板のほうが少し高級な製品を使っているようですが、RAM/ROMの容量やPWM/タイマーのチャネル数が異なる程度で、大きな違いはありません。

なお、どちらの基板もコネクタやピンについてわかりやすい説明がシルク印刷によってなされています。 Sapporo2など一見意味が分からない箇所もありますが、開発を請け負ったと思われるSDATAWAY社がネスレ本社と同じスイスにあること、バリスタは日本法人による提案であることを踏まえれば、 「日本といえば首都は札幌。未来の首都札幌!天下を取るでよ~」という思いから付けられた開発コードネームではないか、そうでなくとも札幌にゆかりのある支店?人物?から提案されたのではといった予想も立ちます5

メイン基板と操作パネルの接続

次の図に示すメイン基板と操作パネルの接続部分では、コーキングを取り除くと +5VIRQGNDSDASCLといったシルク印刷が確認できます。 ピンアサイン ピンアサイン

SDAとSCLがあることから、メイン基板と操作パネルはI2Cで通信しているのではないかと推察されます。 I2Cとは、あるマスターデバイスと1つ以上のスレーブデバイスの間で通信できるシリアルバス規格です。 通信は常にマスターから開始されるため、今回の場合は(基本的には)入力デバイスである操作パネルがマスターになっている?かも知れません。

一方で「水タンクが空」などの異常が発生した場合、それをLEDを通じて人間に伝えられるのは操作パネル側だけです。 そのため、メイン基板がスレーブだと不都合があるのでやっぱり操作パネルをスレーブとしてみましょう。 すると今度は人間からの入力をメイン基板へ伝えられません。 どうすれば良いのでしょうか?

そこで用いるのが割り込み線となるIRQです。 スレーブからマスターに声をかけて通信を開始してもらえれば解決します。

とはいえ、本当にI2Cを使っているのか、またメイン基板と操作パネルのどちらがマスター・スレーブなのかはまだ分かりません。 ロジアナで観測しながら、操作パネルからバリスタの電源をOFFにすると次の図に示す信号がやり取りされていました。

操作パネル内の「電源ON/OFFボタン」を押してOFFにしたときの信号 操作パネル内の「電源ON/OFFボタン」を押してOFFにしたときの信号

分かりにくいかも知れませんが、HIGHレベルだったIRQがLOWレベルになって約40ms後にデバイスアドレス0x58に対するI2Cのメッセージが流れています。 よって、確かにI2Cが利用されていることが分かりました。 さらに、操作パネルはIRQを出さなければいけない立場、すなわちスレーブであると推察されます。 また、別途テスターを用いてI2Cバスの電圧を測定したところ、SCLとSDAともにメイン基板で5Vにプルアップされていると分かりました。

I2Cメッセージの解析

コーヒーを淹れる際やエラーの場合、どのようなメッセージが流れているのか確認します。 解析を進めると、操作パネルのボタンを押した際の挙動は次の図のとおりになっていました。

I2C通信の流れ I2C通信の流れ

ボタンIDは、次の表のとおりです。

ボタンID (Hex表記)ID (Bin表記)
電源ON/OFFボタン0x010b00000001 (== 1<<0)
エスプレッソ0x020b00000010 (== 1<<1)
カフェラテ0x040b00000100 (== 1<<2)
カプチーノ0x080b00001000 (== 1<<3)
ブラック(マグサイズ)0x100b00010000 (== 1<<4)
ブラック0x200b00100000 (== 1<<5)

このやり取りでは、最後にメイン基板から送られてきたデータが送られて終了します。 操作パネル側ではこのデータをメモリに格納し、次の表(ヘッダはメモリアドレス、空欄はDon’t careや未確認箇所)と突き合わせてバリスタ本体の状態を把握しているようです。

0x100x110x140x150x160x170x180x190x20状態
--------0x00電源がOFF
--------0x01電源がON
0x010x00-------正常
0x000x01-------異常
---0x010x010x010x010x01-待機中
---0x020x000x000x000x00-エスプレッソ抽出中
---0x000x020x000x000x00-カフェラテ抽出中
---0x000x000x020x000x00-カプチーノ抽出中
---0x000x000x000x020x00-ブラック(マグ)抽出中
---0x000x000x000x000x02-ブラック抽出中
0x010x000x01------水がわずか(まだ抽出は可能)
0x000x010x02------水がない(異常扱い)
0x000x01-0x02-----上部カバー開放中(異常扱い)
0x000x01----0x02--ドロワー開放中(異常扱い)

水が空になったなど操作に関わらず発生した通信は、最後のメイン基板から操作パネルへの通信のみが行われます。 また、0x80をデータとするWriteと直後のRead、それに対する0x07の返送もあります。 常に同じ値で、発生タイミングもよく分かりませんが、もしかすると操作パネルのバージョンを確認しているのかも知れません。

ちなみに「動作中に突然コンセントプラグを抜いた」などで正しく終了できなかった場合、その次に起動するときは電源ボタンを押してもいきなり終了を表すメッセージが送信されます。 この際は、もう一度電源ボタンを押すのと同じ処理を行うと電源が入ります。

HTCPCPの実装

バリスタの動作を解析したことにより、I2Cから電子的に操作できると判明しました。 それを踏まえ、バリスタにHTCPCPを実装します。

仕様検討

まず、操作パネルをオレオレ操作パネルに置き換えます。 このオレオレ操作パネルはHTCPCPのメッセージを受け、その内容に応じてメイン基板をI2Cで操作する構成にします。

その他の仕様は以下のとおりとします。

  • Content-Typeを気にしない
    • 寛容に、また簡単のため、Content-Typeを全く考慮せずHTCPCPを実装します。
  • coffee:なんてスキームはない
    • HTCPCPクライアントを作成したりnetcatでHTCPCPリクエストを流し込まずとも、curlから叩ければそれで十分と考えられます。遠隔から淹れているぞ!ということが分かれば満足です。ただ、curlにcoffee:スキームを理解させるのは手間がかかるので簡単のためスキームはhttp:とします。
  • 時刻同期はしない
    • HTCPCPでは正確な時刻を持つべきとされます。しかし、やはり簡単のため無視します。
  • 元々の操作パネルは接続しない
    • 元々のパネルからも操作できると便利と考えられます。ただ、この場合はオレオレメイン基板も実装する必要があり、手間がかかりそうなので無視します。
  • カップの有無は気にしない
    • バリスタはコーヒーを抽出する際にカップの有無を確認せず注ぎます。通常時は人間が確認すれば良いので問題ありませんが、遠隔から操作するとカップが無いのに注がれる事例が増えそうです。しかし、簡単のためこの辺は考えないこととします。

ソフトウェアは、この仕様と解析結果に基いて実装します。

また、ハードウェアについては、TCP/IPで通信できる必要があります。 そこで、技適が通っているWiFiモジュールESP-WROOM-02を用います。理由は600円程度と安価な上に、Arduinoとしてプログラミングできて簡単にTCP/IPが使えるからです。

しかし、ESP-WROOM-02のI2Cはソフトウェア実装です。 スレーブの実装例もいくつかあるようですが、マスターならともかくスレーブではかなり不安があります。 そのため、スレーブをハードウェアで実装しているLPC1114を別途用意してメイン基板との通信を任せ、両者をUARTで接続します。 ここでは大した情報は送らないため、人間に優しくエスプレッソ抽出ならE、カプチーノ抽出ならCなどASCII印字可能文字1つだけとしています。

ここまでの構成を図にまとめると、次の図のとおりとなります。

構成図 構成図

実装

あらかじめ実装しておいたものがこちらになります。

lrks/HTCPCPwithBARISTA
https://github.com/lrks/HTCPCPwithBARISTA

ESP-WROOM-02はArduino、LPC1114はmbedとして実装しました。

実装時、I2Cのデータが化けが起こり苦労しました。 このような場合、どこかが短絡していること、プルアップ抵抗の値が適切でないことを疑うべきだそうですがそうした気配はありません。

ロジックアナライザで観測し続けると、SCLがLOWのままで変化していないのにSDAが変化していることが分かりました。 ノイズかも知れませんが「誰かがSCLをLOWにしている」「でも別の誰かはSCLが正しく出ていると思ってSDAを変化させている」?と考えられます。 実際、クロックストレッチングという機能により、 スレーブがSCLをLOWにしてマスターがこれを検知することで、スレーブからマスターへ処理を待つように伝えられるそうです。

ただ、クロックストレッチングはI2Cのオプション機能なので、対応せずとも問題ありません。 そして、メイン基板はこれに対応していませんでした。 この場合、スレーブでは迅速に、次のクロックやメッセージが到着するまでに処理を完了させる必要があります。

LPC1114によるI2Cスレーブの実装では、ポーリングによってマスターからの通信を待っています。 さらに、開発当初はデバッグを目的として、どのようなメッセージが来たかをUARTで送信する処理を行っていました。 しかし、このせいでmsオーダーの遅延が発生し、usオーダーで応答しなければならないI2Cでは都合が悪かったようです。 この辺は実装を見直すことで、正常にI2Cでやりとり出来ました。

実際にHTCPCPで操作している際の様子は次のとおりです。

HTCPCP実装の様子 HTCPCP実装の様子

動画ではきちんと指示通りにエスプレッソが抽出されています。 部屋が薄暗くて分からない? えーきちんと指示通りにエスプレッソが抽出されています(ダメ押し)。

バリスタアイの解析

バリスタ (PM9631) でHTCPCPを実装できたのでよかったよかった…と思いましたが、続いてバリスタアイも解析してみたくなりました。 なので、具体的にはBLEの解析をしてみます。(HTCPCPの実装はしません)

スニファリングBLE

バリスタアイではBLE (Bluetooth Low Energy)のインターフェースを備えており、 ネスカフェが提供する専用のiOS/Androidアプリ経由で操作可能です。

そこで、アプリとバリスタアイの通信内容を見てみましょう。 ここではAdafruitやスイッチサイエンスで売られている「Bluefruit LE Sniffer」というBLEスニファを利用してみます。

結果、普通に通信は見れましたが…やりとりされるメッセージがよく分かりません。 後で判明したことですが、通信内容は暗号化6されておりこのスニファリングにより得られるものはあまり無かったです。

アプリの解析

スニファではよく分からなかったので、アプリを解析して通信の流れを追います。 iOSよりもAndroid版のほうが解析しやすいため、そちらで確認します。

見たところ、Bluetooth経由でFWを更新する機能も備えているようです。 そして気になるのが関数名にEncryptedNewFWが付いているものといないものがある、またlightpairingという文字列が頻出することです。 そういえば、アプリとバリスタアイを最初にリンクさせるとき、ペアリングがなんとかっていわれたような気がします。

BLEの通信内容

こうして解析を進めていくと、結果的に下記のことが分かりました。

  • バリスタアイにはFWバージョンが複数あると思われる
  • 少なくとも手元のSP07というバージョンにおいては、暗号化されたメッセージを送らないと操作できない
  • 暗号化に必要な鍵はlightpairing(アプリとバリスタアイを最初にリンクさせるときの手順)を通じて取得する

また、lightpairingとその後の操作の流れは次の図のとおりです。

lightpairingと操作の流れ lightpairingと操作の流れ

“Encrypted Machine Password” と “Encryption Key” はアプリ側に保存でき使い回せるので、一度lightpairingしておけばもっと簡素に操作できます。

ボタンIDは次の表に従います。

ボタンID
電源ON/OFFボタン1
エスプレッソ4
ブラック8
ブラック(マグサイズ)16
カプチーノ32
カフェラテ64

暗号文の詳細は後述しますが、これは “Encrypted Token” と “Encrypted Machine Password”, “Encryption Key” から計算される値です。

じゃあ、lightpairingを傍受されてしまうと認証の意味がなくなるのでは? あまりlightpairingの瞬間に遭遇しないとは思いますが、もしリスクが大きいと判断されればよりセキュアな方式を利用したFWに更新されるかも知れません。 なお、一度送った「ボタンを押すメッセージ」はそのまま再送しても無視されるのでリプレイ攻撃は効かないようです。

また、(少なくともこのFWバージョンでは)BluetoothマークのLEDを制御するコマンドWrite 0x0038 ID7はなぜか認証無しで通るようでした。

CLIからの操作

先ほどの通信手順を用いてCLIからバリスタアイを操作します。

操作には、まずバリスタアイのBluetoothデバイスアドレスが必要です。 hcitoolでデバイスをスキャンします。

$ sudo hcitool lescan
LE Scan ...
XX:XX:XX:XX:XX:XX (unknown)
XX:XX:XX:XX:XX:XX バリスタ アイ
 :

「バリスタ アイ」というド直球なデバイスがありますね。おそらくこれでしょう。 XX:XX:XX:XX:XX:XXでマスクしていますが、アドレスはこれです。

次に、このデバイスに対してgatttoolで「lightpairingと操作の流れ」に従ったコマンドを発行します。

$ sudo gatttool -b 'XX:XX:XX:XX:XX:XX' -I
[XX:XX:XX:XX:XX:XX][LE]> connect
Attempting to connect to XX:XX:XX:XX:XX:XX
Connection successful
[XX:XX:XX:XX:XX:XX][LE]> char-read-hnd 0x0028
<ERROR>
[XX:XX:XX:XX:XX:XX][LE]> char-read-hnd 0x0030
<Encrypted Token>
[XX:XX:XX:XX:XX:XX][LE]> char-write-req 0x0030 <暗号文>
<以下略>

暗号文を作成するコードはこのgistのとおりです。 画像も動画も一切ないので何のことか分かりませんが、きちんと抽出されています。 本当に?えー本当にきちんと抽出されています(ダメ押し2)。

ここでは全手動で操作しましたが、ちゃんとやれば本当にコマンド一発での抽出もできることでしょう。

おわりに

バリスタのI2CとBLEを解析し、一部でHTCPCPを実装しました。

I2Cの解析とHTCPCPの実装は2016年に行ったもので、正直細かいところまでは覚えていません。 文章や画像はだいたい当時のままですが、「はじめに」の書き出しがなかなか良いと思っています。 書き出しオブジイヤーです(PNPN)

BLEの解析は2019年に実施し、まだ多少記憶は残っているような…いやないような…気がします。 当時BLE周りやhcitoolgatttoolを初めて触ったので勉強になってよかった。

Footnotes

  1. パターンを追いやすくするため、表面・裏面のviaやスルーホールの位置が重なるような射影変換もかけています。

  2. ST-LINK/V2を買い接続を試みたものの、デバッグ機能は封じられているようでした。いまやるとしたらVoltage Fault Injectionを試すかも…いや当時でも結局試そうとするだけで終わっていたかも知れません。

  3. ケーブルの切れ端は何…?と気になるかも知れません。それはこの解析より前に実施した「ボタン押下検知の電極を短絡させて電子的にボタンを押したことにする」を試した結果です。これはこれで普通に出来ましたが…I2Cの解析に興味がありました。

  4. 作業中に取れてしまったようです。その後ちゃんと新たな抵抗を付けました。

  5. 信じるか信じないかはあなた次第です。

  6. 実はこのバリスタアイの解析については先行事例があり、BLEの通信見るだけでなんとかなるだろうと思っていました。詳細は後述しますがFWバージョンによっては平文でいけるものもあり、先行事例では平文で操作できていたようです。

  7. IDは0~3までの数値で、それぞれ「LED消灯」「点灯」「低速点滅」「高速点滅」に割り当てられています。