微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 无线和射频 > TI无线射频设计 > Android平台BLE低功耗蓝牙开发

Android平台BLE低功耗蓝牙开发

时间:10-02 整理:3721RD 点击:

安卓4.3(API 18)为BLE的核心功能提供平台支持和API,App可以利用它来发现设备、查询服务和读写特性。相比传统的蓝牙,BLE更显著的特点是低功耗。这一优 点使android App可以与具有低功耗要求的BLE设备通信,如近距离传感器、心脏速率监视器、健身设备等。

BLE权限

为了在app中使用蓝牙功能,必须声明蓝牙权限BLUETOOTH。利用这个权限去执行蓝牙通信,例如请求连接、接受连接、和传输数据。

如果想让你的app启动后能够发现或操纵蓝牙设置,必须声明BLUETOOTH_ADMIN权限。注意:如果你使用BLUETOOTH_ADMIN权限,你也必须声明BLUETOOTH权限。

在你的app manifest文件中声明蓝牙权限。

<uses-permission android:name="android.permission.BLUETOOTH"/>

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

如果想声明你的app只为具有BLE的设备提供,在manifest文件中包括:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

但是如果想让你的app提供给那些不支持BLE的设备,需要在manifest中包括上面代码并设置required="false",然后在运行时可以通过使用PackageManager.hasSystemFeature()确定BLE的可用性。

// 使用此检查确定BLE是否支持在设备上,然后你可以有选择性禁用BLE相关的功能

if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {

Toast.makeText(this,R.string.ble_not_supported, Toast.LENGTH_SHORT).show();

finish();

}

设置BLE

你的app能与BLE通信之前,你需要确认手机是否支持BLE,如果支持,确认已经启用。注意如果<uses-feature.../>设置为false,这个检查才是必需的。

如果不支持BLE,那么你应该适当地禁用部分BLE功能。如果支持BLE但被禁用,你可以无需离开应用程序而要求用户启动蓝牙。使用BluetoothAdapter两步完成该设置。

获取 BluetoothAdapter

所有的蓝牙活动都需要蓝牙适配器。BluetoothAdapter代表手机本身的蓝牙适配器(蓝牙无线)。整个系统只有一个蓝牙适配器,而且你的app使用它与系统交互。下面的代码片段显示了如何得到适配器。注意该方法使用getSystemService()]返回BluetoothManager,然后将其用于获取适配器的一个实例。Android 4.3(API 18)引入BluetoothManager。

// 初始化蓝牙适配器

final BluetoothManager bluetoothManager =

(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

开启蓝牙

接下来,你需要确认蓝牙是否开启。调用isEnabled())去检测蓝牙当前是否开启。如果该方法返回false,蓝牙被禁用。下面的代码检查蓝牙是否开启,如果没有开启,将显示错误提示用户去设置开启蓝牙。

// 确保蓝牙在手机上可以开启

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {

Intent enableBtIntent = newIntent (BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

}

发现BLE设备

为了发现BLE设备,使用startLeScan())方法。这个方法需要一个参数BluetoothAdapter.LeScanCallback。你必须实现它的回调函数,那就是返回的扫描结果。因为扫描非常消耗电量,你应当遵守以下准则:

只要找到所需的设备,停止扫描。

不要在循环里扫描,并且对扫描设置时间限制。以前可用的设备可能已经移出范围,继续扫描消耗电池电量。

下面代码显示了如何开始和停止一个扫描:

/**

* 扫描和显示可以提供的蓝牙设备.

*/

public class DeviceScanActivity extends ListActivity {

private BluetoothAdapter mBluetoothAdapter;

private boolean mScanning;

private Handler mHandler;

// 10秒后停止寻找.

private static final long SCAN_PERIOD = 10000;

...

private void scanLeDevice(final boolean enable) {

if (enable) {

// 经过预定扫描期后停止扫描

mHandler.postDelayed(new Runnable() {

@Override

public void run() {

mScanning = false;

mBluetoothAdapter.stopLeScan(mLeScanCallback);

}

}, SCAN_PERIOD);

mScanning = true;

mBluetoothAdapter.startLeScan(mLeScanCallback);

} else {

mScanning = false;

mBluetoothAdapter.stopLeScan(mLeScanCallback);

}

...

}

...

}

如果你只想扫描指定类型的外围设备,可以改为调用startLeScan(UUID[], BluetoothAdapter.LeScanCallback)),需要提供你的app支持的GATT Services的UUID对象数组。

作为BLE扫描结果的接口,下面是BluetoothAdapter.LeScanCallback的实现。

private LeDeviceListAdapter mLeDeviceListAdapter;

...

// Device scan callback.

private BluetoothAdapter.LeScanCallback mLeScanCallback =

new BluetoothAdapter.LeScanCallback() {

@Override

public void onLeScan(final BluetoothDevice device, int rssi,

byte[] scanRecord) {

runOnUiThread(new Runnable() {

@Override

public void run() {

mLeDeviceListAdapter.addDevice(device);

mLeDeviceListAdapter.notifyDataSetChanged();

}

});

}

};

连接到GATT服务端

与一个BLE设备交互的第一步就是连接它——更具体的,连接到BLE设备上的GATT服务端。为了连接到BLE设备上的GATT 服务端,需要使用connectGatt( )方法。这个方法需要三个参数:一个Context对象,自动连接(boolean值,表示只要BLE设备可用是否自动连接到它),和 BluetoothGattCallback调用。

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

连接到GATT服务端时,由BLE设备做主机,并返回一个BluetoothGatt实例,然后你可以使用这个实例来进行 GATT客户端操作。请求方(Android app)是GATT客户端。BluetoothGattCallback用于传递结果给用户,例如连接状态,以及任何进一步GATT客户端操作。

在这个例子中,这个BLE APP提供了一个activity(DeviceControlActivity)来连接,显示数据,显示该设备支持的GATT Services和Characteristics。根据用户的输入,这个activity与BluetoothLeService通信,通过 Android BLE API实现与BLE设备交互。

//通过BLE API服务端与BLE设备交互

public class BluetoothLeService extends Service {

private final static String TAG = BluetoothLeService.class.getSimpleName();

private BluetoothManager mBluetoothManager; //蓝牙管理器

private BluetoothAdapter mBluetoothAdapter; //蓝牙适配器

private String mBluetoothDeviceAddress; //蓝牙设备地址

private BluetoothGatt mBluetoothGatt;

private int mConnectionState = STATE_DISCONNECTED;

private static final int STATE_DISCONNECTED = 0; //设备无法连接

private static final int STATE_CONNECTING = 1; //设备正在连接状态

private static final int STATE_CONNECTED = 2; //设备连接完毕

public final static String ACTION_GATT_CONNECTED =

"com.example.bluetooth.le.ACTION_GATT_CONNECTED";

public final static String ACTION_GATT_DISCONNECTED =

"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";

public final static String ACTION_GATT_SERVICES_DISCOVERED =

"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";

public final static String ACTION_DATA_AVAILABLE =

"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";

public final static String EXTRA_DATA =

"com.example.bluetooth.le.EXTRA_DATA";

public final static UUID UUID_HEART_RATE_MEASUREMENT =

UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

//通过BLE API的不同类型的回调方法

private final BluetoothGattCallback mGattCallback =

new BluetoothGattCallback() {

@Override

public void onConnectionStateChange(BluetoothGatt gatt, int status,

int newState) {//当连接状态发生改变

String intentAction;

if (newState == BluetoothProfile.STATE_CONNECTED) {//当蓝牙设备已经连接

intentAction = ACTION_GATT_CONNECTED;

mConnectionState = STATE_CONNECTED;

broadcastUpdate(intentAction);

Log.i(TAG, "Connected to GATT server.");

Log.i(TAG, "Attempting to start Service discovery:" +

mBluetoothGatt.discoverServices());

} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//当设备无法连接

intentAction = ACTION_GATT_DISCONNECTED;

mConnectionState = STATE_DISCONNECTED;

Log.i(TAG, "Disconnected from GATT server.");

broadcastUpdate(intentAction);

}

}

@Override

// 发现新服务端

public void onServicesDiscovered(BluetoothGatt gatt, int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);

} else {

Log.w(TAG, "onServicesDiscovered received: " + status);

}

}

@Override

// 读写特性

public void onCharacteristicRead(BluetoothGatt gatt,

BluetoothGattCharacteristic Characteristic,

int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

broadcastUpdate(ACTION_DATA_AVAILABLE, Characteristic);

}

}

...

};

...

}

当一个特定的回调被触发的时候,它会调用相应的broadcastUpdate()辅助方法并且传递给它一个action。注意在该部分中的数据解析按照蓝牙心率测量配置文件规格进行。

private void broadcastUpdate(final String action) {

final Intent intent = new Intent(action);

sendBroadcast(intent);

}

private void broadcastUpdate(final String action,

final BluetoothGattCharacteristic Characteristic) {

final Intent intent = new Intent(action);

// 这是心率测量配置文件。

if (UUID_HEART_RATE_MEASUREMENT.equals(Characteristic.getUuid())) {

int flag = Characteristic.getProperties();

int format = -1;

if ((flag & 0x01) != 0) {

format = BluetoothGattCharacteristic.FORMAT_UINT16;

Log.d(TAG, "Heart rate format UINT16.");

} else {

format = BluetoothGattCharacteristic.FORMAT_UINT8;

Log.d(TAG, "Heart rate format UINT8.");

}

final int heartRate = Characteristic.getIntValue(format, 1);

Log.d(TAG, String.format("Received heart rate: %d", heartRate));

intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));

} else {

// 对于所有其他的配置文件,用十六进制格式写数据

final byte[] data = Characteristic.getValue();

if (data != null && data.length > 0) {

final StringBuilder stringBuilder = new StringBuilder(data.length);

for(byte byteChar : data)

stringBuilder.append(String.format("%02X ", byteChar));

intent.putExtra(EXTRA_DATA, new String(data) + "\n" +

stringBuilder.toString());

}

}

sendBroadcast(intent);

}

返回DeviceControlActivity, 这些事件由一个BroadcastReceiver来处理:

// 通过服务控制不同的事件

// ACTION_GATT_CONNECTED: 连接到GATT服务端

// ACTION_GATT_DISCONNECTED: 未连接GATT服务端.

// ACTION_GATT_SERVICES_DISCOVERED: 未发现GATT服务.

// ACTION_DATA_AVAILABLE: 接受来自设备的数据,可以通过读或通知操作获得。

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

final String action = intent.getAction();

if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {

mConnected = true;

updateConnectionState(R.string.connected);

invalidateOptionsMenu();

} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {

mConnected = false;

updateConnectionState(R.string.disconnected);

invalidateOptionsMenu();

clearUI();

} else if (BluetoothLeService.

ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {

// 在用户接口上展示所有的Services and Characteristics

displayGattServices(mBluetoothLeService.getSupportedGattServices());

} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {

displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));

}

}

};

关闭客户端App

当你的app完成BLE设备的使用后,应该调用close( ),系统可以合理释放占用资源。

public void close() {

if (mBluetoothGatt == null) {

return;

}

mBluetoothGatt.close();

mBluetoothGatt = null;

}

mark了。谢谢楼主

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top