Android-低功耗藍芽(BLE)-用戶端(主機/中心裝置)和服務端(從機/外圍裝置)

來源:互聯網
上載者:User

標籤:csdn   mvn   結果   for   hub   create   off   scanf   建立   

一.Android 低功耗藍芽(BLE)的API簡介
從Android 4.3(API 18)才支援低功耗藍芽(Bluetooth Low Energy, BLE)的核心功能,BLE藍芽協議是GATT協議, BLE相關類不多, 全都位於android.bluetooth包和android.bluetooth.le包的幾個類:android.bluetooth.  .BluetoothGattService  包含多個Characteristic(屬性特徵值), 含有唯一的UUID作為標識  .BluetoothGattCharacteristic  包含單個值和多個Descriptor, 含有唯一的UUID作為標識  .BluetoothGattDescriptor  對Characteristic進行描述, 含有唯一的UUID作為標識    .BluetoothGatt   用戶端相關  .BluetoothGattCallback  用戶端串連回調       .BluetoothGattServer  服務端相關  .BluetoothGattServerCallback 服務端串連回調android.bluetooth.le.  .AdvertiseCallback  服務端的廣播回調  .AdvertiseData  服務端的廣播資料  .AdvertiseSettings 服務端的廣播設定  .BluetoothLeAdvertiser 服務端的廣播    .BluetoothLeScanner  用戶端掃描相關(Android5.0新增)  .ScanCallback  用戶端掃描回調  .ScanFilter 用戶端掃描過濾  .ScanRecord 用戶端掃描結果的廣播資料  .ScanResult 用戶端掃描結果  .ScanSettings 用戶端掃描設定  BLE裝置分為兩種裝置: 用戶端(也叫主機/中心裝置/Central), 服務端(也叫從機/外圍裝置/peripheral)用戶端的核心類是 BluetoothGatt服務端的核心類是 BluetoothGattServer 和 BluetoothLeAdvertiserBLE資料的核心類是 BluetoothGattCharacteristic 和 BluetoothGattDescriptor
二.低功耗藍芽(BLE)-手機同時作為BLE用戶端和BLE服務端,讀寫Characteristic資料

完整源碼: https://github.com/lifegh/Bluetooth

bt_client.png

 

bt_server.png

 

1.藍芽許可權
BLE許可權增加了BEL支援檢查,其它與上篇的經典藍芽相同,不再寫了(1).在manifest中添加許可權(2).在Activity中設定藍芽// 檢查是否支援BLE藍芽if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {    Util.toast(this, "本機不支援低功耗藍芽!");    finish();    return;}
2.BLE用戶端(也叫主機/中心裝置/Central)(1).掃描BLE裝置(不包含經典藍芽)
注意: BLE裝置地址是動態變化(每隔一段時間都會變化),而經典藍牙裝置是出廠就固定不變了!BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();// 下面使用Android5.0新增的掃描API,掃描返回的結果更友好,比如BLE廣播資料以前是byte[] scanRecord,而新API幫我們解析成ScanRecord類// 舊API是BluetoothAdapter.startLeScan(...)final BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();bluetoothLeScanner.startScan(mScanCallback);mHandler.postDelayed(new Runnable() {    @Override    public void run() {        bluetoothLeScanner.stopScan(mScanCallback); //停止掃描        isScanning = false;    }}, 3000);// 掃描結果Callbackprivate final ScanCallback mScanCallback = new ScanCallback() {    @Override    public void onScanResult(int callbackType, ScanResult result) {、        BluetoothDevice dev = result.getDevice() 擷取BLE裝置資訊        // result.getScanRecord() 擷取BLE廣播資料    }};
(2).建立串連
// 擷取掃描裝置,建立串連closeConn();BluetoothDevice dev = result.getDevice()mBluetoothGatt = dev.connectGatt(BleClientActivity.this, false, mBluetoothGattCallback); // 串連藍牙裝置// BLE中心裝置串連外圍裝置的數量有限(大概2~7個),在建立新串連之前必須釋放舊串連資源,否則容易出現串連錯誤133private void closeConn() {    if (mBluetoothGatt != null) {        mBluetoothGatt.disconnect();        mBluetoothGatt.close();    }}    // 與服務端串連的Callbackpublic BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {    @Override    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {        BluetoothDevice dev = gatt.getDevice();        Log.i(TAG, String.format("onConnectionStateChange:%s,%s,%s,%s", dev.getName(), dev.getAddress(), status, newState));        if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {            isConnected = true;            gatt.discoverServices(); //啟動服務發現        } else {            isConnected = false;            closeConn();        }        logTv(String.format(status == 0 ? (newState == 2 ? "與[%s]串連成功" : "與[%s]串連斷開") : ("與[%s]串連出錯,錯誤碼:" + status), dev));    }    @Override    public void onServicesDiscovered(BluetoothGatt gatt, int status) {        Log.i(TAG, String.format("onServicesDiscovered:%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), status));        if (status == BluetoothGatt.GATT_SUCCESS) { //BLE服務發現成功            // 遍曆擷取BLE服務Services/Characteristics/Descriptors的全部UUID            for (BluetoothGattService service : gatt.getServices()) {                StringBuilder allUUIDs = new StringBuilder("UUIDs={nS=" + service.getUuid().toString());                for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {                    allUUIDs.append(",nC=").append(characteristic.getUuid());                    for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors())                        allUUIDs.append(",nD=").append(descriptor.getUuid());                }                allUUIDs.append("}");                Log.i(TAG, "onServicesDiscovered:" + allUUIDs.toString());                logTv("探索服務" + allUUIDs);            }        }    }    @Override    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {        UUID uuid = characteristic.getUuid();        String valueStr = new String(characteristic.getValue());        Log.i(TAG, String.format("onCharacteristicRead:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));        logTv("讀取Characteristic[" + uuid + "]:n" + valueStr);    }    @Override    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {        UUID uuid = characteristic.getUuid();        String valueStr = new String(characteristic.getValue());        Log.i(TAG, String.format("onCharacteristicWrite:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));        logTv("寫入Characteristic[" + uuid + "]:n" + valueStr);    }    @Override    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {        UUID uuid = characteristic.getUuid();        String valueStr = new String(characteristic.getValue());        Log.i(TAG, String.format("onCharacteristicChanged:%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr));        logTv("通知Characteristic[" + uuid + "]:n" + valueStr);    }    @Override    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {        UUID uuid = descriptor.getUuid();        String valueStr = Arrays.toString(descriptor.getValue());        Log.i(TAG, String.format("onDescriptorRead:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));        logTv("讀取Descriptor[" + uuid + "]:n" + valueStr);    }    @Override    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {        UUID uuid = descriptor.getUuid();        String valueStr = Arrays.toString(descriptor.getValue());        Log.i(TAG, String.format("onDescriptorWrite:%s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));        logTv("寫入Descriptor[" + uuid + "]:n" + valueStr);    }};
(3).傳輸資料(讀寫CHARACTERISTIC和DESCRIPTOR)
注意:    1.每次讀寫資料最多20個位元組,如果超過,只能分包    2.連續頻繁讀寫資料容易失敗,讀寫操作間隔最好200ms以上,或等待上次回調完成後再進行下次讀寫操作!// 讀取資料成功會回調->onCharacteristicChanged()public void read(View view) {    BluetoothGattService service = getGattService(BleServerActivity.UUID_SERVICE);    if (service != null) {        BluetoothGattCharacteristic characteristic = service.getCharacteristic(BleServerActivity.UUID_CHAR_READ_NOTIFY);//通過UUID擷取可讀的Characteristic        mBluetoothGatt.readCharacteristic(characteristic);    }}// 寫入資料成功會回調->onCharacteristicWrite()public void write(View view) {    BluetoothGattService service = getGattService(BleServerActivity.UUID_SERVICE);    if (service != null) {        String text = mWriteET.getText().toString();        BluetoothGattCharacteristic characteristic = service.getCharacteristic(BleServerActivity.UUID_CHAR_WRITE);//通過UUID擷取可寫的Characteristic        characteristic.setValue(text.getBytes()); //單次最多20個位元組        mBluetoothGatt.writeCharacteristic(characteristic);    }}// 擷取Gatt服務private BluetoothGattService getGattService(UUID uuid) {    BluetoothGattService service = mBluetoothGatt.getService(uuid);    if (service == null)        Util.toast(this, "沒有找到服務UUID=" + uuid);    return service;}
(4).設定通知,即時監聽CHARACTERISTIC變化
// Characteristic變化會回調->onCharacteristicChanged()BluetoothGattService service = getGattService(BleServerActivity.UUID_SERVICE);if (service != null) {    // 設定Characteristic通知    BluetoothGattCharacteristic characteristic = service.getCharacteristic(BleServerActivity.UUID_CHAR_READ_NOTIFY);//通過UUID擷取可通知的Characteristic    mBluetoothGatt.setCharacteristicNotification(characteristic, true);    // 向Characteristic的Descriptor屬性寫入通知開關,使藍牙裝置主動向手機發送資料    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(BleServerActivity.UUID_DESC_NOTITY);    // descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);//和通知類似,但服務端不主動發資料,只指示用戶端讀取資料    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);    mBluetoothGatt.writeDescriptor(descriptor);}
3.BLE服務端(也叫從機/外圍裝置/peripheral)
public static final UUID UUID_SERVICE = UUID.fromString("10000000-0000-0000-0000-000000000000"); //自訂UUIDpublic static final UUID UUID_CHAR_READ_NOTIFY = UUID.fromString("11000000-0000-0000-0000-000000000000");public static final UUID UUID_DESC_NOTITY = UUID.fromString("11100000-0000-0000-0000-000000000000");public static final UUID UUID_CHAR_WRITE = UUID.fromString("12000000-0000-0000-0000-000000000000");private BluetoothLeAdvertiser mBluetoothLeAdvertiser; // BLE廣播private BluetoothGattServer mBluetoothGattServer; // BLE服務端@Overrideprotected void onCreate(Bundle savedInstanceState) {    ......    BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);    // BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();    // ============啟動BLE藍芽廣播(廣告) =================================================================================    //廣播設定(必須)    AdvertiseSettings settings = new AdvertiseSettings.Builder()            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) //廣播模式: 低功耗,平衡,低延遲            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //發射功率層級: 極低,低,中,高            .setConnectable(true) //能否串連,廣播分為可串連廣播和不可串連廣播            .build();    //廣播資料(必須,廣播啟動就會發送)    AdvertiseData advertiseData = new AdvertiseData.Builder()            .setIncludeDeviceName(true) //包含藍芽名稱            .setIncludeTxPowerLevel(true) //包含發射功率層級            .addManufacturerData(1, new byte[]{23, 33}) //裝置廠商資料,自訂            .build();    //掃描響應資料(可選,當用戶端掃描時才發送)    AdvertiseData scanResponse = new AdvertiseData.Builder()            .addManufacturerData(2, new byte[]{66, 66}) //裝置廠商資料,自訂            .addServiceUuid(new ParcelUuid(UUID_SERVICE)) //服務UUID    //      .addServiceData(new ParcelUuid(UUID_SERVICE), new byte[]{2}) //服務資料,自訂            .build();    mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();    mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseCallback);    // 注意:必須要開啟可串連的BLE廣播,其它裝置才能發現並串連BLE服務端!    // =============啟動BLE藍芽服務端=====================================================================================    BluetoothGattService service = new BluetoothGattService(UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);    //添加可讀+通知characteristic    BluetoothGattCharacteristic characteristicRead = new BluetoothGattCharacteristic(UUID_CHAR_READ_NOTIFY,            BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_READ);    characteristicRead.addDescriptor(new BluetoothGattDescriptor(UUID_DESC_NOTITY, BluetoothGattCharacteristic.PERMISSION_WRITE));    service.addCharacteristic(characteristicRead);    //添加可寫characteristic    BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHAR_WRITE,            BluetoothGattCharacteristic.PROPERTY_WRITE, BluetoothGattCharacteristic.PERMISSION_WRITE);    service.addCharacteristic(characteristicWrite);    if (bluetoothManager != null)        mBluetoothGattServer = bluetoothManager.openGattServer(this, mBluetoothGattServerCallback);    mBluetoothGattServer.addService(service);}// BLE廣播Callbackprivate AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {    @Override    public void onStartSuccess(AdvertiseSettings settingsInEffect) {        logTv("BLE廣播開啟成功");    }    @Override    public void onStartFailure(int errorCode) {        logTv("BLE廣播開啟失敗,錯誤碼:" + errorCode);    }};// BLE服務端Callbackprivate BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {    @Override    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {        Log.i(TAG, String.format("onConnectionStateChange:%s,%s,%s,%s", device.getName(), device.getAddress(), status, newState));        logTv(String.format(status == 0 ? (newState == 2 ? "與[%s]串連成功" : "與[%s]串連斷開") : ("與[%s]串連出錯,錯誤碼:" + status), device));    }    @Override    public void onServiceAdded(int status, BluetoothGattService service) {        Log.i(TAG, String.format("onServiceAdded:%s,%s", status, service.getUuid()));        logTv(String.format(status == 0 ? "添加服務[%s]成功" : "添加服務[%s]失敗,錯誤碼:" + status, service.getUuid()));    }    @Override    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {        Log.i(TAG, String.format("onCharacteristicReadRequest:%s,%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, offset, characteristic.getUuid()));        String response = "CHAR_" + (int) (Math.random() * 100); //類比資料        mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.getBytes());// 響應用戶端        logTv("用戶端讀取Characteristic[" + characteristic.getUuid() + "]:n" + response);    }    @Override    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {        // 擷取用戶端發過來的資料        String requestStr = new String(requestBytes);        Log.i(TAG, String.format("onCharacteristicWriteRequest:%s,%s,%s,%s,%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, characteristic.getUuid(),                preparedWrite, responseNeeded, offset, requestStr));        mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, requestBytes);// 響應用戶端        logTv("用戶端寫入Characteristic[" + characteristic.getUuid() + "]:n" + requestStr);    }    @Override    public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {        Log.i(TAG, String.format("onDescriptorReadRequest:%s,%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, offset, descriptor.getUuid()));        String response = "DESC_" + (int) (Math.random() * 100); //類比資料        mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.getBytes()); // 響應用戶端        logTv("用戶端讀取Descriptor[" + descriptor.getUuid() + "]:n" + response);    }    @Override    public void onDescriptorWriteRequest(final BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {        // 擷取用戶端發過來的資料        String valueStr = Arrays.toString(value);        Log.i(TAG, String.format("onDescriptorWriteRequest:%s,%s,%s,%s,%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, descriptor.getUuid(),                preparedWrite, responseNeeded, offset, valueStr));        mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);// 響應用戶端        logTv("用戶端寫入Descriptor[" + descriptor.getUuid() + "]:n" + valueStr);        // 簡單類比通知用戶端Characteristic變化        if (Arrays.toString(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE).equals(valueStr)) { //是否開啟通知            final BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();            new Thread(new Runnable() {                @Override                public void run() {                    for (int i = 0; i < 5; i++) {                        SystemClock.sleep(3000);                        String response = "CHAR_" + (int) (Math.random() * 100); //類比資料                        characteristic.setValue(response);                        mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);                        logTv("通知用戶端改變Characteristic[" + characteristic.getUuid() + "]:n" + response);                    }                }            }).start();        }    }    @Override    public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {        Log.i(TAG, String.format("onExecuteWrite:%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, execute));    }    @Override    public void onNotificationSent(BluetoothDevice device, int status) {        Log.i(TAG, String.format("onNotificationSent:%s,%s,%s", device.getName(), device.getAddress(), status));    }    @Override    public void onMtuChanged(BluetoothDevice device, int mtu) {        Log.i(TAG, String.format("onMtuChanged:%s,%s,%s", device.getName(), device.getAddress(), mtu));    }};

簡書: https://www.jianshu.com/p/8ac31a5070d4

CSDN: 80643906

GitHub部落格: http://lioil.win/2018/06/10/Android-BLE.html

Coding部落格: http://c.lioil.win/2018/06/10/Android-BLE.html

Android-低功耗藍芽(BLE)-用戶端(主機/中心裝置)和服務端(從機/外圍裝置)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.