我想知道 BLE gatt 的设置完成时间,比如回调

Posted

技术标签:

【中文标题】我想知道 BLE gatt 的设置完成时间,比如回调【英文标题】:I want to know the finished time of setting of BLE gatt like callback 【发布时间】:2019-09-25 04:35:33 【问题描述】:

我英语不好。敬请谅解。

我想知道蓝牙设置何时完成。我想在蓝牙连接和设置完成后向我的设备发送数据。我如何知道流打开何时完成?

当我尝试了所有 BLE 设置时,我只睡了几秒钟,但我想将其更改为顺序代码。

private class GattClientCallback extends BluetoothGattCallback 
@Override
        public void onServicesDiscovered( BluetoothGatt _gatt, int _status ) 
            super.onServicesDiscovered( _gatt, _status );
            // check if the discovery failed
            if( _status != BluetoothGatt.GATT_SUCCESS ) 
                Log.e( TAG, "Device service discovery failed, status: " + _status );
                return;
            
            // find discovered characteristics
            List<BluetoothGattCharacteristic> matching_characteristics= BluetoothUtils.findBLECharacteristics(_gatt);
            if( matching_characteristics.isEmpty() ) 
                Log.e( TAG, "Unable to find characteristics" );
                return;
            
            // log for successful discovery
            Log.d( TAG, "Services discovery is successful" );

            // find command characteristics from the GATT server
            cmd_characteristic= BluetoothUtils.findCommandCharacteristic( ble_gatt_ );

            //setCharacteristicNotification
            ble_gatt_.setCharacteristicNotification(cmd_characteristic, true);
            BluetoothGattDescriptor descriptor = cmd_characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            ble_gatt_.writeDescriptor(descriptor); //descriptor write operation successfully started?
            // stream open complete.

            // update the connection status message
            msg_handler(SET_TV_STATUS,"connection complete");
        


String result = pref.getString("first_device", "0");
        if(result.equals("0")) 
            ble_info.startScan();
         else 
            ble_info.connectSavedDevice();
        
        packet_sleep(5000); //I have to send data after ble connecting finished.

        if(!ble_info.connected_) 
            return;
        

        while(ble_info.connected_ && state_check != ACK_COMM_START) 
            ble_info.sendData(SET_COMM_START);
            packet_sleep(ACK_TERM);
        

ble_info 类

public class BluetoothInfo 

    protected static final int SET_TV_STATUS = 0;
    protected static final int SET_TV_CONNECTION = 1; 
    protected static final UUID CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
    protected static PacketReceive packetReceive = new PacketReceive();
    //-------------------------------

    //mac address
    public String devAddress;

    // ble adapter
    public BluetoothAdapter ble_adapter_;
    // flag for scanning
    public boolean is_scanning_= false;
    // flag for connection
    public boolean connected_= false;
    // scan results
    public Map<String, BluetoothDevice> scan_results_;
    // scan callback
    public ScanCallback scan_cb_;
    // ble scanner
    public BluetoothLeScanner ble_scanner_;
    // scan handler
    public Handler scan_handler_;
    // BLE Gatt
    public BluetoothGatt ble_gatt_;

    BluetoothGattCharacteristic cmd_characteristic;

    // to save data
    SharedPreferences pref;
    SharedPreferences.Editor editor;

    //constructor
    BluetoothInfo(BluetoothManager ble_manager, SharedPreferences pref) 
        ble_adapter_= ble_manager.getAdapter();
        ble_scanner_ = ble_adapter_.getBluetoothLeScanner();
        //to save data
        this.pref = pref;

    

    @RequiresApi(api = Build.VERSION_CODES.M)
    public void startScan() 

        // check if location permission
        if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            requestLocationPermission();
            Log.d(TAG, "Scanning Failed: no fine location permission");
            return;
        
        // disconnect gatt server
        disconnectGattServer();

        scan_handler_= new Handler();
        scan_handler_.postDelayed( this::stopScan, SCAN_PERIOD ); 

        scan_results_= new HashMap<>();
        scan_cb_= new BLEScanCallback( scan_results_ );

        Log.d(TAG,"Scanning...");

        // check ble adapter and ble enabled
        if (ble_adapter_ == null || !ble_adapter_.isEnabled()) 
            requestEnableBLE();
            Log.d(TAG, "Scanning Failed: ble not enabled");
            return;
        

        // now ready to scan
        ble_scanner_.startScan(scan_cb_); //scan filter 사용x
        // set scanning flag
        is_scanning_= true;
    

    /*
    Stop scanning
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void stopScan() 
        // check pre-conditions
        if( is_scanning_ && ble_adapter_ != null && ble_adapter_.isEnabled() && ble_scanner_ != null ) 
            // stop scanning
            ble_scanner_.stopScan( scan_cb_ );
            scanComplete();
        
        // reset flags
        scan_cb_= null;
        is_scanning_= false;
        scan_handler_= null;
        Log.d(TAG,"scanning stopped");
    

    /*
    Handle scan results after scan stopped
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void scanComplete() 
        Toast.makeText(context, "scan finished", Toast.LENGTH_LONG).show();
        // check if nothing found
        if( scan_results_.isEmpty() ) 
            Log.d( TAG, "scan results is empty" );
            return;
        

        ArrayList<BluetoothDevice> found_devices = new ArrayList<>();
        ArrayList<String> found_devices_names = new ArrayList<>();

        // loop over the scan results and connect to them
        for( String device_name : scan_results_.keySet() ) 
            Log.d( TAG, "Found device: " + device_name );
            if(device_name==null)
                continue;
            BluetoothDevice device= scan_results_.get( device_name );

            if( device_name.contains( BLE_NAME) ) 
                found_devices.add(device);
                found_devices_names.add(device_name);
            
        
        // exist device
        if(found_devices.size() >= 1) 
            CharSequence[] items = found_devices_names.toArray(new String[found_devices_names.size()]);

            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setTitle("select device");
            builder.setItems(items, new DialogInterface.OnClickListener() 
                public void onClick(DialogInterface dialog, int pos) 
                    String selectedText = items[pos].toString();
                    connectDevice(found_devices.get(pos));
                    Toast.makeText(context, selectedText, Toast.LENGTH_SHORT).show();
                
            );
            builder.show();
         else 
            Toast.makeText(context, "not found", Toast.LENGTH_SHORT).show();
        

    

    /*
    Connect to the ble device
    */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void connectDevice(BluetoothDevice _device ) 
        // update the status
        msg_handler(SET_TV_CONNECTION, "connecting");
        devAddress = _device.getAddress();
        editor = pref.edit();
        editor.putString("first_device",devAddress);
        editor.apply();

        Log.d(TAG,"Connecting to " + devAddress);
        GattClientCallback gatt_client_cb= new GattClientCallback();
        ble_gatt_= _device.connectGatt( context, false, gatt_client_cb );// %CONNECT% 
    

    /*
    Disconnect Gatt Server
    */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void disconnectGattServer() 
        Log.d( TAG, "Closing Gatt connection" );
        msg_handler(SET_TV_STATUS,"disconnect");
        // reset the connection flag
        connected_= false;
        // disconnect and close the gatt
        if( ble_gatt_ != null ) 
            ble_gatt_.disconnect();
            ble_gatt_.close();
        
    

    /*
    Request BLE enable
    */
    private void requestEnableBLE() 
        Intent ble_enable_intent= new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE );
        ((MainActivity)context).startActivityForResult( ble_enable_intent, REQUEST_ENABLE_BT );
    

    /*
    Request Fine Location permission
     */
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void requestLocationPermission() 
        ((MainActivity)context).requestPermissions( new String[] Manifest.permission.ACCESS_FINE_LOCATION, REQUEST_FINE_LOCATION );
    

    /*
    connect to the saved device
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public boolean connectSavedDevice() 
        String result = pref.getString("first_device", "0");
        if(result.equals("0")) 
            Log.d(TAG,"no saved device");
            return false;
        

        BluetoothDevice device = ble_adapter_.getRemoteDevice(result);
        GattClientCallback gatt_client_cb= new GattClientCallback();
        ble_gatt_= device.connectGatt( context, false, gatt_client_cb ); //  %connect%뜨는거
        return true;
    

    /*
    Send Data
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void sendData(byte[] strByte) 
        // check connection
        if( !connected_ )
        
            Log.e( TAG, "Failed to sendData due to no connection" );

            return;
        

        // disconnect if the characteristic is not found
        if( cmd_characteristic == null ) 
            Log.e( TAG, "Unable to find cmd characteristic" );
            disconnectGattServer();
            return;
        

        System.out.print("len : " + strByte.length + "->");
        for(int i=0;i<strByte.length;i++) 
            System.out.print((int)strByte[i] + " ");
        
        System.out.println();

        // start stimulation
        startStimulation( cmd_characteristic, strByte );
    

    /*
    Start stimulation
    @param cmd_characteristic command characteristic instance
    @param program_id stimulation program id
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void startStimulation(BluetoothGattCharacteristic _cmd_characteristic, byte[] strByte ) 
        _cmd_characteristic.setValue( strByte );
        boolean success= ble_gatt_.writeCharacteristic( _cmd_characteristic );

        if( success ) 
            Log.d( TAG, "send data complete" );
        
        else
        
            Log.e( TAG, "Failed to write command" );
        
    

    final Handler handler = new Handler() 
        public void handleMessage(Message msg) 
            switch(msg.what) 
                case SET_TV_STATUS: 
                    ((MainActivity)context).tv_status_handler(msg.obj.toString());
                    break;
                case SET_TV_CONNECTION: 
                    ((MainActivity)context).tv_connection_handler(msg.obj.toString());
                    break;
            
        
    ;

    public void msg_handler(int set_tv, String text) 
        Message msg = handler.obtainMessage();
        msg.what = set_tv;
        msg.obj = text;
        handler.sendMessage(msg);
        return;
    

    /*
    BLE Scan Callback class
    */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private class BLEScanCallback extends ScanCallback 
        private Map<String, BluetoothDevice> cb_scan_results_;

        /*
        Constructor
         */
        BLEScanCallback( Map<String, BluetoothDevice> _scan_results ) 
            cb_scan_results_= _scan_results;
        

        @Override
        public void onScanResult( int _callback_type, ScanResult _result ) 
            Log.d( TAG, "onScanResult" );
            addScanResult( _result );
        

        @Override
        public void onBatchScanResults( List<ScanResult> _results ) 
            for( ScanResult result: _results ) 
                addScanResult( result );
            
        

        @Override
        public void onScanFailed( int _error ) 
            Log.e( TAG, "BLE scan failed with code " +_error );
        

        /*
        Add scan result
         */
        private void addScanResult( ScanResult _result ) 
            // get scanned device
            BluetoothDevice device= _result.getDevice();
            // save devices for name
            String device_name = device.getName();
            // add the device to the result list
            cb_scan_results_.put( device_name, device );
            // log
            Log.d( TAG, "scan results device: " + device_name );
            msg_handler(SET_TV_STATUS,"scanned device : " + device_name);
        
    

    /*
    Gatt Client Callback class
    */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private class GattClientCallback extends BluetoothGattCallback 
//        @Override
//        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) 
//            super.onDescriptorWrite(gatt, descriptor, status);
//        

        @Override
        public void onConnectionStateChange(BluetoothGatt _gatt, int _status, int _new_state ) 
            super.onConnectionStateChange( _gatt, _status, _new_state );
            if( _status == BluetoothGatt.GATT_FAILURE ) 
                disconnectGattServer();
                return;
             else if( _status != BluetoothGatt.GATT_SUCCESS ) 
                disconnectGattServer();
                return;
            
            if( _new_state == BluetoothProfile.STATE_CONNECTED ) 
//                // 연결완료 되었을 시 1. set communication, 2. get standby state, 3. get basic data 동기화하기.
//                sendData(SET_COMM_START);
//                MainActivity.packet_sleep(200);
//                sendData(GET_STANDBY_STATE); //get airtop standby state(1.3)
//                MainActivity.packet_sleep(200);
//                if(airtopDevice.standby_state == ACTIVE_STATE) //device active 상태이면 basic data 확인
//                    sendData(GET_BASIC_DATA); //get airtop basic data(1.1)

//                // update the connection status message
//                msg_handler(SET_TV_STATUS,"연결 완료");
//                msg_handler(SET_TV_CONNECTION,"연결됨");
                // set the connection flag
                connected_= true;
                Log.d( TAG, "Connected to the GATT server" );
                _gatt.discoverServices();
             else if ( _new_state == BluetoothProfile.STATE_DISCONNECTED ) 
                disconnectGattServer();
            
        

        @Override
        public void onServicesDiscovered( BluetoothGatt _gatt, int _status ) 
            super.onServicesDiscovered( _gatt, _status );
            // check if the discovery failed
            if( _status != BluetoothGatt.GATT_SUCCESS ) 
                Log.e( TAG, "Device service discovery failed, status: " + _status );
                return;
            
            // find discovered characteristics
            List<BluetoothGattCharacteristic> matching_characteristics= BluetoothUtils.findBLECharacteristics(_gatt);
            if( matching_characteristics.isEmpty() ) 
                Log.e( TAG, "Unable to find characteristics" );
                return;
            
            // log for successful discovery
            Log.d( TAG, "Services discovery is successful" );

            // find command characteristics from the GATT server
            cmd_characteristic= BluetoothUtils.findCommandCharacteristic( ble_gatt_ );

            //setCharacteristicNotification
            ble_gatt_.setCharacteristicNotification(cmd_characteristic, true);
            BluetoothGattDescriptor descriptor = cmd_characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            ble_gatt_.writeDescriptor(descriptor); //descriptor write operation successfully started?
            // stream open complete.

            // update the connection status message
            msg_handler(SET_TV_STATUS,"connect complete");

            // stream open 이후 send data -> main에서 sleep 이후 보내줌.
//            sendData(SET_COMM_START);
//            MainActivity.packet_sleep(200);
//            sendData(GET_STANDBY_STATE); //get airtop standby state(1.3)
//            MainActivity.packet_sleep(200);
//            if(airtopDevice.standby_state == ACTIVE_STATE) //device active 상태이면 basic data 확인
//                sendData(GET_BASIC_DATA); //get airtop basic data(1.1)
        

        @Override
        public void onCharacteristicChanged( BluetoothGatt _gatt, BluetoothGattCharacteristic _characteristic ) 
            super.onCharacteristicChanged( _gatt, _characteristic );

            Log.d( TAG, "characteristic changed: " + _characteristic.getUuid().toString() );
            readCharacteristic( _characteristic );
        

        @Override
        public void onCharacteristicWrite( BluetoothGatt _gatt, BluetoothGattCharacteristic _characteristic, int _status ) 
            super.onCharacteristicWrite( _gatt, _characteristic, _status );
            if( _status == BluetoothGatt.GATT_SUCCESS ) 
                Log.d( TAG, "Characteristic written successfully" );
             else 
                Log.e( TAG, "Characteristic write unsuccessful, status: " + _status) ;
                disconnectGattServer();
            
        

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) 
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) 
                Log.d (TAG, "Characteristic read successfully" );
                readCharacteristic(characteristic);
             else 
                Log.e( TAG, "Characteristic read unsuccessful, status: " + status);
                // Trying to read from the Time Characteristic? It doesnt have the property or permissions
                // set to allow this. Normally this would be an error and you would want to:
                // disconnectGattServer();
            
        

        /*
        Log the value of the characteristic
        @param characteristic
         */
        private void readCharacteristic( BluetoothGattCharacteristic _characteristic ) 
            byte[] msg = _characteristic.getValue();
            String str = new String(msg);

            for(int i=0;i<str.length();i++) 
                packetReceive.input_byte(str.charAt(i)); 
            

            String prt = new String();
            for(int i=0;i<msg.length;i++) 
                prt += " "+(int)msg[i];
            
            Log.d(TAG,"msg len"+msg.length + ": " +prt);
        
    


【问题讨论】:

when the bluetooth setting completed 哪个设置?你到底想要什么回调? 我的意思是设置特征和写入描述符。这些完成后我必须发送数据。所以我想要这个回调。 我在下面添加了连接部分代码。在“ble_info.connectSavedDevice();”之后,当我发送数据“ble_info.sendData(SET_COMM_START);”之后,他们使发送失败错误。所以我睡了好几次。然后发送没有出错。在这种情况下,我想按顺序制作代码。 ble_info 是什么,connectSavedDevice 有什么作用? 我在下面添加了 ble_info 类代码。此类包含蓝牙功能。并且connectSavedDevice通过gatt的mac地址与设备连接。 【参考方案1】:

你可以实现onConnectionStateChange。这会让你知道你的连接是否成功。

@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) 
    if (newState == BluetoothProfile.STATE_CONNECTED) 
      // you can send data here
     else if (newState == BluetoothProfile.STATE_DISCONNECTED) 

    

【讨论】:

我添加了包含 GattClientCallback 的 ble_info 类代码。在代码的 onConnectionStateChange 方法中,我尝试了与注释相同的方法。但由于没有连接,发送数据失败。 连接到 BLE 时你得到哪个 newState?如果你得到 STATE_CONNECTED 那么你已经连接,所以你可以在那里发送数据,但要确保你有互联网连接,否则 api 将不会被调用。

以上是关于我想知道 BLE gatt 的设置完成时间,比如回调的主要内容,如果未能解决你的问题,请参考以下文章

用于打开/关闭 LED 的 BLE 设备(服务器)的 GATT 配置文件

GATT连接后如何与BLE设备配对

Android BLE GATT 外设模式通知

蓝牙 GATT 向 BLE 设备发送数据

从连接的 BLE 设备的 GATT 服务器断开连接

阻止 BLE 设备连接到 GATT 服务器