Android BLE Bluetooth通信学習

30240 ワード

Android BLE Bluetooth通信学習appアプリケーションの開発過程では、一般的にBluetoothと接触することは多くありませんが、スマートウェアラブルデバイスの発展に伴い、ウェアラブルデバイスと携帯電話に関連するappがますます多くなり、これまであまり接触したことがなく、ちょうど最近Bluetoothに関連するappを作る必要があるので、研究学習の下で、アプリケーションのものをまとめてみましょう.プロジェクトのソースコードはgithubにアップロードされました.
紹介するBLEは、Bluetooth Low Energyの略であり、Bluetooth 3.0と従来の技術とは異なるBluetooth 4.0とも呼ばれる.BLEの前身はNOKIAが開発したWibree技術で、主にモバイルスマート端末と周辺部品との間の持続的な接続を実現するために用いられ、消費電力が極めて低い短距離無線通信技術であり、有効伝送距離は100メートル以上に引き上げられ、同時に1本のボタン電池だけで数年働くことができる.BLEは、Bluetooth技術に基づいて発展し、Bluetoothと同様であり、従来のBluetoothとは異なる.BLE設備は単型と二型の2種類に分けられ、二型はBR、商標はBluetooth Smart Ready、単型はBLEまたはLE、商標はBluetooth Smartである.Androidは4.3後にBLEをサポートしたが、これはすべてのBluetooth携帯電話がBLEをサポートしているわけではなく、BLEをサポートしているBluetooth携帯電話が一般的にデュアルモードであることを説明することができる.BLEと通常のBluetoothには違いがあります.
  • の利点:消費電力がより低く、接続速度がより速い
  • 欠点:送信ごとのデータが比較的小さい
  • AndroidとBLE
    いくつかの概念
    ATT ATT(Attribute Protocol)プロトコルはベースプロトコルであり、ATTBLEデバイスに対して特に最適化され、そのベースは属性であり、UUIDを使用して属性のタイプを定義する.
    GATT GATT(Generic Attribute Profile)は、BLEのすべての最上位プロトコルの基礎であり、ATTの属性をどのようにグループ化して有意義なサービスにするかを定義しています.
    services
    サービスは、UUIDの値が0 x 2800のプロパティに基づいています.このプロパティの後に続くすべてのプロパティは、別の0 x 2800プロパティが現れるまで、このプロパティ定義のサービスに属します.1つのBLEデバイスは、複数のサービスを提供することができる.
    characteristics
    フィーチャー.各サービスには複数のフィーチャーが含まれ、フィーチャーには有用な値と権限が格納されます.ここで、Bluetoothモジュールとappとの通信は、主にそれによって行われる.
    descriptor
    フィーチャーの記述は、記述子とも呼ばれ、1つのフィーチャーに複数の記述子があります.GATTプロトコルは、ほとんどの標準記述子を定義しており、特に重要な記述子の1つは、client characteristic configurationであり、そのUUIDは0 x 2902であり、16 bitの読み書き可能な値を有する.通知と暗示を定義するために使用され、設定によってデバイスに通知を送信させ、ホスト側に受信させることができる.
    Notification
    通知、BLEモジュールは、ホスト側のBluetoothモジュールによって受信できるメッセージを空中に送信する.characteristicsに含まれていますが、権限を開く必要があります.
    BLE透伝
    透過モードでは、すべてのシリアルポートデータがユーザーデータと見なされ、モジュールはこれらのデータをBluetoothモジュールと携帯電話または他の制御装置との間の通信(実際には別のコマンドモードがあるが、透過モードはより迅速で簡単である)にホストに送信します.透過モードは、servicesおよびcharacteristicsによって行われる.一般的なメーカーまたはエンジニアは、次の図のような説明をします.
    役割と役割AndroidデバイスとBLEデバイスのインタラクションには、2つのロールがあります.
    センタ装置(Central)および周辺装置(Periphery).周辺機器はデータプロバイダであり,中央はデータ使用/処理者である.1つの中央装置は複数の周辺装置を同時に接続することができるが、1つの周辺装置は同時に1つの中央装置しか接続できない.
    Android使用
    権限の準備
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

    BLEの準備
  • 取得BluetoothAdapter:
  •         BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            bluetoothAdapter = bluetoothManager.getAdapter();
            //        
            if (!bluetoothAdapter.isEnabled()) {
                bluetoothAdapter.enable();
            }
            //      
            Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enabler, REQUEST_ENABLE);
  • 判断BLE:
  •     private boolean checkBluetooth() {
            if (!getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_BLUETOOTH_LE)) {
                return false;
            }
            return true;
        }
  • スキャンデバイスの開始:
  • bluetoothAdapter.startLeScan(mLeScanCallback);
    
    BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        /*
                               ,           BLEDeviceTestActivity               
                                       
                         intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
                         intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
                        */
                        adapter.add("name : " + device.getName() + "
    address : "
    + device.getAddress()); bluetoothDevices.add(device); } }); } };

    デバイスの接続BLEDeviceTestActivityでデバイスを取得し、デバイスを接続します.
    //        
    BluetoothDevice device = bluetoothAdapter.getRemoteDevice(addressStr);
    //             BluetoothGattCallback
    BluetoothGatt bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
    device.connectGatt(this, false, bluetoothGattCallback);方法については、デバイスによってGATTリンクが確立されていることがわかり、GATTリンクに関する操作はコールバックをトリガーします.BluetoothGattCallbackは、これらのコールバック結果を非同期で処理します.
        /**
         * Connect to GATT Server hosted by this device. Caller acts as GATT client.
         * The callback is used to deliver results to Caller, such as connection status as well
         * as any further GATT client operations.
         * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
         * GATT client operations.
         * @param callback GATT callback handler that will receive asynchronous callbacks.
         * @param autoConnect Whether to directly connect to the remote device (false)
         *                    or to automatically connect as soon as the remote
         *                    device becomes available (true).
         * @throws IllegalArgumentException if callback is null
         */
        public BluetoothGatt connectGatt(Context context, boolean autoConnect,
                                         BluetoothGattCallback callback) {
            return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
        }
    bluetoothGattCallbackを実現しました
        BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
            /**
             *       
             * @param gatt
             * @param status              {@link BluetoothGatt#GATT_SUCCESS}
             * @param newState         {@link BluetoothProfile#STATE_DISCONNECTED} or
             *                                  {@link BluetoothProfile#STATE_CONNECTED}
             */
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
            }
    
            /**
             *          GATT      
             * @param gatt
             * @param status     {@link BluetoothGatt#GATT_SUCCESS}
             */
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);
    
            }
    
            /**
             *          
             * @param gatt
             * @param characteristic               
             * @param status
             */
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
            }
    
            /**
             *              
             * @param gatt
             * @param characteristic
             * @param status
             */
            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicWrite(gatt, characteristic, status);
            }
    
            /**
             *               
             * @param gatt
             * @param characteristic
             */
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);
            }
    
            /**
             *             
             * @param gatt
             * @param descriptor
             * @param status
             */
            @Override
            public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                super.onDescriptorRead(gatt, descriptor, status);
            }
    
            /**
             *          
             * @param gatt
             * @param descriptor
             * @param status
             */
            @Override
            public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                super.onDescriptorWrite(gatt, descriptor, status);
            }
    
            /**
             *              
             * @param gatt
             * @param status
             */
            @Override
            public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
                super.onReliableWriteCompleted(gatt, status);
    
            }
    
            @Override
            public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
                super.onReadRemoteRssi(gatt, rssi, status);
            }
    
            @Override
            public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
                super.onMtuChanged(gatt, mtu, status);
            }
        };
    BluetoothGattCallbackには多くの方法が見られ、名前によってその意味が分かりにくくなく、Bluetoothの使用過程に基づいて一つ一つ分析すればその機能が明らかになります.
    前にリンクしたので、最初にトリガーされたコールバックはonConnectionStateChange()メソッドです.
    
             /**
             *       
             * @param gatt
             * @param status              {@link BluetoothGatt#GATT_SUCCESS}         
             * @param newState         {@link BluetoothProfile#STATE_DISCONNECTED} or          
             *                                  {@link BluetoothProfile#STATE_CONNECTED}
    
                 newState      
             */
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    //                   UI          ,       hanlder  Eventbus    
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            connectTv.setText("    ");
                        }
                    });
                    //    
                    gatt.discoverServices();
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    //    
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            connectTv.setText("    ");
                        }
                    });
                }
            }

    上記のコードでは、接続に成功した後にgatt.discoverServices();メソッドが呼び出され、このメソッドは、リモートデバイスが提供するサービス、およびそれらに含まれる特徴的特性および記述子などを発見するために使用されることが分かった.
    前述の説明から分かるように、appとBluetoothモジュールを通信するには、これらのサービスや特徴などが必要です.この方法はコールバックonServicesDiscovered()をトリガーします.
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);
                if (BluetoothGatt.GATT_SUCCESS == status) {
                    gatt.getServices();
    
                    /*
      GATT        BluetoothGattService   ,         UUID  BluetoothGatt      ;
      GATT        BluetoothGattCharacteristic    ,         UUID  BluetoothGattService    ;
             ,     value 0~n value   (BluetoothGattDescriptor)
      GATT         BluetoothGattDescriptor   ,         UUID BluetoothGattCharacteristic       :
       , Characteristic   ,    、     
                     */
    
                    gattCharacteristicList.clear();
                    String uuid = null;
                    ArrayList> gattServiceData = new ArrayList>();
                    ArrayList>> gattCharacteristicData = new ArrayList>>();
    
                    //    
                    for (BluetoothGattService gattService : gatt.getServices()) {
                        HashMap currentServiceData = new HashMap();
                        uuid = gattService.getUuid().toString();
                        currentServiceData.put("name",
                                SampleGattAttributes.lookup(uuid, "    "));
                        currentServiceData.put("uuid", uuid);
                        gattServiceData.add(currentServiceData);
    
                        ArrayList> gattCharacteristicGroupData = new ArrayList>();
                        List gattCharacteristics = gattService
                                .getCharacteristics();
                        ArrayList charas = new ArrayList();
    
                        //             
                        for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                            charas.add(gattCharacteristic);
                            HashMap currentCharaData = new HashMap();
                            uuid = gattCharacteristic.getUuid().toString();
                            currentCharaData.put("name",
                                    SampleGattAttributes.lookup(uuid, "    "));
                            currentCharaData.put("uuid", uuid);
    
                            //      UUID 0000ff02-0000-1000-8000-00805f9b34fb    BLE        
                            if (uuid.equals("0000ff02-0000-1000-8000-00805f9b34fb")) {
                                setCharacteristicNotification(gattCharacteristic, true);
                            }
                            gattCharacteristicGroupData.add(currentCharaData);
                        }   
                        gattCharacteristicList.add(charas);
                        gattCharacteristicData.add(gattCharacteristicGroupData);
                    }
    
                    //                       
                    final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
                            BLEDeviceTestActivity.this, gattServiceData,
                            android.R.layout.simple_expandable_list_item_2, new String[]{
                            "name", "uuid"}, new int[]{android.R.id.text1,
                            android.R.id.text2}, gattCharacteristicData,
                            android.R.layout.simple_expandable_list_item_2, new String[]{
                            "name", "uuid"}, new int[]{android.R.id.text1,
                            android.R.id.text2});
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            listView.setAdapter(gattServiceAdapter);
                        }
                    });
                }
            }

    上のコードの山は、取得したサービスと特徴を1つのリストで示すことが重要ではありません.前のcharacteristicの説明によると、0 xff 02の先頭の特徴値が通知可能であるため、次のコードがあります.
     if (uuid.equals("0000ff02-0000-1000-8000-00805f9b34fb")) {
                                setCharacteristicNotification(gattCharacteristic, true);
                            }
    
    setCharacteristicNotification()メソッドは、指定されたフィーチャーの通知権限を有効にすることです.ここは小さな穴に穴をあけられて、bluetoothGatt.setCharacteristicNotification(characteristic, enabled);だと思っていた.trueを設定すれば十分ですが、デバッグ時に通知を受信できないことに気づき、資料を調べてみたら、ディスクリプタに通知権限を設定して有効にする必要があることがわかりましたが、具体的にどのディスクリプタが分からないので、最後にBluetoothモジュールメーカーから送られてきたdemoに基づいて逆コンパイルし、そのソースコードを見てから対応するUUIDを知りました.さらに、この記述子が発見されたUUIDは0 x 2902で始まることから、この記述子はGATTプロトコルで書かれた死んだ記述子であるべきであると推測される.
    
        public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";   
    
        public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
            //          
            bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
            //                    
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
                    .fromString(CLIENT_CHARACTERISTIC_CONFIG));
            if (descriptor != null) {
                System.out.println("write descriptor");
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                bluetoothGatt.writeDescriptor(descriptor);
            }
        }

    通知が設定された後、BLEデバイスが通知によってデータを送信すると、appエンドが通知を受信するとonCharacteristicChanged()メソッドがトリガーされ、このメソッドは、BLEモジュールがブロードキャスト形式で返されることを受信するために使用することができる.
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(BLEDeviceTestActivity.this, "     ", Toast.LENGTH_SHORT).show();
                    }
                });
                //              
                final byte[] data = characteristic.getValue();
                if (data != null && data.length > 0) {
                    final StringBuilder stringBuilder = new StringBuilder(
                            data.length);
                    StringBuffer test = new StringBuffer();
                    for (byte byteChar : data) {
                        test.append(byteChar);
                        stringBuilder.append(String.format("%02X ", byteChar));//   16         0
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            //    
                            dataTv.setText(new String(data) + "
    "
    + stringBuilder.toString()); } }); } }

    前述の説明からBluetoothの透過はcharacteristicsで行われていることが分かるので、もちろん読み書きの操作もありますが、操作を行う前にサービスのUUIDを判断する必要があります.characteristic.getUuid()に従ってUUIDを得るか、またはUUIDを知っている場合に、対応する特徴をアクティブに取得して操作することができる.
    リード操作は、コールバック関数onCharacteristicRead()をトリガーします.
            //           
            bluetoothGatt.readCharacteristic(characteristic);
    
            //           
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
    
                //        characteristic  ,    characteristic.getValue();    。         。
                if (BluetoothGatt.GATT_SUCCESS == status) {
                    final byte[] data = characteristic.getValue();
                    ...
                    //                          
                }
    
            }

    同様に、読み取りがある以上、書き込み操作も可能であり、書き込み操作は以下のようにして、コールバック関数onCharacteristicWrite()をトリガーする.
            //                  
            characteristic.setValue(byte[] value);
            bluetoothGatt.writeCharacteristic(characteristic);
    
            //        ,          
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicWrite(gatt, characteristic, status);
                if (BluetoothGatt.GATT_SUCCESS == status) {
                    Log.d("BLEDeviceTestActivity", "    ");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(BLEDeviceTestActivity.this, "    ", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
    

    書き込みに成功し、透過モジュールに応答がある場合、一般的な応答方式は通知によって行われる.だから普通はプロジェクトの中でリンクが成功した后に通知の権限を开いて、省の后ろに书く时忘れます.これで,携帯電話とBLEモジュール間の通信はほぼ完了し,他の書き込み記述子のこれらのコールバック過程は大きく異なる.
    もちろん、これは簡単に通信が完了しただけで、本当に使うときはパケットサイズの制限でパケット転送、受信など、完璧な点がたくさんあります.