<概要>
ここではArduinoをBLESerial経由でAndroid端末につないでみます。
Androidの場合、BLEに対応していないものがたくさんありますので、pm
list featuresを確認してください。
<注意>
ここでご紹介するプログラムはあくまでもサンプルプログラムとなります。個別のご質問などにはお答えできませんのでご了承下さい。
機種やOSのバージョン違いや環境の違いなどにより動作しない可能性もあります。また、今後のOSバージョンアップなどに対しての動作も定かではありません。
紹介動画
<BLE対応しているかの確認方法(Android端末の場合)>
ADB接続時にコンソールから adb shell pm list
features を実行してください。
featureの中に、android.hardware.bluetooth_le
があれば動作します。最後に「_le]が付いたbluetoothが無いと動きません。
<ブロック図>
Arduinoからは一定周期でセンサー読み取り値(8bit値)が送信されつづけます。マイコンボードが0x01を受信するとLEDを点灯、0x00で消灯します。
<接続図>
Arduino側接続詳細
■シリアル通信部
それぞれ、TXをRXへ、RXをTXへつなぎます。今回、Arduino側のシリアルポートはハードウェアシリアルポートを使いました。
■LED
LEDはアノードを13番端子、カソードを電流制限抵抗経由でGNDにつないでいますので、13をHiにするとLEDが点灯し、Loにすると消灯します。
■センサー
今回はセンサーダミーとしてスライドボリュームをつけてみました。電圧変化型のセンサーなら同じように値を読み取ることができます。
※フロー制御はしないので、BLESerialのRTSとCTSを接続します。これを行わないとBLESerialから送信ができません。
<Arduino側プログラム説明>
開発環境はArduinoIDE-1.0.5を使いました。
送信、受信、両方のデモを行うため、センサー値を送信し、受信した値によりLEDを点灯させるプログラムを作りました。
const int LED=13;
int rsvData = 0;
int sensorVal = 0;
void setup()
{
pinMode(LED, OUTPUT);
Serial.begin(9600);
}
void loop()
{
// 受信処理
if(Serial.available() > 0){
rsvData = Serial.read();
if(rsvData == 1){
digitalWrite(LED, HIGH);
}else if(rsvData == 0){
digitalWrite(LED, LOW);
}
}
//センサー読み取り〜送信
sensorVal = analogRead(0);
sensorVal = sensorVal>>2; //10bit->8bit値
Serial.write(sensorVal);
//ウェイト
delay(100);
}
■setup
WiMasterの通信速度は標準で9600bpsなので、シリアルを9600bpsに設定しています。また、13番ポートをLEDに割り当てて出力設定にしています。
■受信
Serial.available( )で受信しているか調べて、1バイト以上受信していたらrsvData変数に入れます。それを確認して1ならLEDポートをHigh(LEDが点灯)、0ならLow(LEDが消灯)にしています。
■センサー読み取り〜送信
analogRead関数で読み取った値をSerial.Writeで送信しているだけです。次のウェイト関数で100mS待ちますので、100mS間隔でセンサーデータが送られることになります。
ArduinoのADCは10bitなので、2bitシフトして8bit値にしてから送信しています。
<Android側プログラム説明>
Eclipse上で作成したAndroidのJavaアプリを圧縮したものがこちらになります。
BLESerial_test_Android.zip
本記事を執筆時点で、Androidが正式にBLEに対応してからまだ3ヶ月しか経過しておりませんので、Android側に若干バグが存在します。よって今回のプログラムはそれらを回避したプログラムになっていますので、一般的なBLEのプログラムとは段取りが若干異なります。ご注意下さい。
特に、普通はサービスUUIDを指定して接続するのですが、ライブラリのサービスIDを指定しての接続が現在版では動作しないので、このサンプルプログラムではSSID(BLESerialという名称)からMACアドレスを取得し、アドレス指定で接続を行っています(ので段取りがやや増えている)。
画面イメージ
起動すると3秒間スキャンをします。右上の「スキャンする」を押しても3秒間スキャンします。
アドバタイズメントしているBLE装置を見つけると、画面にリスト表示します。リストをクリックすると次の画面になり、接続します。
LED_ONボタンを押すと、BLESerialを経由してArduinoに0x01が送られ、ArduinoのLEDが点灯します。LED_OFFボタンで0x00が送られ、LEDは消えます。
Arduinoからは常にセンサー(スライドボリューム)の値が送られ続けます。この値は「センサー値」の下に表示されます。
画面レイアウトはXMLで記述しています。詳細はソースを参照願います。
AndroidのBLE対応は4.3以降になりますので、プロジェクトを作るときはターゲットバージョンを4.3以上にします。
■プロジェクトの構成
プロジェクトには3つのファイルがあり、
BLEScanActivity : 初めに開く画面。スキャンを行い、リスト表示する。
SerialComActivity : スキャン画面からリストを押したときに開く画面。Arduinoと通信してLED操作やセンサー値表示
BluetoothLeService : BLESerialとの通信を制御するサービス。バックグランドで動作。
となっています。
また、Bluetooth及びBLEを使用するので、Manifestにパーミッション設定を行います。
■ID
BLEには「サービス」と「キャラクタリスティック」という概念があります。それぞれには固有のIDがつけられます。Bluetoothの団体で決められたIDもありますが、128bit値の個別IDを使うこともできます。
BLESerialはバーチャルシリアルポート(VSP)というオリジナルサービスが搭載されています。
受信(Androidから見て受信:Android<-BLESerial)と、送信(Androidから見て送信:Android->BLESerial)の2つのキャラクタリスティックを持ち、各IDは次の通りとなります。(BluetoothLeService.java内に記述)
//BLESerial サービスUUID
public static String UUID_BLESERIAL_SERVICE = "569a1101-b87F-490c-92cb-11ba5ea5167c";
// BLESerial 受信UUID (Notify)
public static String UUID_BLESERIAL_RX = "569a2000-b87F-490c-92cb-11ba5ea5167c";
// BLESerial 送信UUID (write without response)
public static String UUID_BLESERIAL_TX = "569a2001-b87F-490c-92cb-11ba5ea5167c";
キャラクタリスティックの設定を行うUUIDは固定値で次のようになっています。
// キャラクタリスティック設定UUID
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
1パケット20バイトまで送受信可能です。フロー制御をしていない場合は、連続で20バイト超過をやりとりするとデータ漏れが発生する場合がありますので注意してください。
■起動〜スキャン
Arduinoの電源を入れると、接続されたBLESerialにも電源が供給され、アドバタイズメント状態に正常に移行できた後にBLESerial上の赤LEDが点灯します。
Androidアプリ(本サンプルアプリ)を起動すると、BLEScanActivityが起動し、onCreate時にまずAndroid端末がBLEに対応しているかをチェックします。また、BluetoothAdapterのインスタンスmBluetoothAdapterを生成しています。基本的にはこのアダプタを通じて設定などを行います。詳細はソースを参照願います。
次にonResume(画面が出るときに実行)が動作し、スキャンを開始します。
スキャンを行うメソッドの部分です。
行158〜165は、指定した時間(ここでは3秒)後にスキャンを停止する記述です。ここが最後に実行されることになります。
スキャンを開始するのは行175の BluetoothAdapter#startLeScanメソッドになります。
startLeScanメソッドは、AndroidDeveloperの説明によると、サービスUUIDを指定して、そのサービスを持っているデバイスのみをスキャンする、ということができる、と書いてありますが、コメントにもあるとおり、2013年11月現在ではバグがあり動きません。よって、ここではデバイス名とMACアドレス、電場強度をとりあえず保持し、後でMACアドレス指定で接続することにしています。
(ちなみに、8月の4.3の発表から何度かビルドが更新されていますが、新しいビルドでもまだこのバグは解消されていないようです。)
スキャン中にデバイス(相手側のBLE装置:ここではBLESerial)が発見されたらBluetoothAdapter.LeScanCallbackが発生しますので、発見したデバイス情報を保持(ここではリストアダプタに追加。自動的に画面にもリストが追加される。)します。下がそのコールバック部になります。
■接続、受信
スキャン画面でリストをクリックすると、そのデバイス情報を付加したインテントでSerialComActivityが起動されます。起動部分はつぎのような感じです。
引継ぎしたデバイス名が mDeviceName に、アドレスが
mDeviceAddress に引継ぎされています。
接続は画面が表示されるタイミング(onResume時)に行っています。
BluetoothLeService#connect(行74)で接続します。このとき、アドレスを指定すると、そのアドレスのBLE機器に接続を試みます。
接続後のイベントは、AndroidOSよりブロードキャストされますので、ブロードキャストレシーバで受け取ります。
Arduinoからは常にセンサーデータが送信されていますので、接続後すぐに受信イベントが発生しつづけます。行147のACTION_DATA_AVAILABLEは受信済みデータがあることを知らせるイベント、つまり受信イベントです。受け取ったインテントからデータを抽出して画面に結果表示をさせています。
■送信
画面上の各ボタンを押すと裏で動いているBluetoothLeService(3個目のファイルで書いているクラス)のsendDataメソッドを使ってデータを送信します。
接続や通信に関しては裏で動かしているBluetoothLeServiceが制御しています。
BLEにはいろいろな通信プロトコルがありますが、BLESerialはGATTを使っています。Android4.3からはBluetoothGattクラスが追加され、これを使ってサービスやキャラクタリスティックの管理ができるようになっています。
BluetoothLeServiceの部分を詳細説明しようとするとBLEについての説明からする必要があり、大変長くなりますので説明を割愛しますが、ネット上のBLEの情報を見ながらソースを読んでみてください。
iPhoneでも同じように、同じハードウェアを動かすことができます。BLESerialのページのサンプルプログラムの節、他のデバイスでの例を掲載しています。
以上
|