本来ならば Bluetooth LE を使うところなのでしょうが、価格が高い(苦笑)なので安い方から手を入れていきます。
<b>連載</b>Bluetooth LE (5) Android 4.3 で Bluetooth LE 機器を使う (フェンリル | デベロッパーズブログ)
http://blog.fenrir-inc.com/jp/2013/10/bluetooth-le-android.html
Bluetoothでデバイスと通信するには | garicchi.com
http://garicchi.com/?p=17111
なところで、BLE を使っているのでいずれ追いつこうということで。手始めには、先に作ったモーターの駆動系を Bluetooth で制御していきます。同時に Raspberry Pi の Bluetooth シールドも買ったのですが、まあ、これは要らなかったかも。同じ HC-05 の変換モジュールだけで ok みたいです。
Bluetoothシリアル変換モジュール(マスタ/スレーブ) – Androciti Wiki
http://wiki.androciti.com/index.php?Bluetooth%A5%B7%A5%EA%A5%A2%A5%EB%CA%D1%B4%B9%A5%E2%A5%B8%A5%E5%A1%BC%A5%EB(%A5%DE%A5%B9%A5%BF%2F%A5%B9%A5%EC%A1%BC%A5%D6)
価格的には 1,500円ぐらいですね。スレーブ専用の HC-06 だと 1,000 円ちょっとなので複数使うときに良さそうです。
実は、BLE とそれ以前の Bluetooth の違いを知らなくて、去年 Xamarin のカンファレンスで SensorTag を見せてもらったときに「うーん、Bluetooth なのか」と思ってただけなんですよ。実は、グロサミで解説してもらった(英語だったけど)Rolling Spider も BLE を使っていて、それの SDK を使って Bluetooth 対応のコントローラーで動かした、ってのミソだったんですね…と今更思い直しました。SensorTag は試しに買ってみたので、到着したら試してみる予定です。あと、BLE 対応のシールドも。
シリアルポートでつなげる
とはいえ、Bluetooth に様々なプロファイルがあって、それを使えば様々なことができる、ってことは以前に Bluetooth を調べたときに分かってはいたのですが、その時には具体的な端末がなくて、オーディオ関連とかファイル転送をしてもなぁ、ってな感じでした。あまりハードウェアに詳しくなかったので、何かを制御するには WiFi ぐらいだろうと思っていたわけで、RealSense 絡みも WiFi で作っています。
が、とある人から、がりっちさんの RFCOMM 通信の記事を知って、ちょっと通信部分の制御を変えてみようかと思ったわけです。WiFi+RPi の組み合わせや、Bluetooth+RPi+PSコントローラーのみかと思っていたら、RFCOMM プロトコルでつなげれば、結構手軽に iPhone/Android から繋げられそうです。実は Windows からは Windows Windows ストアアプリから接続しないといけない(デスクトップアプリでもできるはずなんですけどね?)ので、あれこれとややこしいのですが、さっくりと繋がります。
COM ポートとして使えるので、そのまま適当なコマンドを作ってやり取りすれば自前のモーター制御/サーボ制御ぐらいはできそうです。
Bluetooth シリアル変換モジュール(HC-05)をArduinoにつなげる
HC-05 Arudino
RX — TX
TX — RX
GND — GND
VCC — 3.3V
につなげます。RX/TX が送受信でワンセットになっているのと電源用の VCC/GND を差すだけで十分です。これは Raspberry Pi の場合も同じなので、結構さっくりと認識するはずです。
ちなみに、Windows 8 から Bluetooth 機器につなげるときはペアリングが必須なんですが(Windows 10の場合は BLE に対応するので、ペアリングは必要ないらしい?)、Bluetooth 機器だけを認識させる場合は、電源の VCC/GND の間に 3.3V – 6V ぐらい流せば ok です。電波を弱くして Bluetooth 機器の近くに入ったよ、ってのは、これでで十分かもしれませんね(実際、BLE の使い方でそういうのはあると思う)。
Android 側のスケッチを作る
シリアルポートは1バイト単位で読み込むのですが、コマンドっぽく送受信したかったので8バイト単位で受信しています。このあたりはマインドストーム EV3 を制御するときも同じみたいですね。こっちもいずれやってみたいところです。
int ledPin=13; int mPin = 8; // モーター void setup(){ pinMode(ledPin,OUTPUT); pinMode(mPin,OUTPUT); Serial.begin(9600); } void loop(){ int n = Serial.available(); if ( n >= 8 ) { int ch[8]; int i; // 8バイトずつ読み込む for ( i=0; i<8; i++) { ch[i] = Serial.read(); } // コマンドを判別 if ( ch[0] == 'm' ) { if ( ch[1] == '1' ) { if ( ch[2] == 'o' && ch[3] == 'n' ) { // pin8 on digitalWrite(mPin,HIGH); } else { // pin8 off digitalWrite(mPin,LOW); } } } // 通信時に光らせる digitalWrite(ledPin,HIGH); delay(300); digitalWrite(ledPin,LOW); // エコーを送信 for ( i=0; i<8; i++) { Serial.write(ch[i]); } } } [/code] <ul> <li>m1on でモータ-をON</li> <li>m1off でモーターをOFF</li> </ul> <p> な単純な仕組みです。最終的には、モーター制御とアームのサーボ制御も入れたいのと、対物センサーの受信もいれておきたいですね。ここまで来ると、素直に BLE にしたほうがいいような気もするのですが、まあ、HC-05 モジュールが安いので。 </p> <p> <h2>Windows ストアアプリから制御する</h2> </p> <p> Windows 8 の制限から、ストアアプリから制御します。たぶん、Windows Phone からも同じ制御ができるはずです。 </p> [code lang="csharp"] public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } // RFCOMM のサービスID Guid serviceGuid = Guid.Parse("00001101-0000-1000-8000-00805f9b34fb"); RfcommDeviceService rfcommService; StreamSocket socket; DataWriter writer; DataReader reader; // 接続する private async void btn_findDevice_Click(object sender, RoutedEventArgs e) { string selector = RfcommDeviceService.GetDeviceSelector(RfcommServiceId.FromUuid(serviceGuid)); DeviceInformationCollection collection = await DeviceInformation.FindAllAsync(selector); if (collection.Count > 0) { DeviceInformation info = collection.First(); rfcommService = await RfcommDeviceService.FromIdAsync(info.Id); socket = new StreamSocket(); await socket.ConnectAsync(rfcommService.ConnectionHostName, rfcommService.ConnectionServiceName); writer = new DataWriter(socket.OutputStream); reader = new DataReader(socket.InputStream); textOut.Text = "接続しました"; } else { MessageDialog dialog = new MessageDialog("デバイスが見つかりませんでした"); await dialog.ShowAsync(); } } // 切断する private void btn_close_Click(object sender, RoutedEventArgs e) { writer.Dispose(); reader.Dispose(); } // 送信する private async void btn_sendMessage_Click(object sender, RoutedEventArgs e) { // 8文字にして送る string text = textIn.Text; await SendCommand( text ); } // コマンド送信 async Task SendCommand(string text) { textIn.Text = text; // 8文字にして送る if (text.Length < 8) { text = text.PadRight(8, '*'); } else { text = text.Substring(0, 8); } writer.WriteString(text); await writer.StoreAsync(); // そのまま受信待ち var res = reader.LoadAsync(8); res.Completed = async delegate { await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { string text2 = reader.ReadString(8); textOut.Text = text2; }); }; } private async void clickM1on(object sender, RoutedEventArgs e) { await SendCommand("m1on"); } private async void clickM1off(object sender, RoutedEventArgs e) { await SendCommand("m1off"); } }
基本は、がりっちさんの記事 http://garicchi.com/?p=17111 と変わりません。デバイスを見つけて、最初のデバイスに自動的に接続します。最初の1回だけはストアアプリから接続許可を求めるダイアログが出ます。
あと、RFCOMM を使うために Package.appxmanifest に以下のように追加しておきます。
<Capabilities> <Capability Name="internetClient" /> <m2:DeviceCapability Name="bluetooth.rfcomm"> <m2:Device Id="any"> <m2:Function Type="serviceId:00001101-0000-1000-8000-00805f9b34fb" /> </m2:Device> </m2:DeviceCapability> </Capabilities>
うまくできあがると、m1on/m1off のボタンを押すことでモーターが動いたり止まったりします。ここは LED で動かしてて確認してもよいですね。ブレッドボード上でモーターが動いても何も面白くない(娘談)なので、ええ、キャタピラを付けてコントローラーで制御するところまでやりますよ。
ちなみに BLE 機器を見つけるだけならばペアリングが不要(通信には必要?)なので、忘れ物タグとかについているんですね。なるほど。これ Windows 8.1 の場合はまだ見つけるだけでもペアリングが必要で、Windows 10 ではそれに対応するらしいのですが…適当な BLE 対応のスマートフォンを媒介すれば、Windows 8.1 でもできるかなと。まあ、半年後に Windows 10 が出るのでそれ待ちってのもあるし、手元のノートPCにWindows 10 TP 版を入れて確かめてみるってのもありですね。