(2002.11.13更新) | |
(2003.12.31更新) | |
(2003.12.31更新) | |
(2005.07.22更新) |
無線 LAN PC カードをマイコンに接続して、改めて Bluetooth の魅力に引き込まれました。数十mA という Bluetooth モジュールの消費電力の低さは電池駆動でも安心ですし、モジュールと CPU との間の I/F は USB か UART となっているため、UART 接続なら非常に簡単にマイコンから制御が可能です。これで PCMCIA I/F の半田付け 68本ノックともオサラバ出来ます。
Bluetooth に関しては、これまでもトラ技や Interface 誌にて幾度か紹介されています。周波数ホッピングとかスペクトル拡散とか Bluetooth パケットのフォーマット等の情報が書かれています。しかしこれらの情報は Bluetooth モジュールを作る人には重要なのかもしれませんが、モジュールをマイコンとつなげて使いたい自分には余り役に立たない情報でした。モジュールとどんなやり取りをすれば無線通信が出来るようになるのか、雑誌の記事を読んでも全然分からない、そんな状態が続いていました。
そんなモンモンとしている時にめぐり合ったのが京セラ H" 端末用 Bluetooth モデム BL-PC10 の 1,980円投売り通販でした。このモデムユニットは Ericsson の Bluetooth モジュールが使われていることがパッケージに記載されており、以前から目をつけておりました。Ericsson のモジュールは UART I/F を持っており、マイコンや PC との接続も容易です。あまりの安さに、迷わず大量購入をしてしまいました。
早速モデムユニットと PC カードを分解し、Ericsson Bluetooth モジュールを取り外しました。モジュールのピン配置は Ericsson の Web ページからデータシートを落として知ることが出来ます。現在は ROK 101 008 ではなく 007 の方しか落とせないようですが、ピン配は同一でした。BGA パッケージなのでハンダ付けに若干難儀しますが、接続するピン数も少ないので、細いポリウレタン線を使って引っ張り出しています。
モジュールの VCC / VCC_IO / ON の3つの端子に 3.3V を供給し、TxD / RxD を PC やマイコンと接続するだけで、このモジュールを制御することが出来るようになります。このままでは数 cm しか電波が飛ばないので、誘電体アンテナをモジュールと同じくモデムユニットや PC カードから取り外し、ANT と GND の2端子を接続します。
また分解してモジュールを取り外さなくても、BL-PC10 に付属する PC カードを使って Bluetooth モジュールの制御が可能です。BL-PC10 のドライバをインストールするとシリアルポートとモデムが追加されますが、このシリアルポートの方が、上記モジュールの TxD / RxD とそのまま繋がっています。inf ファイルを修正してモデムがインストールされないようにすると良いかもしれません。難点は Win9x ドライバしか用意されていない点でしょうか。
HCI なんて怖くない
さてこれで準備が整いました。肝心なのはモジュールをどう制御するのか、です。まず重要なのは仕様書です。Bluetooth 公式サイトの仕様書ページから v.1.1 CORE 仕様書を落とします。あとはこれをどう読みこなすか、です。英文だということもさることながら、必要な情報が分散しているので読むコツを掴むまでは時間がかかってしまいました。
Bluetooth では HCI というインタフェースの名前をよく聞きます。これが PC やマイコンで Bluetooth モジュールを制御したり、データを無線で送受信するための一番最下層のインタフェースになります。この HCI では Command Packet, Event Packet, ACL/SCO Packet という3種類のパケットが定義されており、ここを理解するのが最初の山でした。
右図のように、モジュールに対する指示が Command Packet です。モジュールはこのパケットを渡されると、コマンドの内容に応じて Event を返したり、無線で他のモジュールをやり取りをしたりします。他のモジュールと送受信するデータそのものは Data Packet によってやり取りします。また、無線によって他のモジュールから指示があった時には Event Packet がモジュールから通知されます。
これら3種類のパケットのフォーマットは CORE 仕様書の PartH:1 4.4 EXCHANGE OF HCI-SPECIFIC INFORMATION に書かれています。が、UART を通じてやり取りされるパケットの先頭には PartH:4 2 PROTOCOL の Table 2.1 に書かれている indicator が付加されます。ということで Command Packet をモジュールに送る時にはまず 0x01 を UART で送り、続いて 16bit の Opcode を送るということになります。またモジュールから UART を介してパケットを受信した時には、先頭が 0x04 なら Event Packet と、0x02/0x03 なら Data Packet だと解釈をします。
さて次は Data Packet で他のモジュールとデータのやり取りをするまでの手順です。Bluetooth モジュールは内部に state を持っており、決まった手順を踏まないと相手が見えない、相手から自分が見えない、となってしまいます。ザックリ書くと、(1)他のモジュールから見えるように Write Scan Enable Command でモジュールを設定する、(2)他のモジュールから接続要求があると Connection Request Event がモジュールから渡されるので、Accept Conenction Request Command をモジュールに伝える、(3)接続が完了すると Connection Complete Event がモジュールから渡される、です。これで相手のモジュールとの接続が完了し、これ以降は相手のモジュールと自由に Data Packet のやり取りが出来ます。
あとは Command をモジュールに送ると Command Complete Event や Command Status Event が返って来るのでそれを処理したり、Reset したりモジュール名を設定したりコマゴマとしたことを行なえば良いでしょう。実際には Data Packet を使って、HCI より上位の L2CAP とか SDP とか RFCOMM といったプロトコルに応じたやり取りをする必要があるのですが、とりあえずはこの辺でモジュール間でのやり取りをさせてみることにします。
イカサマ無線移動カメラをデッチ上げる
おなじみ C8051F300 に Treva と Ericsson Bluetooth モジュールをつなげます。そして L2CAP の一部まで実装したプロトコルスタックを上記仕様書を見ながら作成しました。このプロトコルスタックを blueme と名付けてみました。最初は PC 用と共用で作っていたのですが、力弱く途中から見事に分岐してしまいました。
C8051F300 はご存知の通り RAM が 256 バイトしか搭載しておりませんので、パケットの処理には難儀します。とりあえず UART 受信用に 32 バイトのバッファを用意し、プロトコルスタックの方ではバッファから1バイトずつ読んでは処理をするという形を取ることにしました。処理コードは冗長な部分がありますが、必要とあればパケット処理を幾らでも続けることが出来ます。
PC 側のアプリも VC++ にて作成しました。COM ポートの先に Ericsson モジュールが見えれば OK です。Open ボタンで選択した COM ポートをオープンし、Inquiry ボタンで他のモジュールを探します。他のモジュールが見つかったらモジュール固有のアドレスが表示されるので選択し、Connect ボタンを押すと接続します。
接続状態のときにスペースバーを押すと、Treva 画像転送開始コマンドが Bluetooth モジュールを介して C8051F300 に送られます。そして C8051F300 は Treva 画像を取得しながら Bluetooth モジュールを介して画像データを PC に送ります。1画面分の画像データが送られたら PC 側に画像が表示されます。Bluetooth とは呼べないイカサマなやり方ですが、とりあえず無線カメラの完成です。
調子に乗って C8051F300 に移動用の RC サーボを付けてみました。先程の PC アプリでカーソルキーを使って台車を動かせます。これでイカサマ無線移動カメラの完成です。処理がイカサマかつヤッツケなので、パケットをロストすると暴走してしまうシロモノですが、とりあえずカーソルキーで好きな所に移動させて、その場所の映像を PC に転送出来ます。それだけで、結構遊べます。楽しいです。
こんな感じで、アマチュアでもモジュールさえ入手出来れば、Bluetooth ゴッコで遊べるようになりました。Treva による画像遊びも楽しいですが、無線遊びも楽しいです。現状ではマイコン側 PC 側両方とも自作のアプリを使わないといけないのですが、Bluetooth のメリットは PC 側の環境が整っていることだと思うので、やはり L2CAP から上のプロトコルを真面目に実装しないとイカンなあと考えています。
イカサマプロトコルスタック blueme で Bluetooth モジュールを使った通信が出来るようになりました。しかしこれでは微弱無線モジュールを使うのと大差ありません。やはり Bluetooth で通信するからには、フツーの PC 相手に、市販の Bluetooth モジュール相手に、仮想シリアルポートで通信したいものです。しかし相手は L2CAP / SDP / RFCOMM の3段構えです。手強いです。勇気を持って前に進みます。Bluetooth Specification と Bluetooth テクノロジーへの招待、そして OpenBT や BlueZ のソースコードを頼りに進みます。
まず L2CAP です。Connect/Disconnect はイカサマプロトコルスタックでも簡易的に実装したので、次はパケット分割・結合問題をクリアする必要があります。HCI レイヤーでは length が 8bit ですが、L2CAP になると length が 16bit なので、パケットの分割・結合が必要となります。しかし将来のマイコン実装を考えると大きなパケットはどの道扱えません。そこで分割の必要なパケットは送らない、結合の必要なパケットが届いても無視することで、この問題にケリをつけます。
残る Configuration のやり取りをクリアすれば L2CAP は突破出来ます。ここは避けて通れません。L2CAP Connection Response 送信後に L2CAP Configuration Request が届くので L2CAP Configuration Response を返し、今度は L2CAP Configuration Request を送ってやれば L2CAP Configuration Response が返って来ました。Configuration Parameter Options の内容とかあまり考えなくても良かったのが救いでした。
SDP と格闘する
次に行く手を阻むのは SDP です。HCI は Little Endian なのに対してコイツは Big Endian だったりして、ちょっと毛色が違います。Bluetooth Specification を読んでも最初ピンと来ませんでした。まず UUID というのが分かりません。イロイロと調べていくうちに、このページに記載されている事が分かりました。
結局のところ、提供サービスを示したデータ列を持っていて、外部からの要求に応じてデータを返せばいいのですが、データ列の構造を理解するのに時間がかかりました。正確に言えば、まだ理解していません。このページの記述やら何やらを参考にして、何とか仮想シリアルポートを見せることに成功しました。
泣き泣き RFCOMM をデッチ上げる
正直言って段々ツラくなって来ました。イカサマでも何でも RFCOMM をデッチ上げて早くこの作業から抜け出したくなりました。ラスボスは RFCOMM です。手強いです。コイツは Bluetooth Specification だけ見ても分かりません。TS 07.10 と両方参照しながら理解する必要があります。この各レイヤ毎のバラバラさには正直泣きそうです。
C/R bit の扱い、DLCI の扱い、FCS の計算、色々とありましたがもう忘れてしまいました。とにかく何とか RFCOMM をデッチ上げ、PC から仮想シリアルポート経由で送られたデータを RFCOMM の UIH Frame を通じて受け取ることが出来るようになりました。この 受信データをそのまま UIH Frame で送り返すことで、Bluetooth を介したエコーバックに成功しました。
PC 上のアプリケーション、相手の Bluetooth チップやデバイスドライバも限定するという限られた環境ではありますが、当初の目標の仮想シリアルポートでの接続に成功しました。次はコイツをマイコンに実装してみることにします。
blueme による仮想シリアルポート接続成功の後、早速愛用している C8051F300 で動かす為に sdcc でコンパイルをしてみました。しかし必要 ROM サイズが約 12KB。これでは C8051F300 の内蔵 ROM 8KB には収まりません。必要 RAM サイズを 256byte 以内に抑えないといけないのは意識していたのですが、ROM サイズまでは考えていませんでした。他のマイコンに乗り換える等色々と考えましたが、一発逃げずに勝負することにしました。どうせ ROM サイズを減らすなら、目標は Keil C コンパイラ評価版制約の 4KB 以内にします。
まず blueme では RAM サイズを削減する為に、パケットの解析処理の中でシリアル受信を呼び出していました。これにより大きなパケットを受け取っても解析可能になると考えていました。しかし実際にはそれ程大きなパケットは送られてきていません。そこで思いきって最初のパケット長を受信した後パケット全体を RAM に格納し、パケット解析では RAM をアクセスすることで解析処理のサイズを減らすことにしました。今までは解析の結果残りは不要だと分かっていても全てを受信する処理が各所に散らばっていました。これをなくすることが出来たのが大きいです。
次に送信処理です。従来はシリアル送信関数の引数として送信データを都度渡していました。しかし固定データの送信については配列に格納し、これを送信することで送信処理呼び出し部分を減らすことにしました。勿論固定データの途中に変数値を送信する場合もあるので、そこは分割する必要がありますが、それでも ROM サイズ削減に繋がりました。
その他細かな対策を打つことで、どうにか 4KB の ROM に収めることが出来ました。RAM もスタック含めて C8051F300 の 256byte 内蔵 RAM だけで動かすことが出来ました。その後 sdcc での動作、OAKS16-MINI や OAKS8 への移植を行いました。Win2K/XP で動く OII 製のデバイスドライバとの相性が悪いという問題は有りますが、WIDCOMM 製ドライバと CSR/Broadcom チップの組み合わせではとりあえず通信出来るようです。ただヤッツケで作ったプロトコルスタックなので色々と不満はあります。もう一度言訳の無いものを作りたいと考えています。
bluemelt は泣き泣き作ったので色々と不具合があったり未実装な部分があったりします。その中で今回は RFCOMM 関連のバグをチョビっと修正してみました。RFCOMM UIH Frame の MSC Command 応答部分の修正と、DISC Frame への応答部分の追加です。
今までの blueme / bluemelt では、外部からの MSC Command に対する応答が間違っており、相手に正しい Command であると認識されていませんでした。但し MSC が正しくやり取りされなくても RFCOMM の Connect は行われるため、通信相手の実装が MSC Command への応答が返って来なくてもいい場合には問題とはなりませんでした。しかし Nokia 製携帯電話 702NK と RFCOMM で接続する場合、MSC Command への応答が無いためにタイムアウトとなり 702NK 側でエラーとなる不具合があり、応答データを見直した結果バグが見つかり修正を行いました。
また RFCOMM で相手からの Disconnect 要求となる DISC Frame への応答は今まで実装していませんでした。今までは相手のタイムアウトを待って Disconnect を完了させていました。これも結局は実装済みの UA Frame の返送を呼び出せば良いので、サクッと case 文を1行追加して応答処理を実装しました。
DIP マイコンでサクッと動作確認
動作確認はルネサスの R8C/15 で行いました。C8051 と Keil C コンパイラ評価版のような ROM サイズの制約も無く、RAM 容量も多くて DIP 20pin なので、非常にお手軽に Bluetooth を堪能できました。いつものようにループバックアプリケーションを動かし 702NK と Bluetooth 通信が行えることを確認しました。
かなりブランクが空いていましたが、段々と自分の書いたコードが読めてくる感覚は非常に楽しいものがありました。やはり、もう一度、言訳の無いナイスなプロトコルスタックを作りたいと思うようになってしまいます。思うだけで2年が過ぎたので言訳は出来ないのですが。それはともかく、今回のバグ修正の機会を与えて下さった TeamKNOx の皆様には深く感謝を致します。