2016年11月9日水曜日
2016年10月31日月曜日
2016年10月17日月曜日
ROSで始めるロボティクス(8) ー ロボットのナビゲーションを行う
by 匿名 with 1 comment
前回までの作業でシミュレータ上で動くロボットが準備出来ましたが、今のままでは人が指示を出して動くただのラジコンでしかありません。
ここからは、障害物を避けながら目的の位置まで自立で移動するようにしていきましょう。
まずは地図を用意します。人間でも知らない場所に行く時には事前に地図を確認しますよね。ロボットにも同じように地図が必要になります。
ROSで始めるロボティクス(7) ー ロボットのための二次元mapを作る
by 匿名 with 6 comments
PMD CamBoard pico flexxは3D形状を認識することが可能な非常に小型のToFセンサーです。たまたま別部署用に購入していたものを借りることが出来ましたので、色々試してみました。
このセンサーはとにかく小さいです。ミントケースの半分ぐらいのセンサーで十分な解像度のToFセンサーなので、使い勝手は最高です。早速、ロボットのセンサーに使うため、ROSで使えるように環境を整えていきましょう。
ロボットに使用される定番センサーとしてステレオカメラやレーザーレンジファインダーなどがあります。これらを使うことで、ロボットの周囲の環境を判定し、ロボットの動作を決めることができるようになり、ロボットの自立制御の第一歩となります。
センサーのその他の選択として、マイクロソフト社Kinectやインテル社のRealSenseといった、デプスカメラなどが挙げられます。今回はロボットシミュレータにデプスカメラを追加していきます。
ロボットシミュレータとしてGazeboを使用します。Gazeboはオープンソースで開発されたロボットアプリケーション開発のための動力学シミュレータです。ROSとも連携しており、モータのみならず、ToFセンサやカメラなどもシミュレーション出来ます。
ROSで始めるロボティクス(4) ー シミュレータ上でロボットを動かしてみる
by 匿名 with 6 comments
ROSで始めるロボティクス(3) ー 差動二輪ロボットを準備する
by 匿名 with 6 comments
それではROSの開発環境をインストールしていきます。現在ROSの最新バージョンは「Kinetic Kame」ですが、ここでは「Indigo Igloo」を使用します。というのも、このバージョンはユーザーも多く、サポートする周辺のライブラリも多いため、最初はこのバージョンを選択しておくのが無難だからです。
![]() |
| ROSのサイト。http://www.ros.org/ ROSに関する情報はこのサイトを確認しよう。 |
ROSで始めるロボティクス(2) ー ROSのインストールとセットアップ
by 匿名 with 1 comment
皆さんは、「ロボット開発」と聞いてどんなイメージをお持ちでしょうか。「難しい」「専門知識が必要」「大量のプログラミングが必要」そんなイメージの方も多いのではないでしょうか。本連載ではROSを使用することで手軽に「ロボット開発」を行います。ROSではロボットの「シミュレータ」環境も充実しており、「動作するロボット」がなくても開発することが可能です。
本連載ではROSを使って差動二輪ロボットを作成し、シミュレータ上でうごかしていきます。連載を通じてROSのイメージを掴んていくことができます。また実際のToFセンサーも扱います。実際に動くロボットも作成していきます。
2016年9月6日火曜日
UnityもVisual Studioも触ったことのなかった筆者が、Unity公式キャラクターである『ユニティちゃん』を使ってHololens開発をしてみます。その第1回目としてユニティちゃんをHoloLensで表示してみます。
【HoloLens開発】ユニティちゃんとHololensで戯れる - 表示編 -
by 匿名 with No comments
UnityもVisual Studioも触ったことのなかった筆者が、Unity公式キャラクターである『ユニティちゃん』を使ってHololens開発をしてみます。その第1回目としてユニティちゃんをHoloLensで表示してみます。
2016年8月29日月曜日
HoloLensの肝であるHPUチップは○○で出来ていた!?
法人向けHoloLens(Commercial Suite)が発売!追加された新機能とは?等の最新情報に加え、HoloLensに関する基本的な知識を今回の記事で書いていきます。次回、Part3の記事では、遂にHoloLens開発の中身を書いていきますのでご期待ください!
| 連載目次 |
|
Part 2: 【HoloLens開発】日本での発売に備える実機開発 - 基礎知識編 - ←本記事
Part 8: 【HoloLens開発】ユニティちゃんとHoloLensで戯れる - シェアリング編2 - (予定)
|
本記事目次
|
【HoloLens開発】日本での発売に備える実機開発 基礎知識編
by 匿名 with No comments
HoloLensの肝であるHPUチップは○○で出来ていた!?
法人向けHoloLens(Commercial Suite)が発売!追加された新機能とは?等の最新情報に加え、HoloLensに関する基本的な知識を今回の記事で書いていきます。次回、Part3の記事では、遂にHoloLens開発の中身を書いていきますのでご期待ください!
| 連載目次 |
|
Part 2: 【HoloLens開発】日本での発売に備える実機開発 - 基礎知識編 - ←本記事
Part 8: 【HoloLens開発】ユニティちゃんとHoloLensで戯れる - シェアリング編2 - (予定)
|
本記事目次
|
2016年8月19日金曜日
HoloLensは13m先の壁まで認識できる!?
| 連載目次 |
Part 1: 【HoloLens開発】日本での発売に備える実機開発 - 体験編 - ←本記事
Part 8: 【HoloLens開発】ユニティちゃんとHoloLensで戯れる - シェアリング編2 - (予定)
|
【HoloLens開発】日本での発売に備える実機開発 体験編
by 匿名 with No comments
HoloLensは13m先の壁まで認識できる!?
| 連載目次 |
Part 1: 【HoloLens開発】日本での発売に備える実機開発 - 体験編 - ←本記事
Part 8: 【HoloLens開発】ユニティちゃんとHoloLensで戯れる - シェアリング編2 - (予定)
|
2016年6月3日金曜日
はじめに
Googleに買収された Firebase が、Googleサービスに統合されより使いやすく強力になりました。またFirebaseの持つリアルタイム性を生かした IoTプロジェクトも現れてきています。今回はI/Oに発表されずにいた小粒ななIoTプロジェクトに焦点を当て、Googleに統合されたFirebaseの設定やIoTとしての活用方法について紹介いたします。2016年4月25日月曜日
![]() |
| http://www.grocerycrud.com/より抜粋 |
エラーチェックをしていなかったりなどそのまま使うには不完全なところもありますので、適宜付け加えて使ってみてください。
2016年3月23日水曜日
はじめに
Linkingを利用したアプリの実装方法について
by 匿名 with No comments
2015年12月10日木曜日
https://www.youtube.com/watch?v=vUbFB1Qypg8 より抜粋
本記事は以下の記事の続きになります。
[第一回] 生活で使えるBLEデバイス
[第二回] 生活で使えるBLEデバイス~ペアリング編~
[第三回] 生活で使えるBLE~アプリ間通信編~
はじめに
私事ですが、最近携帯をGalaxy S6に変更しました。(とても気に入っています)
しかしGalaxy S6は電池の容量が少ないらしく、日常的に使っていても少し電池の減りが早い気がします。
なんとか出来ないかと電池の消費などについて少し調べてみると、充電回数を減らすことや過充電を行わないことが電池の寿命を延ばすことに繋がるようです。
ということで、今回はBLEデバイスを使って少しでも携帯の電池寿命の延命を行いたいと思います。
1. 過充電を防いで電池の寿命を長く保ちたい。
2. 家の中で携帯をすぐ無くすので、居場所を特定したい。
Battery ServiceとImmediate Alertのサービスがあるので、やりたいことは実現出来そうです。
では、実際に作ってみましょう。
実際に作ってみる
BLEデバイスは鳴動可能で、物理ボタンがあるモノにしました。今後もいろいろ遊べそうです。
やりたいこと
1. 過充電を防いで電池の寿命を長く保ちたい。
2. 家の中で携帯をすぐ無くすので、居場所を特定したい。
イメージとしては以下のよう感じです。
1. 過充電防止
2. 携帯の位置通知
BLEデバイスのサービスの確認
まず、購入したBLEデバイスを実際に接続してサービスを確認しましょう。
以下のようなサービスがあるようです。
以下のようなサービスがあるようです。
サービス名
|
UUID
|
説明
|
Generic Access
|
1800
|
デバイス名などの情報取得
|
Immediate Alert
|
1802
|
アラームを鳴らす
|
Link Loss
|
1803
|
接続が切れたときの挙動を設定する
|
Tx Power
|
1804
|
BLEの送信のパワー
|
Battery Service
|
180f
|
バッテリーの状態
|
では、実際に作ってみましょう。
実際に作ってみる
1. 過充電防止
ただし、電源接続している時だけ通知が欲しいので、ACTION_POWER_CONNECTEDを契機に、バッテリーの状態を監視するサービスを常駐させるようにしたいと思います。
構成としては以下の通りです。
図3 過充電防止-構成
では次に、Android側の実装に移ります。
使用するUUID
サービスのImmediate Alertを使用します。
Alert Levelに値(0 or 1or 2)を設定することで通知を行えるようです。
public static final UUID ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
public static final UUID ALERT_LEVEL_UUID = UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
AndroidManifestに宣言
PowerConnectedReciverの実装
電源の接続/切断を受けるレシーバーで、電源接続を契機にバッテリーの監視を行い、電源の切断を契機にバッテリーの監視を終了します。
BattryMonitorServiceからの通知を受けて、BLEデバイスへアラームの鳴動要求を通知します。
過充電を防ぐことで、電池の寿命を長持ちさせましょう。
2. 携帯の位置通知
<service android:name=".power.BatteryMonitorService" android:enabled="true"/>
<service android:name=".ble.BluetoothLeService" android:enabled="true"/>
<receiver android:name=".power.PowerConnectedReceiver" android:enabled="true" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
電源の接続/切断を受けるレシーバーで、電源接続を契機にバッテリーの監視を行い、電源の切断を契機にバッテリーの監視を終了します。
public class PowerConnectedReceiver extends BroadcastReceiver {
private static final String TAG = "BleNotify.PowerConnectedReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive : " + intent.getAction());
if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Intent serviceIntent = new Intent(context, BatteryMonitorService.class);
context.startService(serviceIntent);
} else if (intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Intent serviceIntent = new Intent(context, BatteryMonitorService.class);
context.stopService(serviceIntent);
}
}
}
BatteryMonitorServiceの実装
起動時にバッテリーの状態変化を監視するサービスで、バッテリーが90%以上になるとブロードキャストを送信します。
BLEデバイスへ通知起動時にバッテリーの状態変化を監視するサービスで、バッテリーが90%以上になるとブロードキャストを送信します。
public class BatteryMonitorService extends Service {
private static final String TAG = "BleNotify.BatteryMonitorService";
private ChargingOnReceiver mChargingOnReceiver;
private boolean isRegisteredChargingReceiver = false;
class ChargingOnReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float)scale;
Log.d(TAG, "change battery state : " + batteryPct*100 + "%");
if (batteryPct > 0.9) {
Intent notify = new Intent(BluetoothLeService.ACTION_CALL_BATTERY_NOTIFY);
LocalBroadcastManager.getInstance(context).sendBroadcast(notify);
Log.d(TAG, "send broadcast Notify");
}
}
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
mChargingOnReceiver = new ChargingOnReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(mChargingOnReceiver, filter);
isRegisteredChargingReceiver = true;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "starting the BatteryMonitorService.");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.d(TAG, "terminate the BatteryMonitorService.");
// close process
if (isRegisteredChargingReceiver) {
unregisterReceiver(mChargingOnReceiver);
}
super.onDestroy();
}
}
BattryMonitorServiceからの通知を受けて、BLEデバイスへアラームの鳴動要求を通知します。
public class BluetoothLeService extends Service {
private BluetoothGatt mBluetoothGatt;
public final static String ACTION_CALL_BATTERY_NOTIFY =
"com.brilliant.blenotify.ble.le.ACTION_CALL_BATTERY_NOTIFY";
〜中略〜
class GattRequestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive : " + intent.getAction());
if (mBluetoothGatt == null) {
Log.e(TAG, "GattService is not connected...");
return;
}
if (ACTION_CALL_BATTERY_NOTIFY.equals(intent.getAction())) {
BluetoothGattCharacteristic c =
getCharacteristic(GattAttributes.ALERT_SERVICE_UUID,
GattAttributes.ALERT_LEVEL_UUID);
if (c != null) {
// 1 : vibrator
// 2 : sound
int level = 2;
c.setValue(new byte[]{(byte) level});
mBluetoothGatt.writeCharacteristic(c);
}
}
}
}
public BluetoothGattCharacteristic getCharacteristic(UUID sid, UUID cid) {
BluetoothGattService s = mBluetoothGatt.getService(sid);
if (s == null) {
Log.w(TAG, "Service NOT found :" + sid.toString());
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(cid);
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid.toString());
return null;
}
return c;
}
このようにすることで、充電中で90パーセントを超えると音を鳴らして通知してくれます。過充電を防ぐことで、電池の寿命を長持ちさせましょう。
2. 携帯の位置通知
次携帯の位置通知は、BLEデバイス側に物理ボタンがあるので、押された場合に端末側にて鳴動等で位置を知らせます。
今回は端末がバイブレーションするところまで作ります。
構成としては以下の通り。
NotificationをONに設定
今回は端末がバイブレーションするところまで作ります。
構成としては以下の通り。
図4 携帯の位置通知-構成
NotificationをONに設定
まずはBLEデバイスからの通知を受け取る為に設定を行います。
サービス内のキャラクタリスティックスにNotification設定をONにすることで、BLEデバイスからの通知を受けることが出来ます。
サービス内のキャラクタリスティックスにNotification設定をONにすることで、BLEデバイスからの通知を受けることが出来ます。
調べてみると、Battery Service内にある「00002a1b」から始まるキャラクタリスティックスがどうやらNotificationのプロパティを持っているようです。
このキャラクタリスティックスを利用すれば、BLEデバイスからの通知を受けることが出来そうです。
※取得したキャラクタリスティックスはBluetoothGattCharacteristic#getProperties()で確認出来ます。
Blutooth SIGで定義されているBattery ServiceにはBattery Levelしかいないようなので、カスタムで追加されているようです。
このキャラクタリスティックスを利用すれば、BLEデバイスからの通知を受けることが出来そうです。
※取得したキャラクタリスティックスはBluetoothGattCharacteristic#getProperties()で確認出来ます。
Blutooth SIGで定義されているBattery ServiceにはBattery Levelしかいないようなので、カスタムで追加されているようです。
「Power State Lebel(00002a1b)」の参考:
https://groups.google.com/d/msg/android-group-japan/8PffzGRfMms/0gtEcPdjQY8J
public static final UUID BATTERY_SERVICE_UUID = UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
public static final UUID BATTERY_LEVEL_STATE_UUID = UUID.fromString("00002a1b-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic c =
getCharacteristic(GattAttributes.BATTERY_SERVICE_UUID,
GattAttributes.BATTERY_LEVEL_STATE_UUID);
if (c != null) {
final int charaProp = c.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
Log.d(TAG, "has PROPERTY_NOTIFY");
}
mBluetoothGatt.setCharacteristicNotification(c, true);
}
通知はボタンを押された時だけに限定します。
通知されたデータの1byte目でボタンの状態がわかります。
public class BluetoothLeService extends Service {
public final static String ACTION_FINDME_NOTIFY =
"com.brilliant.blenotify.ble.le.ACTION_FINDME_NOTIFY";
〜中略〜
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
UUID uuid = characteristic.getUuid();
Log.e(TAG, "onCharacteristicChanged : " + characteristic.getUuid());
if (GattAttributes.BATTERY_LEVEL_STATE_UUID.equals(uuid)) {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
Log.d(TAG, "BATTERY_LEVEL_STATE_UUID received : " + new String(data));
if (data != null && data.length > 0) {
// if button pressed.
if (data[0] == 1) {
Intent intent = new Intent(ACTION_FINDME_NOTIFY);
sendBroadcast(intent);
Log.d(TAG, "send broadcast ACTION_FINDME_NOTIFY.");
} else {
Log.d(TAG, "Button is not pressed.");
}
}
} else {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
FindMeRecieverの実装BLEデバイスから通知を受けたときの動作を実装します。
public class FindMeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
long[] pattern = {3000, 1000, 3000, 1000};
vibrator.vibrate(pattern, -1);
}
}
AndrodManifestにも以下を追加しておきます。
<uses-permission android:name="android.permission.VIBRATE"/>
<receiver android:name=".findme.FindMeReceiver" android:enabled="true" android:exported="false"/>
<intent-filter/>
<action android:name="com.brilliant.blenotify.ble.le.ACTION_FINDME_NOTIFY" />
</intent-filter/>
</receiver/>
これで、BLEデバイスから通知があったら端末がブルブルと震えてくれるのでどこに置いたかわかるようになりました。
さいごに
以上で「生活で使えるBLEデバイス」シリーズは終了となります。
いかがでしたでしょうか。
BLEの接続方法から実際のデータ通信までの方法を紹介してきましたが、BLEを利用するにあたって多少の苦労がありました。
BLEデバイスの持っているプロファイルに関して、独自に追加されているサービスやキャラクタリスティックスは情報がなかったり、デバイス毎にどのサービスをどの機能に利用しているか等が不明だったりします。(今回で言うところのPower State Lebel)
BluetoothやBLEでの通信のとっつきにくさはこのあたりも関係しているような気がします。
しかし、コツさえ掴んでしまえばアイデア次第で自由なモノ作りが可能な為、様々な分野で活躍できる技術であると思います。
今回までの記事が、そのコツを掴むまでのお手伝いになれば幸いです。
[第四回] 生活で使えるBLEデバイス~実用編~
by 匿名 with No comments
https://www.youtube.com/watch?v=vUbFB1Qypg8 より抜粋
本記事は以下の記事の続きになります。
[第一回] 生活で使えるBLEデバイス
[第二回] 生活で使えるBLEデバイス~ペアリング編~
[第三回] 生活で使えるBLE~アプリ間通信編~
はじめに
私事ですが、最近携帯をGalaxy S6に変更しました。(とても気に入っています)
しかしGalaxy S6は電池の容量が少ないらしく、日常的に使っていても少し電池の減りが早い気がします。
なんとか出来ないかと電池の消費などについて少し調べてみると、充電回数を減らすことや過充電を行わないことが電池の寿命を延ばすことに繋がるようです。
ということで、今回はBLEデバイスを使って少しでも携帯の電池寿命の延命を行いたいと思います。
1. 過充電を防いで電池の寿命を長く保ちたい。
2. 家の中で携帯をすぐ無くすので、居場所を特定したい。
Battery ServiceとImmediate Alertのサービスがあるので、やりたいことは実現出来そうです。
では、実際に作ってみましょう。
実際に作ってみる
BLEデバイスは鳴動可能で、物理ボタンがあるモノにしました。今後もいろいろ遊べそうです。
やりたいこと
1. 過充電を防いで電池の寿命を長く保ちたい。
2. 家の中で携帯をすぐ無くすので、居場所を特定したい。
イメージとしては以下のよう感じです。
1. 過充電防止
2. 携帯の位置通知
BLEデバイスのサービスの確認
まず、購入したBLEデバイスを実際に接続してサービスを確認しましょう。
以下のようなサービスがあるようです。
以下のようなサービスがあるようです。
サービス名
|
UUID
|
説明
|
Generic Access
|
1800
|
デバイス名などの情報取得
|
Immediate Alert
|
1802
|
アラームを鳴らす
|
Link Loss
|
1803
|
接続が切れたときの挙動を設定する
|
Tx Power
|
1804
|
BLEの送信のパワー
|
Battery Service
|
180f
|
バッテリーの状態
|
では、実際に作ってみましょう。
実際に作ってみる
1. 過充電防止
ただし、電源接続している時だけ通知が欲しいので、ACTION_POWER_CONNECTEDを契機に、バッテリーの状態を監視するサービスを常駐させるようにしたいと思います。
構成としては以下の通りです。
図3 過充電防止-構成
では次に、Android側の実装に移ります。
使用するUUID
サービスのImmediate Alertを使用します。
Alert Levelに値(0 or 1or 2)を設定することで通知を行えるようです。
public static final UUID ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
public static final UUID ALERT_LEVEL_UUID = UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
AndroidManifestに宣言
PowerConnectedReciverの実装
電源の接続/切断を受けるレシーバーで、電源接続を契機にバッテリーの監視を行い、電源の切断を契機にバッテリーの監視を終了します。
BattryMonitorServiceからの通知を受けて、BLEデバイスへアラームの鳴動要求を通知します。
過充電を防ぐことで、電池の寿命を長持ちさせましょう。
2. 携帯の位置通知
<service android:name=".power.BatteryMonitorService" android:enabled="true"/>
<service android:name=".ble.BluetoothLeService" android:enabled="true"/>
<receiver android:name=".power.PowerConnectedReceiver" android:enabled="true" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
電源の接続/切断を受けるレシーバーで、電源接続を契機にバッテリーの監視を行い、電源の切断を契機にバッテリーの監視を終了します。
public class PowerConnectedReceiver extends BroadcastReceiver {
private static final String TAG = "BleNotify.PowerConnectedReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive : " + intent.getAction());
if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Intent serviceIntent = new Intent(context, BatteryMonitorService.class);
context.startService(serviceIntent);
} else if (intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Intent serviceIntent = new Intent(context, BatteryMonitorService.class);
context.stopService(serviceIntent);
}
}
}
BatteryMonitorServiceの実装
起動時にバッテリーの状態変化を監視するサービスで、バッテリーが90%以上になるとブロードキャストを送信します。
BLEデバイスへ通知起動時にバッテリーの状態変化を監視するサービスで、バッテリーが90%以上になるとブロードキャストを送信します。
public class BatteryMonitorService extends Service {
private static final String TAG = "BleNotify.BatteryMonitorService";
private ChargingOnReceiver mChargingOnReceiver;
private boolean isRegisteredChargingReceiver = false;
class ChargingOnReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float)scale;
Log.d(TAG, "change battery state : " + batteryPct*100 + "%");
if (batteryPct > 0.9) {
Intent notify = new Intent(BluetoothLeService.ACTION_CALL_BATTERY_NOTIFY);
LocalBroadcastManager.getInstance(context).sendBroadcast(notify);
Log.d(TAG, "send broadcast Notify");
}
}
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
mChargingOnReceiver = new ChargingOnReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(mChargingOnReceiver, filter);
isRegisteredChargingReceiver = true;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "starting the BatteryMonitorService.");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.d(TAG, "terminate the BatteryMonitorService.");
// close process
if (isRegisteredChargingReceiver) {
unregisterReceiver(mChargingOnReceiver);
}
super.onDestroy();
}
}
BattryMonitorServiceからの通知を受けて、BLEデバイスへアラームの鳴動要求を通知します。
public class BluetoothLeService extends Service {
private BluetoothGatt mBluetoothGatt;
public final static String ACTION_CALL_BATTERY_NOTIFY =
"com.brilliant.blenotify.ble.le.ACTION_CALL_BATTERY_NOTIFY";
〜中略〜
class GattRequestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive : " + intent.getAction());
if (mBluetoothGatt == null) {
Log.e(TAG, "GattService is not connected...");
return;
}
if (ACTION_CALL_BATTERY_NOTIFY.equals(intent.getAction())) {
BluetoothGattCharacteristic c =
getCharacteristic(GattAttributes.ALERT_SERVICE_UUID,
GattAttributes.ALERT_LEVEL_UUID);
if (c != null) {
// 1 : vibrator
// 2 : sound
int level = 2;
c.setValue(new byte[]{(byte) level});
mBluetoothGatt.writeCharacteristic(c);
}
}
}
}
public BluetoothGattCharacteristic getCharacteristic(UUID sid, UUID cid) {
BluetoothGattService s = mBluetoothGatt.getService(sid);
if (s == null) {
Log.w(TAG, "Service NOT found :" + sid.toString());
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(cid);
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid.toString());
return null;
}
return c;
}
このようにすることで、充電中で90パーセントを超えると音を鳴らして通知してくれます。過充電を防ぐことで、電池の寿命を長持ちさせましょう。
2. 携帯の位置通知
次携帯の位置通知は、BLEデバイス側に物理ボタンがあるので、押された場合に端末側にて鳴動等で位置を知らせます。
今回は端末がバイブレーションするところまで作ります。
構成としては以下の通り。
NotificationをONに設定
今回は端末がバイブレーションするところまで作ります。
構成としては以下の通り。
図4 携帯の位置通知-構成
NotificationをONに設定
まずはBLEデバイスからの通知を受け取る為に設定を行います。
サービス内のキャラクタリスティックスにNotification設定をONにすることで、BLEデバイスからの通知を受けることが出来ます。
サービス内のキャラクタリスティックスにNotification設定をONにすることで、BLEデバイスからの通知を受けることが出来ます。
調べてみると、Battery Service内にある「00002a1b」から始まるキャラクタリスティックスがどうやらNotificationのプロパティを持っているようです。
このキャラクタリスティックスを利用すれば、BLEデバイスからの通知を受けることが出来そうです。
※取得したキャラクタリスティックスはBluetoothGattCharacteristic#getProperties()で確認出来ます。
Blutooth SIGで定義されているBattery ServiceにはBattery Levelしかいないようなので、カスタムで追加されているようです。
このキャラクタリスティックスを利用すれば、BLEデバイスからの通知を受けることが出来そうです。
※取得したキャラクタリスティックスはBluetoothGattCharacteristic#getProperties()で確認出来ます。
Blutooth SIGで定義されているBattery ServiceにはBattery Levelしかいないようなので、カスタムで追加されているようです。
「Power State Lebel(00002a1b)」の参考:
https://groups.google.com/d/msg/android-group-japan/8PffzGRfMms/0gtEcPdjQY8J
public static final UUID BATTERY_SERVICE_UUID = UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
public static final UUID BATTERY_LEVEL_STATE_UUID = UUID.fromString("00002a1b-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic c =
getCharacteristic(GattAttributes.BATTERY_SERVICE_UUID,
GattAttributes.BATTERY_LEVEL_STATE_UUID);
if (c != null) {
final int charaProp = c.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
Log.d(TAG, "has PROPERTY_NOTIFY");
}
mBluetoothGatt.setCharacteristicNotification(c, true);
}
通知はボタンを押された時だけに限定します。
通知されたデータの1byte目でボタンの状態がわかります。
public class BluetoothLeService extends Service {
public final static String ACTION_FINDME_NOTIFY =
"com.brilliant.blenotify.ble.le.ACTION_FINDME_NOTIFY";
〜中略〜
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
UUID uuid = characteristic.getUuid();
Log.e(TAG, "onCharacteristicChanged : " + characteristic.getUuid());
if (GattAttributes.BATTERY_LEVEL_STATE_UUID.equals(uuid)) {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
Log.d(TAG, "BATTERY_LEVEL_STATE_UUID received : " + new String(data));
if (data != null && data.length > 0) {
// if button pressed.
if (data[0] == 1) {
Intent intent = new Intent(ACTION_FINDME_NOTIFY);
sendBroadcast(intent);
Log.d(TAG, "send broadcast ACTION_FINDME_NOTIFY.");
} else {
Log.d(TAG, "Button is not pressed.");
}
}
} else {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
FindMeRecieverの実装BLEデバイスから通知を受けたときの動作を実装します。
public class FindMeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
long[] pattern = {3000, 1000, 3000, 1000};
vibrator.vibrate(pattern, -1);
}
}
AndrodManifestにも以下を追加しておきます。
<uses-permission android:name="android.permission.VIBRATE"/>
<receiver android:name=".findme.FindMeReceiver" android:enabled="true" android:exported="false"/>
<intent-filter/>
<action android:name="com.brilliant.blenotify.ble.le.ACTION_FINDME_NOTIFY" />
</intent-filter/>
</receiver/>
これで、BLEデバイスから通知があったら端末がブルブルと震えてくれるのでどこに置いたかわかるようになりました。
さいごに
以上で「生活で使えるBLEデバイス」シリーズは終了となります。
いかがでしたでしょうか。
BLEの接続方法から実際のデータ通信までの方法を紹介してきましたが、BLEを利用するにあたって多少の苦労がありました。
BLEデバイスの持っているプロファイルに関して、独自に追加されているサービスやキャラクタリスティックスは情報がなかったり、デバイス毎にどのサービスをどの機能に利用しているか等が不明だったりします。(今回で言うところのPower State Lebel)
BluetoothやBLEでの通信のとっつきにくさはこのあたりも関係しているような気がします。
しかし、コツさえ掴んでしまえばアイデア次第で自由なモノ作りが可能な為、様々な分野で活躍できる技術であると思います。
今回までの記事が、そのコツを掴むまでのお手伝いになれば幸いです。
2015年11月27日金曜日
https://linkingiot.com/developer/#developers より引用はじめに
NTTドコモ等の複数の国内企業が連携して「Linking」というプラットフォームが発表されました。
公開されたサイトを見てみると、どうやらLinkingとはIoT(Internet of Things)に関係するらしいフレーズが散りばめられています。
- すべてのモノが、ネットでつながる
- デバイス開発者も、アプリ開発者も、そしてユーザーも。
- Linkingプラットフォームが、つくるをつなぐ。
すべてがつながる「Linking」とは
by 匿名 with No comments
https://linkingiot.com/developer/#developers より引用はじめに
NTTドコモ等の複数の国内企業が連携して「Linking」というプラットフォームが発表されました。
公開されたサイトを見てみると、どうやらLinkingとはIoT(Internet of Things)に関係するらしいフレーズが散りばめられています。
- すべてのモノが、ネットでつながる
- デバイス開発者も、アプリ開発者も、そしてユーザーも。
- Linkingプラットフォームが、つくるをつなぐ。
2015年11月12日木曜日
[第一回] 生活で使えるBLEデバイス
[第二回] 生活で使えるBLEデバイス~ペアリング編~
はじめに
前回は端末側でのBLE検知と接続までの方法をまとめました。
そして今回はセントラル(端末)とペリフェラル(BLEデバイス)での通信方法に関して触れていきたいと思います。
環境
前回記事と同様に以下の環境で行います。
- Nexus 5(端末)
- Nexus 9(BLEデバイス)
独自サービス
今回のデータ送受信では、セントラル(GATT Client)側からペリフェラル(GATT Server)へ接続し、サービス内の必要なキャラクタリスティクスにアクセスして、データ(Value)の読み書きを行います。
なので、今回のデータ送受信に使用するサービスとキャラクタリスティクスを定義しておきます。
例として、以下のように定義しておきます。
・サービス(図1 のService部分)
SERVICE_UUID = "00000001-0000-1000-8000-2f97f3b2dcd5";
・キャラクタリスティクス(図2 のCharacterri部分)
CHAR_READ_UUID = "00000010-0000-1000-8000-2f97f3b2dcd5";
→データ読み込み用
CHAR_WRITE_UUID = "00000011-0000-1000-8000-2f97f3b2dcd5";
→データ書き込み用
図1 GATT Server & GATT Client
アドバタイジングについて
前回からNexus 9をペリフェラルとして機能させていますが、どのようになっていたのでしょうか。
GATT通信の方法と併せて、Android端末でのアドバタイジング方法を簡単に説明していきます。(Bluetooth機能の確認と位置情報の権限取得は前回記事を参照)
BluetoothLeAdvertiserは端末のアドバタイジング開始/停止の操作等を行えるクラスです。
GATT通信の方法と併せて、Android端末でのアドバタイジング方法を簡単に説明していきます。(Bluetooth機能の確認と位置情報の権限取得は前回記事を参照)
BluetoothLeAdvertiserは端末のアドバタイジング開始/停止の操作等を行えるクラスです。
BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
}
次にGATT Serverのインスタンスを取得します。(BluetoothManager#openGattServer)取得する際に引数として、BluetoothGattServerCallbackを渡します。
このコールバックにて読み書き等、セントラルから要求が行われた際の動作を実装していくようになります。
ペリフェラル機能を実装していく上で本体となる部分です。
mGattServer = mBluetoothManager.openGattServer(this, new BLEServer());
class BLEServer extends BluetoothGattServerCallback {
//セントラルから読み込み要求が来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
}
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
}
}
次は宣言しておいたサービスとキャラクタリスティクスをGATT Serverに設定していきます。
この時、GATT Serverのcloseも忘れずに。
アドバタイジングが開始された後、セントラル(Nexus 5)側から検知出来るようになっていることでしょう。
そして、定義したサービスとGATT Serverによって、データの読み書きを行う準備も出来ました。
では、実際にデータの読み書きを行ってみましょう。
ここでGATT Serverに設定することで、セントラルから独自宣言したサービスを検知することが可能になります。
この時、読み込み用のキャラクタリスティクスには読み込みの、書き込み用のキャラクタリスティクスには書き込みのプロパティと権限を付与しています。
キャラクタリスティクスの権限とプロパティを正しく設定出来ていないと、読み込み/書き込みに失敗してしまうので注意してください。
この時、読み込み用のキャラクタリスティクスには読み込みの、書き込み用のキャラクタリスティクスには書き込みのプロパティと権限を付与しています。
キャラクタリスティクスの権限とプロパティを正しく設定出来ていないと、読み込み/書き込みに失敗してしまうので注意してください。
private void setServices() {
//serviceUUIDを設定BluetoothGattService service = new BluetoothGattService(
UUID.fromString(Constants.SERVICE_UUID),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//characteristicUUIDを設定
BluetoothGattCharacteristic charRead = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_READ_UUID),
BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic charWrite = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_WRITE_UUID),
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
//characteristicUUIDをserviceUUIDにのせる
service.addCharacteristic(charRead);
service.addCharacteristic(charWrite);
//serviceUUIDをサーバーにのせる
mGattServer.addService(service);
}
次にアドバタイジング時の設定(AdvertiseSettings)とデータ(AdvertiseData)の設定を行います。
そして、アドバタイジングの設定の準備が出来たらBluetoothLeAdvertiser#startAdvertisingで、アドバタイジングを開始します。
アドバタイジングを終了する時はBluetoothLeAdvertiser#stopAdvertisingを呼びます。//AdvertiseSettingsの設定
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
//AdvertiseDataの設定
private AdvertiseData buildAdvertiseData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.addServiceUuid(ParcelUuid.fromString(Constants.SERVICE_UUID));
dataBuilder.setIncludeDeviceName(true);
return dataBuilder.build();
}
//Advertiseの開始
private void startAdvertising() {
setServices();
AdvertiseSettings settings = buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
mAdvertiseCallback = new SimpleAdvertiseCallback();
mBluetoothLeAdvertiser.startAdvertising(settings, data,mAdvertiseCallback);
}
//Advertiseの成功可否
private class SimpleAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "Advertising failed");
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(TAG, "Advertising successfully started");
}
}
この時、GATT Serverのcloseも忘れずに。
private void stopAdvertising() {
Log.d(TAG, "Service: Stopping Advertising");
if (mGattServer != null) {
mGattServer.clearServices();
mGattServer.close();
mGattServer = null;
}
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
}
}
以上で、Nexus 9がペリフェラルとして機能するようになります。アドバタイジングが開始された後、セントラル(Nexus 5)側から検知出来るようになっていることでしょう。
そして、定義したサービスとGATT Serverによって、データの読み書きを行う準備も出来ました。
では、実際にデータの読み書きを行ってみましょう。
データの読み込み
セントラル側からペリフェラルのキャラクタリスティクスデータを読み込む手順です。
図3 データの読み込み
接続済みのBluetoothGattから読み込み用キャラクタリスティクスを取得して、読み込み要求(BluetoothGattl#readCharacteristic)を行います。
public void readCharacteristic() {
BluetoothGattCharacteristic read = mBluetoothLeService.getCharacteristic(
GattAttributes.SERVICE_UUID,
GattAttributes.CHAR_READ_UUID);
mBluetoothGatt.readCharacteristic(read);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
読み込み要求を行い、成功するとBluetoothGattCallback#onCharacteristicReadが呼び出されます。
その引数に渡されるBluetoothGattCharacteristicにペリフェラルからのレスポンスを受け取ることが出来ます。
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
}
<ペリフェラル(Nexus 9)側>
セントラル側から読み込み要求が行われると、BluetoothGattServerCallback#onCharacteristicReadRequestに通知が来ます。
セントラルに対して送りたいデータをGattServer#sendResponseにセットすることで、セントラルに任意のデータを送信することが出来ます。
//セントラルからReadRequestが来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
//セントラルに任意の文字を返信する
if (UUID.fromString(Constants. CHAR_READ_UUID).equals(characteristic.getUuid())) {
String response = "your message.";
byte value[] = response.getBytes();
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
}
接続が完了したBluetoothGattから書き込み用キャラクタリスティクスを取得し、書き込みたいデータをセットすることでペリフェラル側へデータを送ることが出来ます。
※書き込み権限のないキャラクタリスティクスに書き込み要求(BluetoothGatt#writeCharacteristic)を行うと戻り値に false が返り、書き込みに失敗します。
public void writeCharacteristic() {
BluetoothGattCharacteristic write = getCharacteristic(
UUID.fromString(GattAttributes.SERVICE_UUID),
UUID.fromString(GattAttributes.CHAR_WRITE_UUID));
String message = "your message";
write.setValue(message);
mBluetoothGatt.writeCharacteristic(characteristic);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
<ペリフェラル(Nexus 9)側>
セントラル側から書き込み要求がされると、BluetoothGattServerCallback#onCharacteristicWriteRequestに通知が来ます。 セントラルから書きこまれた内容はcharacteristicから取得することが出来ます。
また、処理が終わる時には必ずGattServer#sendResponseを呼んでください。
さいごに
以上がAndroid間でのBLE通信(読み書き)の手順です。
今回までで、BLE接続、通信を行えるようになりました。
なので次回からはタイトル通り、生活の中でBLEデバイスを使ったアイデアを考えてみたいと思います。
セントラル側から書き込み要求がされると、BluetoothGattServerCallback#onCharacteristicWriteRequestに通知が来ます。 セントラルから書きこまれた内容はcharacteristicから取得することが出来ます。
また、処理が終わる時には必ずGattServer#sendResponseを呼んでください。
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
Log.d(TAG, "onCharacteristicWriteRequest");
if (UUID.fromString(Constants.CHAR_WRITE_UUID).equals(characteristic.getUuid())) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
}
}
以上がAndroid間でのBLE通信(読み書き)の手順です。
今回までで、BLE接続、通信を行えるようになりました。
なので次回からはタイトル通り、生活の中でBLEデバイスを使ったアイデアを考えてみたいと思います。
[第三回] 生活で使えるBLE~アプリ間通信編~
by 匿名 with 1 comment
[第一回] 生活で使えるBLEデバイス
[第二回] 生活で使えるBLEデバイス~ペアリング編~
はじめに
前回は端末側でのBLE検知と接続までの方法をまとめました。
そして今回はセントラル(端末)とペリフェラル(BLEデバイス)での通信方法に関して触れていきたいと思います。
環境
前回記事と同様に以下の環境で行います。
- Nexus 5(端末)
- Nexus 9(BLEデバイス)
独自サービス
今回のデータ送受信では、セントラル(GATT Client)側からペリフェラル(GATT Server)へ接続し、サービス内の必要なキャラクタリスティクスにアクセスして、データ(Value)の読み書きを行います。
なので、今回のデータ送受信に使用するサービスとキャラクタリスティクスを定義しておきます。
例として、以下のように定義しておきます。
・サービス(図1 のService部分)
SERVICE_UUID = "00000001-0000-1000-8000-2f97f3b2dcd5";
・キャラクタリスティクス(図2 のCharacterri部分)
CHAR_READ_UUID = "00000010-0000-1000-8000-2f97f3b2dcd5";
→データ読み込み用
CHAR_WRITE_UUID = "00000011-0000-1000-8000-2f97f3b2dcd5";
→データ書き込み用
図1 GATT Server & GATT Client
アドバタイジングについて
前回からNexus 9をペリフェラルとして機能させていますが、どのようになっていたのでしょうか。
GATT通信の方法と併せて、Android端末でのアドバタイジング方法を簡単に説明していきます。(Bluetooth機能の確認と位置情報の権限取得は前回記事を参照)
BluetoothLeAdvertiserは端末のアドバタイジング開始/停止の操作等を行えるクラスです。
GATT通信の方法と併せて、Android端末でのアドバタイジング方法を簡単に説明していきます。(Bluetooth機能の確認と位置情報の権限取得は前回記事を参照)
BluetoothLeAdvertiserは端末のアドバタイジング開始/停止の操作等を行えるクラスです。
BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
}
次にGATT Serverのインスタンスを取得します。(BluetoothManager#openGattServer)取得する際に引数として、BluetoothGattServerCallbackを渡します。
このコールバックにて読み書き等、セントラルから要求が行われた際の動作を実装していくようになります。
ペリフェラル機能を実装していく上で本体となる部分です。
mGattServer = mBluetoothManager.openGattServer(this, new BLEServer());
class BLEServer extends BluetoothGattServerCallback {
//セントラルから読み込み要求が来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
}
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
}
}
次は宣言しておいたサービスとキャラクタリスティクスをGATT Serverに設定していきます。
この時、GATT Serverのcloseも忘れずに。
アドバタイジングが開始された後、セントラル(Nexus 5)側から検知出来るようになっていることでしょう。
そして、定義したサービスとGATT Serverによって、データの読み書きを行う準備も出来ました。
では、実際にデータの読み書きを行ってみましょう。
ここでGATT Serverに設定することで、セントラルから独自宣言したサービスを検知することが可能になります。
この時、読み込み用のキャラクタリスティクスには読み込みの、書き込み用のキャラクタリスティクスには書き込みのプロパティと権限を付与しています。
キャラクタリスティクスの権限とプロパティを正しく設定出来ていないと、読み込み/書き込みに失敗してしまうので注意してください。
この時、読み込み用のキャラクタリスティクスには読み込みの、書き込み用のキャラクタリスティクスには書き込みのプロパティと権限を付与しています。
キャラクタリスティクスの権限とプロパティを正しく設定出来ていないと、読み込み/書き込みに失敗してしまうので注意してください。
private void setServices() {
//serviceUUIDを設定BluetoothGattService service = new BluetoothGattService(
UUID.fromString(Constants.SERVICE_UUID),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//characteristicUUIDを設定
BluetoothGattCharacteristic charRead = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_READ_UUID),
BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic charWrite = new BluetoothGattCharacteristic(
UUID.fromString(Constants.CHAR_WRITE_UUID),
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
//characteristicUUIDをserviceUUIDにのせる
service.addCharacteristic(charRead);
service.addCharacteristic(charWrite);
//serviceUUIDをサーバーにのせる
mGattServer.addService(service);
}
次にアドバタイジング時の設定(AdvertiseSettings)とデータ(AdvertiseData)の設定を行います。
そして、アドバタイジングの設定の準備が出来たらBluetoothLeAdvertiser#startAdvertisingで、アドバタイジングを開始します。
アドバタイジングを終了する時はBluetoothLeAdvertiser#stopAdvertisingを呼びます。//AdvertiseSettingsの設定
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
//AdvertiseDataの設定
private AdvertiseData buildAdvertiseData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
dataBuilder.addServiceUuid(ParcelUuid.fromString(Constants.SERVICE_UUID));
dataBuilder.setIncludeDeviceName(true);
return dataBuilder.build();
}
//Advertiseの開始
private void startAdvertising() {
setServices();
AdvertiseSettings settings = buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
mAdvertiseCallback = new SimpleAdvertiseCallback();
mBluetoothLeAdvertiser.startAdvertising(settings, data,mAdvertiseCallback);
}
//Advertiseの成功可否
private class SimpleAdvertiseCallback extends AdvertiseCallback {
@Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(TAG, "Advertising failed");
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(TAG, "Advertising successfully started");
}
}
この時、GATT Serverのcloseも忘れずに。
private void stopAdvertising() {
Log.d(TAG, "Service: Stopping Advertising");
if (mGattServer != null) {
mGattServer.clearServices();
mGattServer.close();
mGattServer = null;
}
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
mAdvertiseCallback = null;
}
}
以上で、Nexus 9がペリフェラルとして機能するようになります。アドバタイジングが開始された後、セントラル(Nexus 5)側から検知出来るようになっていることでしょう。
そして、定義したサービスとGATT Serverによって、データの読み書きを行う準備も出来ました。
では、実際にデータの読み書きを行ってみましょう。
データの読み込み
セントラル側からペリフェラルのキャラクタリスティクスデータを読み込む手順です。
図3 データの読み込み
接続済みのBluetoothGattから読み込み用キャラクタリスティクスを取得して、読み込み要求(BluetoothGattl#readCharacteristic)を行います。
public void readCharacteristic() {
BluetoothGattCharacteristic read = mBluetoothLeService.getCharacteristic(
GattAttributes.SERVICE_UUID,
GattAttributes.CHAR_READ_UUID);
mBluetoothGatt.readCharacteristic(read);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
読み込み要求を行い、成功するとBluetoothGattCallback#onCharacteristicReadが呼び出されます。
その引数に渡されるBluetoothGattCharacteristicにペリフェラルからのレスポンスを受け取ることが出来ます。
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
}
<ペリフェラル(Nexus 9)側>
セントラル側から読み込み要求が行われると、BluetoothGattServerCallback#onCharacteristicReadRequestに通知が来ます。
セントラルに対して送りたいデータをGattServer#sendResponseにセットすることで、セントラルに任意のデータを送信することが出来ます。
//セントラルからReadRequestが来ると呼ばれる
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, int requestId,
int offset, BluetoothGattCharacteristic characteristic) {
//セントラルに任意の文字を返信する
if (UUID.fromString(Constants. CHAR_READ_UUID).equals(characteristic.getUuid())) {
String response = "your message.";
byte value[] = response.getBytes();
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
}
接続が完了したBluetoothGattから書き込み用キャラクタリスティクスを取得し、書き込みたいデータをセットすることでペリフェラル側へデータを送ることが出来ます。
※書き込み権限のないキャラクタリスティクスに書き込み要求(BluetoothGatt#writeCharacteristic)を行うと戻り値に false が返り、書き込みに失敗します。
public void writeCharacteristic() {
BluetoothGattCharacteristic write = getCharacteristic(
UUID.fromString(GattAttributes.SERVICE_UUID),
UUID.fromString(GattAttributes.CHAR_WRITE_UUID));
String message = "your message";
write.setValue(message);
mBluetoothGatt.writeCharacteristic(characteristic);
}
public BluetoothGattCharacteristic getCharacteristic(String sid, String cid) {
BluetoothGattService s = mBluetoothGatt.getService(UUID.fromString(sid));
if (s == null) {
Log.w(TAG, "Service NoT found :" + sid);
return null;
}
BluetoothGattCharacteristic c = s.getCharacteristic(UUID.fromString(cid));
if (c == null) {
Log.w(TAG, "Characteristic NOT found :" + cid);
return null;
}
return c;
}
<ペリフェラル(Nexus 9)側>
セントラル側から書き込み要求がされると、BluetoothGattServerCallback#onCharacteristicWriteRequestに通知が来ます。 セントラルから書きこまれた内容はcharacteristicから取得することが出来ます。
また、処理が終わる時には必ずGattServer#sendResponseを呼んでください。
さいごに
以上がAndroid間でのBLE通信(読み書き)の手順です。
今回までで、BLE接続、通信を行えるようになりました。
なので次回からはタイトル通り、生活の中でBLEデバイスを使ったアイデアを考えてみたいと思います。
セントラル側から書き込み要求がされると、BluetoothGattServerCallback#onCharacteristicWriteRequestに通知が来ます。 セントラルから書きこまれた内容はcharacteristicから取得することが出来ます。
また、処理が終わる時には必ずGattServer#sendResponseを呼んでください。
//セントラルから書き込み要求が来ると呼ばれる
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded,
int offset, byte[] value) {
Log.d(TAG, "onCharacteristicWriteRequest");
if (UUID.fromString(Constants.CHAR_WRITE_UUID).equals(characteristic.getUuid())) {
final byte[] data = characteristic.getValue();
Log.d(TAG, "onCharacteristicRead : " + new String(data));
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
}
}
以上がAndroid間でのBLE通信(読み書き)の手順です。
今回までで、BLE接続、通信を行えるようになりました。
なので次回からはタイトル通り、生活の中でBLEデバイスを使ったアイデアを考えてみたいと思います。
2015年11月4日水曜日
http://developer.android.com/intl/ja/about/versions/jelly-bean.html から引用
この記事は以下の記事の続きになります。
[第一回] 生活で使えるBLEデバイス
はじめに
前回の記事では、BluetoothとBLEおさらいをしました。
では今回はAndroid端末からのBLEデバイス検知とペアリングを行う方法を紹介します。
環境
今回はNexus 5から周辺のBLE端末(Nexus9)を検知して、接続を試みます。
- Nexus 5(OS 6.0)
- Nexus 9(OS 6.0)
以下のサンプルアプリを元に、BLEデバイスの検知と接続方法を説明します。
- RuntimePermission対応(「Bluetooth機能を利用する」の項目を参照)
- BLEスキャン時のAPIの修正(「BLEデバイスの検知」の項目を参照)
以下のサンプルアプリをインストールしておき、BLE端末としての役割をもたせています。
BluetoothAdvertisements
図2 BluetoothAdvertisements
では、実際にBLEデバイスの検知と接続を行う実装方法を紹介していきます。
BLEデバイスの検知と接続
Bluetooth機能を利用する
まずはじめに、Bluettoth機能を利用する宣言を行います。
AndroidManifest.xmlに宣言するパーミッションは以下の通りです。
さらに、Android 6.0からBLEの利用には位置情報の権限が必要になりました。
よって、マニフェストにも「ACCESS_COARSE_LOCATION」または「ACCESS_FINE_LOCATION」の宣言が必要になります。
BLEデバイスの検知と接続
Bluetooth機能を利用する
まずはじめに、Bluettoth機能を利用する宣言を行います。
AndroidManifest.xmlに宣言するパーミッションは以下の通りです。
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
また、BLEの機能を利用する場合は以下の宣言も必要です。
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>※端末がBLEに対応しているかの確認
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE未対応端末です", Toast.LENGTH_SHORT).show();
finish();
}
さらに、Android 6.0からBLEの利用には位置情報の権限が必要になりました。
よって、マニフェストにも「ACCESS_COARSE_LOCATION」または「ACCESS_FINE_LOCATION」の宣言が必要になります。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> または <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>しかし、それだけではBLE機能を使用することは出来ません。。
位置情報の権限はプロテクションレベルが「Dangerous」になるので、ユーザーが権限を許可しない限りBLEの機能は使用することが出来ません。
※RuntimePermissionの詳細はこちら
よって、BLE機能を利用する前には、位置情報の権限の利用が許可されているかの確認を行い、ユーザーが許可をしていなければ許可を行うように促します。
許可されているかのチェックと権限取得の要求
※RuntimePermissionの詳細はこちら
よって、BLE機能を利用する前には、位置情報の権限の利用が許可されているかの確認を行い、ユーザーが許可をしていなければ許可を行うように促します。
許可されているかのチェックと権限取得の要求
private boolean checkPermission() {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_LOCATION_STATE);
return false;
}
return true;
}
許可/不許可の結果は onRequestPermissionsResult で判別出来ます。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (PERMISSIONS_REQUEST_LOCATION_STATE == requestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 許可された場合
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT).show();
} else {
// 不許可だった場合
Toast.makeText(this, "権限を拒否されました", Toast.LENGTH_SHORT).show();
finish();
}
}
}
事前準備としては以上です。
次はBluetooth関連クラスを使用していきます。
BluetoothAdapterの取得
まずはBluetoothAdapterを取得します。
(BluetoothAdapterは、すべてのBluetooth相互作用のためのエントリー・ポイントです。)
次はBluetooth関連クラスを使用していきます。
BluetoothAdapterの取得
まずはBluetoothAdapterを取得します。
(BluetoothAdapterは、すべてのBluetooth相互作用のためのエントリー・ポイントです。)
final BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();次に、端末のBluetoothが有効になっているかの確認を行います。
もし、Bluetoothが無効であればユーザーに有効にするように促します。
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
BLEデバイスの検知
次にBLEデバイスの検知方法を紹介します。
サンプルアプリでは、BLEを検知する為にBluetoothAdapter#startLeScanを使用していますが、APIレベル21から非推奨となり、現在はBluetoothLeScannerを利用するようになりました。
サンプルアプリでは、BLEを検知する為にBluetoothAdapter#startLeScanを使用していますが、APIレベル21から非推奨となり、現在はBluetoothLeScannerを利用するようになりました。
使用方法は以下の通りです。
先ほど取得したBluetoothAdapterからBluetoothLeScannerを取得します。
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
BluetoothLeScannerを取得したら、以下のAPIを利用してスキャンの開始/停止を制御します。
そして、BLE機器が検知された場合、ScanCallback#onScanResultが呼び出され、ScanResultからBluetoothDeviceが取得出来ます。
そして、BLE機器が検知された場合、ScanCallback#onScanResultが呼び出され、ScanResultからBluetoothDeviceが取得出来ます。
BLEデバイスのスキャン開始
BLEデバイスのスキャン停止
BLEスキャンのCallback
// Device scan callback.
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, final ScanResult result) {
super.onScanResult(callbackType, result);
if (result != null && result.getDevice() != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
}
};
これで周辺のBLE機器の検知がすることが出来ました。
では次に、検知したデバイスに接続を行います。
では次に、検知したデバイスに接続を行います。
ペアリング(GATTサーバーに接続)方法
ペアリングと銘打ってますが、BLEデバイスと通信を行う際にはペアリングをする必要はありません。(対応していればペアリングを行うことも可能です)
BLEデバイスの情報を取得するには、GATTサーバーに接続を行うことで、BLEデバイスの情報を読みとることが可能です。
GATTサーバーに接続するには先ほど検知したBluetoothDeviceのBluetoothDevice#connectGattメソッドを使用します。
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);接続が確立されると、BluetoothGattCallback#onConnectionStateChangeが呼ばれます。
newState が BluetoothProfile.STATE_CONNECTEDであれば接続が完了している状態です。
接続が確認出来たら、BluetoothGatt#discoverServicesにより、Gattサーバーの情報を検索します。
結果は BluetoothGattCallback#onServicesDiscovered が呼び出されます。
接続が確認出来たら、BluetoothGatt#discoverServicesにより、Gattサーバーの情報を検索します。
結果は BluetoothGattCallback#onServicesDiscovered が呼び出されます。
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
serviceList = gatt.getServices();
// サービスの内容を取得等処理を行う
// 取得したサービスからBLEデバイスの情報を取得する
}
では最後に、BLEデバイスの情報を取りまとめるGATTについての説明をします。
Generic Attribute Profile (GATT)について
GATTとは、前回の記事で言うところのBluetooth 4.0で使用出来るプロファイルです。
GATTプロファイルは、"attribute"として知られる、BLE link上での短いデータの送受信の一般的な仕様で、現在のBLEアプリケーションプロファイルは、全て、GATTをベースにしています。
※Bluetooth SIGは、BLE端末のための多くのプロファイルを定義します。
※プロファイルは、特定のアプリケーションにおける端末の動作仕様です。
※端末は1つ以上のプロファイルを実装可能です。
図3 Generic Attribute Profile (GATT)について
https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx より引用
・Attribute Protocol (ATT)
GATTは、Attribute Protocol (ATT)の上位に組み込まれています。
GATT/ATTとしても参照されます。
GATTは、Attribute Protocol (ATT)の上位に組み込まれています。
GATT/ATTとしても参照されます。
ATTは、BLEデバイス上で動作するよう最適化されています(数バイト使用します)
各attributeは、Universally Unique Identifier (UUID)によりユニークに識別され、ユニークに情報を識別するために使われるstring IDのため標準化された128-bitフォーマットとなります。
各attributeは、Universally Unique Identifier (UUID)によりユニークに識別され、ユニークに情報を識別するために使われるstring IDのため標準化された128-bitフォーマットとなります。
ATTにより転送されたattributeは、characteristicおよびserviceとしてフォーマットされています。
・Characteristic
characteristicは1つの値と0-nのcharacteristicに言及するdescriptorを含みます。
characteristicはtypeとみなされ、classに似たものとなります。
・Descriptor
Descriptorは、characteristic値を記述する定義されたattributeです。
・Descriptor
Descriptorは、characteristic値を記述する定義されたattributeです。
例えば, characteristicの値が許容可能な範囲を人間が読めるdescriptionとして定義されるものであったり, characteristicの値の単位を表すものであったりします。
・Service
serviceは、characteristicを集めたものです。
例えば, "Heart rate Monitor"と呼ばれるサービスには"heart rate measurement"という名のcharacteristicが含まれます。
既存のGATTベースのprofile/serviceリストをbluetooth.orgで入手可能です。
※これらのサービスやキャラクタリスティックは、Bluetooth SIG が標準として定義していますが、デバイス開発者が独自に定義することも可能です。
GATTプロファイルのサービスとその中にあるCharacteristicを読み取ることで、BLEデバイスの情報を読み出すことが出来ます。
また、GATT通信はサーバークライアントモデルであるため、基本的にはサーバーが機能や情報を保持し、クライアントがそれを利用することになります。
GATTクライアント
データを利用する機器
GATTサーバー
データを保持する機器
データを利用する機器
GATTサーバー
データを保持する機器
GATTクライアントとGATTサーバーは接続確立後、2つの端末がどのように通信し合うかのか決定します。
※今回であれば、Nexus 5(client、つまり端末)とNexus 9(server、つまりBLEデバイス)があると考えて下さい。
接続が確立すると、GATTメタデータの他への転送を開始します。
転送するデータの種類により、どちらかがサーバーとして動作します。
例えば、
例えば、
BLEデバイスがセンサーデータを端末へレポートしたい場合、BLEデバイスがサーバーとして動作するのが理にかなっていると考えられます。
BLEデバイスが端末からアップデートを受信したい場合は、端末がサーバーとして動作するのが理にかなっていると考えられます。
本記事で使用したアプリにおいては、Nexus 5はGATTクライアントとして動作しています。
Nexus 5側のアプリ(GATTクライアント)は、Nexus 9(GATTサーバー)からデータを読み取り、アプリ内で解釈することで連携を行っている。という構成になっています。
BLEデバイスが端末からアップデートを受信したい場合は、端末がサーバーとして動作するのが理にかなっていると考えられます。
本記事で使用したアプリにおいては、Nexus 5はGATTクライアントとして動作しています。
Nexus 5側のアプリ(GATTクライアント)は、Nexus 9(GATTサーバー)からデータを読み取り、アプリ内で解釈することで連携を行っている。という構成になっています。
さいごに
今回はBLEデバイスの検知から接続までをまとめました。
サンプルアプリでBLEの検知と接続を試すことは出来ますが、非推奨になっている点とBLE機能を使用する為の権限が増えている点に御留意いただければと思います。
次回はBLEデバイスからの情報の読み書きを行い、実際にBLE端末とデータのやり取りをしてみたいと思います。
今回はBLEデバイスの検知から接続までをまとめました。
サンプルアプリでBLEの検知と接続を試すことは出来ますが、非推奨になっている点とBLE機能を使用する為の権限が増えている点に御留意いただければと思います。
次回はBLEデバイスからの情報の読み書きを行い、実際にBLE端末とデータのやり取りをしてみたいと思います。
[第二回] 生活で使えるBLE~ペアリング編~
by 匿名 with 2 comments
http://developer.android.com/intl/ja/about/versions/jelly-bean.html から引用
この記事は以下の記事の続きになります。
[第一回] 生活で使えるBLEデバイス
はじめに
前回の記事では、BluetoothとBLEおさらいをしました。
では今回はAndroid端末からのBLEデバイス検知とペアリングを行う方法を紹介します。
環境
今回はNexus 5から周辺のBLE端末(Nexus9)を検知して、接続を試みます。
- Nexus 5(OS 6.0)
- Nexus 9(OS 6.0)
以下のサンプルアプリを元に、BLEデバイスの検知と接続方法を説明します。
- RuntimePermission対応(「Bluetooth機能を利用する」の項目を参照)
- BLEスキャン時のAPIの修正(「BLEデバイスの検知」の項目を参照)
以下のサンプルアプリをインストールしておき、BLE端末としての役割をもたせています。
BluetoothAdvertisements
図2 BluetoothAdvertisements
では、実際にBLEデバイスの検知と接続を行う実装方法を紹介していきます。
BLEデバイスの検知と接続
Bluetooth機能を利用する
まずはじめに、Bluettoth機能を利用する宣言を行います。
AndroidManifest.xmlに宣言するパーミッションは以下の通りです。
さらに、Android 6.0からBLEの利用には位置情報の権限が必要になりました。
よって、マニフェストにも「ACCESS_COARSE_LOCATION」または「ACCESS_FINE_LOCATION」の宣言が必要になります。
BLEデバイスの検知と接続
Bluetooth機能を利用する
まずはじめに、Bluettoth機能を利用する宣言を行います。
AndroidManifest.xmlに宣言するパーミッションは以下の通りです。
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
また、BLEの機能を利用する場合は以下の宣言も必要です。
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>※端末がBLEに対応しているかの確認
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE未対応端末です", Toast.LENGTH_SHORT).show();
finish();
}
さらに、Android 6.0からBLEの利用には位置情報の権限が必要になりました。
よって、マニフェストにも「ACCESS_COARSE_LOCATION」または「ACCESS_FINE_LOCATION」の宣言が必要になります。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> または <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>しかし、それだけではBLE機能を使用することは出来ません。。
位置情報の権限はプロテクションレベルが「Dangerous」になるので、ユーザーが権限を許可しない限りBLEの機能は使用することが出来ません。
※RuntimePermissionの詳細はこちら
よって、BLE機能を利用する前には、位置情報の権限の利用が許可されているかの確認を行い、ユーザーが許可をしていなければ許可を行うように促します。
許可されているかのチェックと権限取得の要求
※RuntimePermissionの詳細はこちら
よって、BLE機能を利用する前には、位置情報の権限の利用が許可されているかの確認を行い、ユーザーが許可をしていなければ許可を行うように促します。
許可されているかのチェックと権限取得の要求
private boolean checkPermission() {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_LOCATION_STATE);
return false;
}
return true;
}
許可/不許可の結果は onRequestPermissionsResult で判別出来ます。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (PERMISSIONS_REQUEST_LOCATION_STATE == requestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 許可された場合
Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT).show();
} else {
// 不許可だった場合
Toast.makeText(this, "権限を拒否されました", Toast.LENGTH_SHORT).show();
finish();
}
}
}
事前準備としては以上です。
次はBluetooth関連クラスを使用していきます。
BluetoothAdapterの取得
まずはBluetoothAdapterを取得します。
(BluetoothAdapterは、すべてのBluetooth相互作用のためのエントリー・ポイントです。)
次はBluetooth関連クラスを使用していきます。
BluetoothAdapterの取得
まずはBluetoothAdapterを取得します。
(BluetoothAdapterは、すべてのBluetooth相互作用のためのエントリー・ポイントです。)
final BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();次に、端末のBluetoothが有効になっているかの確認を行います。
もし、Bluetoothが無効であればユーザーに有効にするように促します。
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
BLEデバイスの検知
次にBLEデバイスの検知方法を紹介します。
サンプルアプリでは、BLEを検知する為にBluetoothAdapter#startLeScanを使用していますが、APIレベル21から非推奨となり、現在はBluetoothLeScannerを利用するようになりました。
サンプルアプリでは、BLEを検知する為にBluetoothAdapter#startLeScanを使用していますが、APIレベル21から非推奨となり、現在はBluetoothLeScannerを利用するようになりました。
使用方法は以下の通りです。
先ほど取得したBluetoothAdapterからBluetoothLeScannerを取得します。
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
BluetoothLeScannerを取得したら、以下のAPIを利用してスキャンの開始/停止を制御します。
そして、BLE機器が検知された場合、ScanCallback#onScanResultが呼び出され、ScanResultからBluetoothDeviceが取得出来ます。
そして、BLE機器が検知された場合、ScanCallback#onScanResultが呼び出され、ScanResultからBluetoothDeviceが取得出来ます。
BLEデバイスのスキャン開始
BLEデバイスのスキャン停止
BLEスキャンのCallback
// Device scan callback.
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, final ScanResult result) {
super.onScanResult(callbackType, result);
if (result != null && result.getDevice() != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(result.getDevice());
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
}
};
これで周辺のBLE機器の検知がすることが出来ました。
では次に、検知したデバイスに接続を行います。
では次に、検知したデバイスに接続を行います。
ペアリング(GATTサーバーに接続)方法
ペアリングと銘打ってますが、BLEデバイスと通信を行う際にはペアリングをする必要はありません。(対応していればペアリングを行うことも可能です)
BLEデバイスの情報を取得するには、GATTサーバーに接続を行うことで、BLEデバイスの情報を読みとることが可能です。
GATTサーバーに接続するには先ほど検知したBluetoothDeviceのBluetoothDevice#connectGattメソッドを使用します。
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);接続が確立されると、BluetoothGattCallback#onConnectionStateChangeが呼ばれます。
newState が BluetoothProfile.STATE_CONNECTEDであれば接続が完了している状態です。
接続が確認出来たら、BluetoothGatt#discoverServicesにより、Gattサーバーの情報を検索します。
結果は BluetoothGattCallback#onServicesDiscovered が呼び出されます。
接続が確認出来たら、BluetoothGatt#discoverServicesにより、Gattサーバーの情報を検索します。
結果は BluetoothGattCallback#onServicesDiscovered が呼び出されます。
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBluetoothGatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
serviceList = gatt.getServices();
// サービスの内容を取得等処理を行う
// 取得したサービスからBLEデバイスの情報を取得する
}
では最後に、BLEデバイスの情報を取りまとめるGATTについての説明をします。
Generic Attribute Profile (GATT)について
GATTとは、前回の記事で言うところのBluetooth 4.0で使用出来るプロファイルです。
GATTプロファイルは、"attribute"として知られる、BLE link上での短いデータの送受信の一般的な仕様で、現在のBLEアプリケーションプロファイルは、全て、GATTをベースにしています。
※Bluetooth SIGは、BLE端末のための多くのプロファイルを定義します。
※プロファイルは、特定のアプリケーションにおける端末の動作仕様です。
※端末は1つ以上のプロファイルを実装可能です。
図3 Generic Attribute Profile (GATT)について
https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx より引用
・Attribute Protocol (ATT)
GATTは、Attribute Protocol (ATT)の上位に組み込まれています。
GATT/ATTとしても参照されます。
GATTは、Attribute Protocol (ATT)の上位に組み込まれています。
GATT/ATTとしても参照されます。
ATTは、BLEデバイス上で動作するよう最適化されています(数バイト使用します)
各attributeは、Universally Unique Identifier (UUID)によりユニークに識別され、ユニークに情報を識別するために使われるstring IDのため標準化された128-bitフォーマットとなります。
各attributeは、Universally Unique Identifier (UUID)によりユニークに識別され、ユニークに情報を識別するために使われるstring IDのため標準化された128-bitフォーマットとなります。
ATTにより転送されたattributeは、characteristicおよびserviceとしてフォーマットされています。
・Characteristic
characteristicは1つの値と0-nのcharacteristicに言及するdescriptorを含みます。
characteristicはtypeとみなされ、classに似たものとなります。
・Descriptor
Descriptorは、characteristic値を記述する定義されたattributeです。
・Descriptor
Descriptorは、characteristic値を記述する定義されたattributeです。
例えば, characteristicの値が許容可能な範囲を人間が読めるdescriptionとして定義されるものであったり, characteristicの値の単位を表すものであったりします。
・Service
serviceは、characteristicを集めたものです。
例えば, "Heart rate Monitor"と呼ばれるサービスには"heart rate measurement"という名のcharacteristicが含まれます。
既存のGATTベースのprofile/serviceリストをbluetooth.orgで入手可能です。
※これらのサービスやキャラクタリスティックは、Bluetooth SIG が標準として定義していますが、デバイス開発者が独自に定義することも可能です。
GATTプロファイルのサービスとその中にあるCharacteristicを読み取ることで、BLEデバイスの情報を読み出すことが出来ます。
また、GATT通信はサーバークライアントモデルであるため、基本的にはサーバーが機能や情報を保持し、クライアントがそれを利用することになります。
GATTクライアント
データを利用する機器
GATTサーバー
データを保持する機器
データを利用する機器
GATTサーバー
データを保持する機器
GATTクライアントとGATTサーバーは接続確立後、2つの端末がどのように通信し合うかのか決定します。
※今回であれば、Nexus 5(client、つまり端末)とNexus 9(server、つまりBLEデバイス)があると考えて下さい。
接続が確立すると、GATTメタデータの他への転送を開始します。
転送するデータの種類により、どちらかがサーバーとして動作します。
例えば、
例えば、
BLEデバイスがセンサーデータを端末へレポートしたい場合、BLEデバイスがサーバーとして動作するのが理にかなっていると考えられます。
BLEデバイスが端末からアップデートを受信したい場合は、端末がサーバーとして動作するのが理にかなっていると考えられます。
本記事で使用したアプリにおいては、Nexus 5はGATTクライアントとして動作しています。
Nexus 5側のアプリ(GATTクライアント)は、Nexus 9(GATTサーバー)からデータを読み取り、アプリ内で解釈することで連携を行っている。という構成になっています。
BLEデバイスが端末からアップデートを受信したい場合は、端末がサーバーとして動作するのが理にかなっていると考えられます。
本記事で使用したアプリにおいては、Nexus 5はGATTクライアントとして動作しています。
Nexus 5側のアプリ(GATTクライアント)は、Nexus 9(GATTサーバー)からデータを読み取り、アプリ内で解釈することで連携を行っている。という構成になっています。
さいごに
今回はBLEデバイスの検知から接続までをまとめました。
サンプルアプリでBLEの検知と接続を試すことは出来ますが、非推奨になっている点とBLE機能を使用する為の権限が増えている点に御留意いただければと思います。
次回はBLEデバイスからの情報の読み書きを行い、実際にBLE端末とデータのやり取りをしてみたいと思います。
今回はBLEデバイスの検知から接続までをまとめました。
サンプルアプリでBLEの検知と接続を試すことは出来ますが、非推奨になっている点とBLE機能を使用する為の権限が増えている点に御留意いただければと思います。
次回はBLEデバイスからの情報の読み書きを行い、実際にBLE端末とデータのやり取りをしてみたいと思います。
登録:
投稿 (Atom)






































