本文轉自:http://blog.csdn.net/chaoyue0071/article/details/43450183/
正文 :
本文簡單結合兩篇文章
http://blog.csdn.net/hellogv/article/details/24267685
http://blog.csdn.net/jimoduwu/article/details/21604215
在BLE協議中,有兩個角色,周邊(Periphery)和中央(Central),一箇中央可以同時連接多個周邊,但是一個周邊某一時刻只能連接一箇中央。但是不管是Periphery還是Central都是可以實現 GATT server 和 GATT client去傳輸數據,但是無法同時都是。
大概瞭解了概念後,看看Android BLE SDK的四個關鍵類(class):
a) BluetoothGattServer作爲周邊來提供數據;BluetoothGattServerCallback返回周邊的狀態。
b) BluetoothGatt作爲中央來使用和處理數據;BluetoothGattCallback返回中央的狀態和周邊提供的數據。
因爲我們討論的是Android的BLE SDK,下面所有的BluetoothGattServer代表周邊,BluetoothGatt代表中央。
一.創建一個周邊(雖然目前周邊API在Android手機上不工作,但還是看看)
a)先看看周邊用到的class,藍色橢圓
b)說明:
每一個周邊BluetoothGattServer,包含多個服務Service,每一個Service包含多個特徵Characteristic。
1.new一個特徵:character = new BluetoothGattCharacteristic(
UUID.fromString(characteristicUUID),
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ);
2.new一個服務:service = new BluetoothGattService(UUID.fromString(serviceUUID),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
3.把特徵添加到服務:service.addCharacteristic(character);
4.獲取BluetoothManager:manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
5.獲取/打開周邊:BluetoothGattServer server = manager.openGattServer(this,
new BluetoothGattServerCallback(){...});
6.把service添加到周邊:server.addService(service);
7.開始廣播service:Google還沒有廣播Service的API,等吧!!!!!所以目前我們還不能讓一個Android手機作爲周邊來提供數據。
二.創建一箇中央(這次不會讓你失望,可以成功創建並且連接到周邊的)
a)先看看中央用到的class,藍色橢圓
b)說明:
爲了拿到中央BluetoothGatt,可要爬山涉水十八彎:
1.先拿到BluetoothManager:bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
2.再拿到BluetoothAdapt:btAdapter = bluetoothManager.getAdapter();
3.開始掃描:btAdapter.startLeScan( BluetoothAdapter.LeScanCallback);
4.從LeScanCallback中得到BluetoothDevice:public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {.....}
5.用BluetoothDevice得到BluetoothGatt:gatt = device.connectGatt(this, true, gattCallback);
終於拿到中央BluetoothGatt了,它有一堆方法(查API吧),調用這些方法,你就可以通過BluetoothGattCallback和周邊BluetoothGattServer交互了。
最近穿戴設備發展得很火,把相關技術也帶旺了,其中一項是BLE(Bluetooth Low Energy)。BLE是藍牙4.0的核心Profile,主打功能是快速搜索,快速連接,超低功耗保持連接和傳輸數據,弱點是數據傳輸速率低,由於BLE的低功耗特點,因此普遍用於穿戴設備。Android 4.3纔開始支持BLE API,所以請各位客官把本文代碼運行在藍牙4.0和Android 4.3及其以上的系統,另外本文所用的BLE終端是一個藍牙4.0的串口藍牙模塊。
PS:我的i9100刷了4.4系統後,竟然也能跟BLE藍牙模塊通信。
BLE分爲三部分Service、Characteristic、Descriptor,這三部分都由UUID作爲唯一標示符。一個藍牙4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Value。一般來說,Characteristic是手機與BLE終端交換數據的關鍵,Characteristic有較多的跟權限相關的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍牙模塊竟然沒有標準的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過位運算符組合來設置讀寫屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY後要分解成所用的組合(本文代碼已含此分解方法)。
本文代碼改自Android 4.3 Sample的BluetoothLeGatt,把冗餘代碼去掉,獲取的BLE設備信息都通過Log,還有一些必要的讀寫藍牙方法,應該算是簡化到大家一看就可以懂了。本文代碼可以到http://download.csdn.net/detail/hellogv/7228819下載。接下來貼出本文運行的結果,首先是連接BLE設備後,枚舉出設備所有Service、Characteristic、Descriptor,並且手機會往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb寫入“send data->”字符串,BLE終端收到數據通過串口傳到PC串口助手(見PC串口助手的截圖):
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:INDICATE
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:READ|WRITE_NO_RESPONSE|NOTIFY|
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:26.025: E/DeviceScanActivity(12254): onCharRead BLE DEVICE read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00
這裏紅字是由BluetoothGattCallback的onCharacteristicRead()回調而打出Log
以下Log是PC上的串口工具通過BLE模塊發送過來,由BluetoothGattCallback的 onCharacteristicChanged()打出Log
04-21 18:30:18.260: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:18.745: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.085: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.350: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.605: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.835: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.055: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.320: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.510: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.735: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:21.000: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
接下來貼出本文核心代碼:
- public class DeviceScanActivity extends ListActivity {
- private final static String TAG = DeviceScanActivity.class.getSimpleName();
- private final static String UUID_KEY_DATA = "0000ffe1-0000-1000-8000-00805f9b34fb";
- private LeDeviceListAdapter mLeDeviceListAdapter;
- /**搜索BLE終端*/
- private BluetoothAdapter mBluetoothAdapter;
- /**讀寫BLE終端*/
- private BluetoothLeClass mBLE;
- private boolean mScanning;
- private Handler mHandler;
- // Stops scanning after 10 seconds.
- private static final long SCAN_PERIOD = 10000;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getActionBar().setTitle(R.string.title_devices);
- mHandler = new Handler();
- // Use this check to determine whether BLE is supported on the device. Then you can
- // selectively disable BLE-related features.
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
- finish();
- }
- // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
- // BluetoothAdapter through BluetoothManager.
- final BluetoothManager bluetoothManager =
- (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
- mBluetoothAdapter = bluetoothManager.getAdapter();
- // Checks if Bluetooth is supported on the device.
- if (mBluetoothAdapter == null) {
- Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
- //開啓藍牙
- mBluetoothAdapter.enable();
- mBLE = new BluetoothLeClass(this);
- if (!mBLE.initialize()) {
- Log.e(TAG, "Unable to initialize Bluetooth");
- finish();
- }
- //發現BLE終端的Service時回調
- mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
- //收到BLE終端數據交互的事件
- mBLE.setOnDataAvailableListener(mOnDataAvailable);
- }
- @Override
- protected void onResume() {
- super.onResume();
- // Initializes list view adapter.
- mLeDeviceListAdapter = new LeDeviceListAdapter(this);
- setListAdapter(mLeDeviceListAdapter);
- scanLeDevice(true);
- }
- @Override
- protected void onPause() {
- super.onPause();
- scanLeDevice(false);
- mLeDeviceListAdapter.clear();
- mBLE.disconnect();
- }
- @Override
- protected void onStop() {
- super.onStop();
- mBLE.close();
- }
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
- if (device == null) return;
- if (mScanning) {
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- mScanning = false;
- }
- mBLE.connect(device.getAddress());
- }
- private void scanLeDevice(final boolean enable) {
- if (enable) {
- // Stops scanning after a pre-defined scan period.
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mScanning = false;
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- invalidateOptionsMenu();
- }
- }, SCAN_PERIOD);
- mScanning = true;
- mBluetoothAdapter.startLeScan(mLeScanCallback);
- } else {
- mScanning = false;
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- }
- invalidateOptionsMenu();
- }
- /**
- * 搜索到BLE終端服務的事件
- */
- private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener(){
- @Override
- public void onServiceDiscover(BluetoothGatt gatt) {
- displayGattServices(mBLE.getSupportedGattServices());
- }
- };
- /**
- * 收到BLE終端數據交互的事件
- */
- private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener(){
- /**
- * BLE終端數據被讀的事件
- */
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS)
- Log.e(TAG,"onCharRead "+gatt.getDevice().getName()
- +" read "
- +characteristic.getUuid().toString()
- +" -> "
- +Utils.bytesToHexString(characteristic.getValue()));
- }
- /**
- * 收到BLE終端寫入數據回調
- */
- @Override
- public void onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- Log.e(TAG,"onCharWrite "+gatt.getDevice().getName()
- +" write "
- +characteristic.getUuid().toString()
- +" -> "
- +new String(characteristic.getValue()));
- }
- };
- // 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();
- }
- });
- }
- };
- private void displayGattServices(List<BluetoothGattService> gattServices) {
- if (gattServices == null) return;
- for (BluetoothGattService gattService : gattServices) {
- //-----Service的字段信息-----//
- int type = gattService.getType();
- Log.e(TAG,"-->service type:"+Utils.getServiceType(type));
- Log.e(TAG,"-->includedServices size:"+gattService.getIncludedServices().size());
- Log.e(TAG,"-->service uuid:"+gattService.getUuid());
- //-----Characteristics的字段信息-----//
- List<BluetoothGattCharacteristic> gattCharacteristics =gattService.getCharacteristics();
- for (final BluetoothGattCharacteristic gattCharacteristic: gattCharacteristics) {
- Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid());
- int permission = gattCharacteristic.getPermissions();
- Log.e(TAG,"---->char permission:"+Utils.getCharPermission(permission));
- int property = gattCharacteristic.getProperties();
- Log.e(TAG,"---->char property:"+Utils.getCharPropertie(property));
- byte[] data = gattCharacteristic.getValue();
- if (data != null && data.length > 0) {
- Log.e(TAG,"---->char value:"+new String(data));
- }
- //UUID_KEY_DATA是可以跟藍牙模塊串口通信的Characteristic
- if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){
- //測試讀取當前Characteristic數據,會觸發mOnDataAvailable.onCharacteristicRead()
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mBLE.readCharacteristic(gattCharacteristic);
- }
- }, 500);
- //接受Characteristic被寫的通知,收到藍牙模塊的數據後會觸發mOnDataAvailable.onCharacteristicWrite()
- mBLE.setCharacteristicNotification(gattCharacteristic, true);
- //設置數據內容
- gattCharacteristic.setValue("send data->");
- //往藍牙模塊寫入數據
- mBLE.writeCharacteristic(gattCharacteristic);
- }
- //-----Descriptors的字段信息-----//
- List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors();
- for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {
- Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid());
- int descPermission = gattDescriptor.getPermissions();
- Log.e(TAG,"-------->desc permission:"+ Utils.getDescPermission(descPermission));
- byte[] desData = gattDescriptor.getValue();
- if (desData != null && desData.length > 0) {
- Log.e(TAG, "-------->desc value:"+ new String(desData));
- }
- }
- }
- }//
- }
- }