Android 获得低功耗蓝牙 (BLE) 设备通知的步骤是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20907411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:40:33  来源:igfitidea点击:

What are the steps to get notified by Bluetooth Low Energy (BLE) device?

androidbluetooth-lowenergy

提问by Arun Badole

I am working on a Bluetooth Low Energy (BLE) app. I have a BLE device (scale) which measures weight. I am able to connect with this device. But I am not getting how to read data (weight value) from it.

我正在开发低功耗蓝牙 (BLE) 应用程序。我有一个测量重量的 BLE 设备(秤)。我可以连接到这个设备。但我不知道如何从中读取数据(权重值)。

I want to know if my app is connected to any BLE device, so what are the steps to get notified by device in order to get updated data.

我想知道我的应用程序是否已连接到任何 BLE 设备,那么通过设备获取通知以获取更新数据的步骤是什么。

Okay, following is the for my Activity which I am using..

好的,以下是我正在使用的活动。

public class BlogBLEActivity extends Activity implements OnItemClickListener
{
    private final static String TAG = BlogBLEActivity.class.getSimpleName();

    private BluetoothAdapter bluetoothAdapter;
    BluetoothManager bluetoothManager;

    boolean hasBleFeature = false;

    TextView tvMessage;
    int messageId = R.string.doesnt_support_ble;
    int colorId = android.R.color.holo_red_light;

    private boolean mScanning;
    private Handler handler = new Handler();

    private static final long SCAN_PERIOD = 10000;
    private static final int REQUEST_ENABLE_BT = 1209;

    ListView listView;
    ArrayList<BluetoothDevice> listDevices;

    BleDeviceAdapter bleDeviceAdapter;

    TextView tvHumidity;
    TextView tvTemperature;
    TextView tvPressure;

    boolean isConnected = false;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.blog_ble);

        initParameters();
        initViews();

        scanLeDevice(true);
    }

    @SuppressLint("NewApi")
    void initParameters()
    {
        hasBleFeature = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
        Log.i(TAG, "hasBleFeature : " + hasBleFeature);

        if (hasBleFeature)
        {
            messageId = R.string.supports_ble;
            colorId = android.R.color.holo_blue_light; 
        } else
        {
            messageId = R.string.doesnt_support_ble;
            colorId = android.R.color.holo_red_light;
        }

        bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();// BluetoothAdapter.getDefaultAdapter();

        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled())
        {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

        listDevices = new ArrayList<BluetoothDevice>();
        bleDeviceAdapter = new BleDeviceAdapter(this, listDevices);
    }

    void initViews()
    {
        tvHumidity = (TextView) findViewById(R.id.blog_ble_tv_humidity);
        tvTemperature = (TextView) findViewById(R.id.blog_ble_tv_temprature);
        tvPressure = (TextView) findViewById(R.id.blog_ble_tv_pressure);

        tvMessage = (TextView) findViewById(R.id.blog_ble_tv_message);
        tvMessage.setText(getResources().getString(messageId));
        tvMessage.setTextColor(getResources().getColor(colorId));

        listView = (ListView) findViewById(R.id.blog_ble_list_view);
        listView.setAdapter(bleDeviceAdapter);
        listView.setOnItemClickListener(this);
    }

    @SuppressLint("NewApi")
    void scanLeDevice(final boolean enable)
    {
        if (enable)
        {
            handler.postDelayed(new Runnable()
            {
                @SuppressLint("NewApi")
                @Override
                public void run()
                {
                    mScanning = false;
                    bluetoothAdapter.stopLeScan(leScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = false;
            bluetoothAdapter.startLeScan(leScanCallback);
        } else
        {
            mScanning = false;
            bluetoothAdapter.stopLeScan(leScanCallback);
        }
    }

    @SuppressLint("NewApi")
    private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback()
    {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)
        {
            runOnUiThread(new Runnable()
            {
                @Override
                public void run()
                {
                    if (device != null)
                    {
                        bleDeviceAdapter.add(device);
                        bleDeviceAdapter.notifyDataSetChanged();
                    }
                }
            });
        }
    };

    class BleDeviceAdapter extends ArrayAdapter<BluetoothDevice>
    {
        public BleDeviceAdapter(Context context, List<BluetoothDevice> objects)
        {
            super(context, R.layout.row_ble_device, R.id.row_ble_device_tv_name, objects);
        }

        @SuppressLint("NewApi")
        @Override
        public View getView(int position, View convertView, ViewGroup parent)
        {
            View row = super.getView(position, convertView, parent);
            ViewHolder holder = (ViewHolder) row.getTag();
            if (holder == null)
            {
                holder = new ViewHolder(row);
                row.setTag(holder);
            }

            BluetoothDevice device = getDevice(position);
            holder.tvName.setText("" + device.getName());

            Log.i(TAG, "" + device.getName());
            return row;
        }
    }

    BluetoothDevice getDevice(int position)
    {
        return (BluetoothDevice) listView.getAdapter().getItem(position);
    }

    @SuppressLint("NewApi")
    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
    {
        BluetoothDevice device = getDevice(position);
        Toast.makeText(this, "" + device.getName(), Toast.LENGTH_SHORT).show();
        BluetoothGatt connectGatt = device.connectGatt(this, false, mGattCallback);

    }

    /* Client Configuration Descriptor */
    private static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    private static final UUID KITCHEN_SCALE_SERVICE = UUID.fromString("0000780a-0000-1000-8000-00805f9b34fb");
    private static final UUID KITCHEN_SCALE_FEATURE_CHAR = UUID.fromString("00008aa0-0000-1000-8000-00805f9b34fb");
    private static final UUID KITCHEN_SCALE_MEASUREMENT_CHAR = UUID.fromString("00008aa1-0000-1000-8000-00805f9b34fb");
    private static final UUID KITCHEN_SCALE_INTERMEDIATE_CHAR = UUID.fromString("00008aa2-0000-1000-8000-00805f9b34fb");

    /*
     * In this callback, we've created a bit of a state machine to enforce that
     * only one characteristic be read or written at a time until all of our
     * sensors are enabled and we are registered to get notifications.
     */
    @SuppressLint("NewApi")
    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
    {

        /* State Machine Tracking */
        private int mState = 0;

        private void reset()
        {
            mState = 0;
        }

        private void advance()
        {
            mState++;
        }

        /*
         * Send an enable command to each sensor by writing a configuration
         * characteristic. This is specific to the SensorTag to keep power low
         * by disabling sensors you aren't using.
         */
        private void enableNextSensor(BluetoothGatt gatt)
        {
            BluetoothGattCharacteristic characteristic;
            switch (mState)
            {
            case 0:
                Log.i(TAG, "Enabling weight scale");
                characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_FEATURE_CHAR);
                Log.i(TAG, "Feature Properties : "+characteristic.getProperties());
                characteristic.setValue(new byte[]
                { 0x09 });
                break;

            default:
                mHandler.sendEmptyMessage(MSG_DISMISS);
                Log.i(TAG, "All Sensors Enabled");
                return;
            }

            gatt.writeCharacteristic(characteristic);
        }

        /*
         * Read the data characteristic's value for each sensor explicitly
         */
        private void readNextSensor(BluetoothGatt gatt)
        {
            BluetoothGattCharacteristic characteristic;
            switch (mState)
            {
            case 0:
                Log.i(TAG, "Reading weight cal");
                characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
                break;

            default:
                mHandler.sendEmptyMessage(MSG_DISMISS);
                Log.i(TAG, "All Sensors Enabled");
                return;
            }

            gatt.readCharacteristic(characteristic);
        }

        /*
         * Enable notification of changes on the data characteristic for each
         * sensor by writing the ENABLE_NOTIFICATION_VALUE flag to that
         * characteristic's configuration descriptor.
         */
        private void setNotifyNextSensor(BluetoothGatt gatt)
        {
            BluetoothGattCharacteristic characteristic;
            switch (mState)
            {
            case 0:
                Log.i(TAG, "Set notify weight ");
                characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
                break;

            default:
                mHandler.sendEmptyMessage(MSG_DISMISS);
                Log.i(TAG, "All Sensors Enabled");
                return;
            }

            // Enable local notifications
            gatt.setCharacteristicNotification(characteristic, true);
            // Enabled remote notifications
            BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
            desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            gatt.writeDescriptor(desc);
        }

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
        {
            Log.i(TAG, "Connection State Change: " + status + " -> " + connectionState(newState));
            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED)
            {
                /*
                 * Once successfully connected, we must next discover all the
                 * services on the device before we can read and write their
                 * characteristics.
                 */
                gatt.discoverServices();
                mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services..."));
            } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED)
            {
                /*
                 * If at any point we disconnect, send a message to clear the
                 * weather values out of the UI
                 */

                mHandler.sendEmptyMessage(MSG_CLEAR);
            } else if (status != BluetoothGatt.GATT_SUCCESS)
            {
                /*
                 * If there is a failure at any stage, simply disconnect
                 */
                gatt.disconnect();
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status)
        {
            Log.i(TAG, "Services Discovered: " + status);
            if (status == BluetoothGatt.GATT_SUCCESS)
            {
                Log.i(TAG, "No of services discovered: " + gatt.getServices().size());
                mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "No of services discovered: " + gatt.getServices().size()));

                List<BluetoothGattService> services = gatt.getServices();
                for (BluetoothGattService bluetoothGattService : services)
                {
                    UUID uuid = bluetoothGattService.getUuid();
                    Log.e(TAG, ""+uuid.toString());
                    List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
                    for (BluetoothGattCharacteristic bluetoothGattCharacteristic : characteristics)
                    {
                        UUID uuidC = bluetoothGattCharacteristic.getUuid();
                        Log.i(TAG, "Gatt Properties : "+bluetoothGattCharacteristic.getProperties());
                        Log.i(TAG, ""+uuidC.toString());
                        CharacteristicHelper helper = new CharacteristicHelper(bluetoothGattCharacteristic);
                        Log.i(TAG, "isRead : "+helper.isRead());
                        Log.i(TAG, "isWrite : "+helper.isWrite());
                        Log.i(TAG, "isNotify : "+helper.isNotify());
                        Log.i(TAG, "isWriteNoResponse : "+helper.isWriteNoResponse());
                    }
                }
            }
            // mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS,
            // "Enabling Sensors..."));
            /*
             * With services discovered, we are going to reset our state machine
             * and start working through the sensors we need to enable
             */
             reset();
             enableNextSensor(gatt);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
        {
            Log.i(TAG, "onCharacteristicRead");
            // For each read, pass the data up to the UI thread to update the
            // display
            /**methodToUpdateUI().*/

            // After reading the initial value, next we enable notifications
            setNotifyNextSensor(gatt);
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
        {
            Log.i(TAG, "onCharacteristicWrite");
            // After writing the enable flag, next we read the initial value
            readNextSensor(gatt);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
        {
            Log.i(TAG, "onCharacteristicChanged");
            /*
             * After notifications are enabled, all updates from the device on
             * characteristic value changes will be posted here. Similar to
             * read, we hand these up to the UI thread to update the display.
             */
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
        {
            Log.i(TAG, "onDescriptorWrite");
            // Once notifications are enabled, we move to the next sensor and
            // start over with enable
            advance();
            enableNextSensor(gatt);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
        {
            Log.i(TAG, "Remote RSSI: " + rssi);
        }

        private String connectionState(int status)
        {
            switch (status)
            {
            case BluetoothProfile.STATE_CONNECTED:
                return "Connected";
            case BluetoothProfile.STATE_DISCONNECTED:
                return "Disconnected";
            case BluetoothProfile.STATE_CONNECTING:
                return "Connecting";
            case BluetoothProfile.STATE_DISCONNECTING:
                return "Disconnecting";
            default:
                return String.valueOf(status);
            }
        }
    };

    /*
     * We have a Handler to process event results on the main thread
     */
    private static final int MSG_PROGRESS = 201;
    private static final int MSG_DISMISS = 202;
    private static final int MSG_CLEAR = 301;
    private Handler mHandler = new Handler()
    {
        @SuppressLint("NewApi")
        @Override
        public void handleMessage(Message msg)
        {
            BluetoothGattCharacteristic characteristic;
            switch (msg.what)
            {
            case MSG_PROGRESS:
                tvMessage.setText((String) msg.obj);
                break;
            case MSG_DISMISS:
                tvMessage.setText("Service Enabled");
                break;
            case MSG_CLEAR:
                tvMessage.setText("");
                break;
            }
        }
    };
}

In my activity, first of all I am scanning all the available devices & preparing ListView. On clicking on list item I connect to that particular device. When device's status become connected then I discover services. I have UUIDs of the device's service & its characteristics. But I am not sure how to write to any particular characteristics or enable or read data from it. Although I have tried this thing but I don't see any success.

在我的活动中,首先我正在扫描所有可用的设备并准备 ListView。单击列表项时,我连接到该特定设备。当设备的状态变为连接时,我会发现服务。我有设备服务及其特征的 UUID。但我不确定如何写入任何特定特征或启用或从中读取数据。虽然我已经尝试过这件事,但我没有看到任何成功。

If any one has any idea about it, so please help me.

如果有人对此有任何想法,请帮助我。

回答by etherel

Had a device which required me to use

有一个需要我使用的设备

descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)

instead of

代替

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)

as explained in this question

正如在这个问题中所解释的

Android BLE API: GATT Notification not received

Android BLE API:未收到 GATT 通知

回答by Skumar

Refer the source code of sample application "bluetoothlegatt" provided on developer portal.

请参阅开发人员门户上提供的示例应用程序“bluetoothlegatt”的源代码。

Sample service: http://developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.html

示例服务:http: //developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.html

Using service: http://developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.html

使用服务:http: //developer.android.com/samples/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.html

This example contains characteristics with read and notify properties. So you definitely find your solution. Please go to section with following code:(You may figure it out)

此示例包含具有读取和通知属性的特征。所以你肯定会找到你的解决方案。请转到带有以下代码的部分:(您可能会弄清楚)

public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }

回答by jp_eagle

you have to differentiate the value is notification or indication, and set respective value using descriptor.setValue. if you set wrongly, you will not get the value.

您必须区分值是通知还是指示,并使用descriptor.setValue 设置相应的值。如果您设置错误,您将无法获得该值。

回答by Doni

This one is working for me:

这个对我有用:

to notify master device that some characteristic is change, call this function on your pheripheral:

要通知主设备某些特性发生变化,请在您的外围设备上调用此函数:

private BluetoothGattServer server;
//init....

//on BluetoothGattServerCallback...

//call this after change the characteristic
server.notifyCharacteristicChanged(device, characteristic, false);

in your master device: enable setCharacteristicNotification after discover the service:

在您的主设备中:在发现服务后启用 setCharacteristicNotification:

@Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        services = mGatt.getServices();
        for(BluetoothGattService service : services){
            if( service.getUuid().equals(SERVICE_UUID)) {
                characteristicData = service.getCharacteristic(CHAR_UUID);
                for (BluetoothGattDescriptor descriptor : characteristicData.getDescriptors()) {
                    descriptor.setValue( BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                    mGatt.writeDescriptor(descriptor);
                }
                gatt.setCharacteristicNotification(characteristicData, true);
            }
        }
        if (dialog.isShowing()){
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    dialog.hide();
                }
            });
        }
   }

now you can check your characteristic value is change, for example onCharacteristicRead function :

现在您可以检查您的特征值是否发生变化,例如 onCharacteristicRead 函数:

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.i("onCharacteristicRead", characteristic.toString());
        byte[] value=characteristic.getValue();
        String v = new String(value);
        Log.i("onCharacteristicRead", "Value: " + v);
}