Android-蓝牙低功耗(BLE)

1,072 阅读14分钟

蓝牙低功耗(BLE)

Android 为发挥核心作用的蓝牙低功耗 (BLE) 提供内置平台支持,常见用例包括:

  • 在临近设备间传输少量数据。
  • 与近程传感器交互,根据用户当前的位置为用户提供个性化体验。

BLE 旨在大幅降低功耗。这样一来,应用就可以与功率要求更严格的 BLE 设备(如近程传感器、心率监测器和健身设备)进行通信。

注意: 当用户使用 BLE 将其设备与其他设备配对时,用户设备上的所有应用都可以访问在两台设备之间传输的数据。因此,捕获敏感数据,需要实现应用层安全机制来保护这些数据的隐私。

客户查找 BLE 设备

BLE 设备大范围扫描

查找 BLE 设备,使用 BluetoothLeScanner 的startScan() 方法。此方法将 ScanCallback 作为参数。 实现ScanCallback回调,已实现返回扫描结果的接收。 由于扫描非常耗电,因此您应遵循以下准则:

  • 找到所需设备后,立即停止扫描。
  • 永不循环扫描,并始终为扫描设置时间限制。 之前可用的设备可能移到了覆盖范围内,继续扫描会耗尽电池电量。 示例代码:
private BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
private boolean scanning;
private Handler handler = new Handler();

// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;

private void scanLeDevice() {
    if (!scanning) {
        // Stops scanning after a predefined scan period.
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                scanning = false;
                bluetoothLeScanner.stopScan(leScanCallback);
            }
        }, SCAN_PERIOD);

        scanning = true;
        bluetoothLeScanner.startScan(leScanCallback);
    } else {
        scanning = false;
        bluetoothLeScanner.stopScan(leScanCallback);
    }
}

注意 :只有当设备上当前启用了蓝牙时,才能从 BluetoothAdapter 获取 BluetoothLeScanner。如果蓝牙未启用,则 getBluetoothLeScanner() 会返回 null。

BLE设备特定扫描。

如需仅扫描特定类型的外围设备,改为调用BluetoothLeScanner的 startScan(List, ScanSettings, ScanCallback),并提供一个 ScanFilter 对象列表(用于限制扫描所查找的设备)和一个 ScanSettings 对象(用于指定扫描的相关参数)。

  • 1.List:这是一个扫描过滤器列表,用于指定我们感兴趣的设备的筛选条件,例如设备的名称、UUID 等。只有符合这些条件的设备才会被返回。
List<ScanFilter>  的ScanFilter:
    private final String mDeviceName;
    private final String mDeviceAddress;
    private final ParcelUuid mServiceUuid;
    private final ParcelUuid mServiceUuidMask;
    private final ParcelUuid mServiceDataUuid;
    private final byte[] mServiceData;
    private final byte[] mServiceDataMask;
    private final int mManufacturerId;
    private final byte[] mManufacturerData;
    private final byte[] mManufacturerDataMask;
  • 2.ScanSettings:这是一个包含了扫描设置的对象,可以指定扫描模式、扫描间隔、匹配模式等参数。
  • 3.ScanCallback:这是一个回调对象,用于接收扫描结果和状态变化的通知,例如找到新设备、设备信号强度变化等。 在调用 startScan 方法后,系统将开始扫描周围的 BLE 设备,并根据提供的过滤器和设置进行筛选。当发现符合条件的设备时,将通过 ScanCallback 中的相应回调方法来通知应用程序。
private void scanLeDevice2() {
        List<ScanFilter> filters = new ArrayList<>();
        ScanSettings settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                .build();
        if (!scanning) {
            // Stops scanning after a predefined scan period.
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    scanning = false;
                    bluetoothLeScanner.stopScan(leScanCallback);
                }
            }, SCAN_PERIOD);

            scanning = true;
            bluetoothLeScanner.startScan(filters, settings, leScanCallback);
        } else {
            scanning = false;
            bluetoothLeScanner.stopScan(leScanCallback);
        }
    }

ble蓝牙扫描回调,onScanResult 返回扫描结果,通过返回信息可以连接设备 onBatchScanResults 特定设备扫描返回列表

ScanCallback leScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            leDeviceListAdapter.add("发现设备:" + result.getDevice().getName() + " : " + result.getDevice().getAddress());
            leDeviceListAdapter.notifyDataSetChanged();
//            Log.d(TAG, "[onScanResult] callbackType :" + callbackType + " , ScanResult : " + result.toString());
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            Log.d(TAG, "[onBatchScanResults] 列表 :" + results.size());
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            Log.d(TAG, "[onScanFailed] 错误信息code :" + errorCode);
        }

    };

ble客户端连接设备

使用BluetoothDevice.connectGatt(Context,autoConnect,BluetoothGattCallback). 1,context对象 2.设置是否自动连接 3.BluetoothGattCallback 异步的GATT回调。

 bluetoothGatt = device.connectGatt(BLEActivity.this, true, bluetoothGattCallback);

GATT回调(BluetoothGattCallback)

GATT(通用属性配置文件)是一种常见的协议,用于在BLE设备之间进行数据交换。

重点关注 1.onConnectionStateChange(BluetoothGatt gatt, int status, int newState): 当连接状态发生变化时调用。status 表示操作的状态(成功或失败),newState 表示新的连接状态(如 STATE_CONNECTED、STATE_DISCONNECTED 等)。 2.onServicesDiscovered(BluetoothGatt gatt, int status): 当远程设备的服务被发现时调用。status 表示发现服务的状态(成功或失败)。你可以在此方法中获取远程设备的服务列表。 3.onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status): 当从远程设备读取特征值时调用。characteristic 包含读取的特征值数据,status 表示读取操作的状态(成功或失败)。 4.onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status): 当向远程设备写入特征值时调用。characteristic 包含写入的特征值数据,status 表示写入操作的状态(成功或失败)。 5.onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic): 当远程设备的特征值发生变化时调用。characteristic 包含新的特征值数据。 6.onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status): 当从远程设备读取描述符值时调用。descriptor 包含读取的描述符值数据,status 表示读取操作的状态(成功或失败)。 7.onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status): 当向远程设备写入描述符值时调用。descriptor 包含写入的描述符值数据,status 表示写入操作的状态(成功或失败)。 8.onReliableWriteCompleted(BluetoothGatt gatt, int status): 当可靠写操作完成时调用(通常与多个特征值写入相关)。status 表示操作的状态(成功或失败)。

9.onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status): 当读取远程设备的RSSI(Received Signal Strength Indication)值时调用。rssi 表示远程设备的RSSI值,status 表示读取操作的状态(成功或失败)。

10.onMtuChanged(BluetoothGatt gatt, int mtu, int status): 当MTU(Maximum Transmission Unit)改变时调用。mtu 表示新的MTU值,status 表示操作的状态(成功或失败)。

    BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
            Log.d(TAG, "[bluetoothGattCallback] onPhyUpdate :");
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
            Log.d(TAG, "[bluetoothGattCallback] onPhyRead :");
        }

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
// 处理连接状态变化
            Log.d(TAG, "[bluetoothGattCallback] onConnectionStateChange :"
                    + (newState == BluetoothProfile.STATE_CONNECTED ? "已连接" : "链接断开")
                    + ", status:" + status + ", BluetoothGatt: " + gatt.getDevice().toString()
            );
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                sendBLEmsg();
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
          // 服务发现完成,可以读取和写入特征值
            Log.d(TAG, "[bluetoothGattCallback] onServicesDiscovered :");
List<BluetoothGattService> services = gatt.getServices();
          // 遍历服务并获取特征值
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
          // 特征值读取结果
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "[bluetoothGattCallback] onCharacteristicRead SUCCESS :"
                        + characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0));

              //byte[] value = characteristic.getValue();
            // 处理读取到的数据
            } else {
                Log.d(TAG, "[bluetoothGattCallback] onCharacteristicRead faile:");
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
           // 特征值写入结果
            if (status == BluetoothGatt.GATT_SUCCESS) {
           // 写入成功
                Log.d(TAG, "[bluetoothGattCallback] onCharacteristicWrite SUCCESS :"
                        + characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0));
            }else {
                Log.d(TAG, "[bluetoothGattCallback] onCharacteristicWrite faile :");
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.d(TAG, "[bluetoothGattCallback] onCharacteristicChanged :");
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            Log.d(TAG, "[bluetoothGattCallback] onDescriptorRead :");
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            Log.d(TAG, "[bluetoothGattCallback] onDescriptorWrite :");
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
            Log.d(TAG, "[bluetoothGattCallback] onReliableWriteCompleted :");
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            Log.d(TAG, "[bluetoothGattCallback] onReadRemoteRssi :");
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            Log.d(TAG, "[bluetoothGattCallback] onMtuChanged :");
        }
    };

回调方法能够实现在与远程BLE设备进行通信的不同阶段处理相关事件。通过实现这些回调方法来处理读取、写入、通知、连接状态变化等操作,并获取相应的状态和数据。

注意,回调方法都是在与远程BLE设备的通信线程中调用的,因此避免在这些方法中进行耗时的操作,以免阻塞通信线程。

ble客户端发送特征消息

使用 BluetoothGattService.writeCharacteristic(BluetoothGattCharacteristic)

private void sendBLEmsg() {
        Log.d(TAG, "[sendBLEmsg] 选择设备开始发送信息 :----------------》》》 ");

        // 获取服务列表
        List<BluetoothGattService> services = bluetoothGatt.getServices();

// 遍历服务列表
        for (BluetoothGattService service : services) {
            String uuid = service.getUuid().toString();
            Log.d(TAG, "[sendBLEmsg] uuid  : " + uuid);
            // 获取特征值列表
            List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
            // 遍历特征值列表
            for (BluetoothGattCharacteristic characteristic : characteristics) {
                Log.d(TAG, "[sendBLEmsg] getUuid  : " + characteristic.getUuid());
                // 判断特征值的 UUID 是否符合要求
                if (characteristic.getUuid().equals(MY_UUID)) {
                    // 设置要发送的数据
                    byte[] data = "Hello, BLE 我是客户端数据----->".getBytes();

                    // 设置特征值的值
                    characteristic.setValue(100, BluetoothGattCharacteristic.FORMAT_UINT16, 0);

                    // 【发送数据到服务端】
                    bluetoothGatt.writeCharacteristic(characteristic);
                    Log.d(TAG, "[sendBLEmsg] xx消息已发送---- : " + data.toString());
                }
            }
        }
    }


// 【设置写入特征值的回调】
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
    // ...

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // 写入成功
        } else {
            // 写入失败
        }
    }

首先需要根据服务UUID获取到服务对象service,然后根据特征值UUID获取到特征值对象characteristic。接下来,设置要写入的信息,并将信息字节数组设置到特征值中,然后通过调用gatt.writeCharacteristic(characteristic)方法来写入信息到特征值。

在BluetoothGattCallback的onCharacteristicWrite()方法中,当特征值写入完成时,会通过status参数判断写入是否成功。如果写入成功,可以进行相应的处理,如果写入失败,也需要进行错误处理。

ble客户端读取服务端的特征消息

使用 BluetoothGattService. readCharacteristic(BluetoothGattCharacteristic)

// 获取服务
BluetoothGattService service = gatt.getService(serviceUuid);
if (service != null) {
    // 获取特征值
    BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
    if (characteristic != null) {
        // 读取特征值
        gatt.readCharacteristic(characteristic);
    }
}

// 设置读取特征值的回调
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
    // ...

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            byte[] value = characteristic.getValue();
            String message = new String(value, Charset.forName("UTF-8"));
            // 处理读取到的信息
        }
    }

    // ...
};

首先需要根据服务UUID获取到服务对象service,然后根据特征值UUID获取到特征值对象characteristic。接下来,通过调用gatt.readCharacteristic(characteristic)方法来读取特征值的内容。 在BluetoothGattCallback的onCharacteristicRead()方法中,当特征值读取完成时,会通过status参数判断读取是否成功。如果成功,可以通过characteristic.getValue()获取到特征值的字节数组,然后根据需要将字节数组转换为字符串或其他数据类型来处理读取到的信息

ble 服务端

开启蓝牙、蓝牙可见、权限等都是标准操作。

检查设备是否支持BLE

// 检查设备是否支持BLE
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            // 设备不支持BLE
            Log.e(TAG,  "该设备不支持蓝牙低功耗");
        } else {
            // 设备支持BLE
            Log.d(TAG,"该设备支持蓝牙低功耗");
        }

开启ble蓝牙服务端

在BLE通信中,服务端是指提供蓝牙服务的设备,也称为外围设备(Peripheral Device)。服务端可以提供多个服务(Services),每个服务包含一个或多个特征值(Characteristics)。特征值用于读取、写入和通知数据。

  • 使用 BluetoothManager.openGattServer(Context,BluetoothGattServerCallback)创建一个BluetoothGattServer对象
  • 使用BluetoothGattCharacteristic(UUID,serviceType)创建特征 serviceType The type of this service, {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} or {@link BluetoothGattService#SERVICE_TYPE_SECONDARY}
  • 使用BluetoothGattDescriptor(UUID,permissions)特征描述 常用permissions BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_WRITE
 bluetoothManager = this.getSystemService(BluetoothManager.class);
        bluetoothAdapter = bluetoothManager.getAdapter();
        // 创建一个BluetoothGattServer对象
        bluetoothGattServer = bluetoothManager.openGattServer(this, bluetoothGattServerCallback);
        // 创建一个服务
        //辅助服务(包含在主要服务中)\主要服务
        BluetoothGattService service = new BluetoothGattService(UUID_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);
        //创建可读特征
        //uuid-此特性的uuid,properties-此特性的属性,permissions-此特性的权限
        characteristicRead = new BluetoothGattCharacteristic(UUID_CHARACTERISTIC,
                BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
                BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
        //添加可读characteristic的descriptor
        BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_CHARACTERISTIC, BluetoothGattCharacteristic.PERMISSION_WRITE);
        characteristicRead.addDescriptor(descriptor);
        //创建可写特征
        //添加指定UUID的可写characteristic
        BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHARACTERISTIC,
                BluetoothGattCharacteristic.PROPERTY_WRITE |
                        BluetoothGattCharacteristic.PROPERTY_READ |
                        BluetoothGattCharacteristic.PROPERTY_NOTIFY,
                BluetoothGattCharacteristic.PERMISSION_WRITE);
        // 添加特征值到服务中
        service.addCharacteristic(characteristicWrite);
        service.addCharacteristic(characteristicRead);
        // 将服务添加到Gatt服务器中
        bluetoothGattServer.addService(service);

ble服务回调

BluetoothGattServerCallback回调接口,用于处理与BLE蓝牙服务端相关的事件和操作。通过实现BluetoothGattServerCallback接口,你可以监听和响应来自客户端的请求、连接状态变化等事件。 1.onConnectionStateChange(BluetoothDevice device, int status, int newState): 当与客户端的连接状态发生变化时调用,可以通过newState参数获取新的连接状态。 2.onServiceAdded(int status, BluetoothGattService service): 当服务添加到Gatt服务器中时调用,可以通过status参数判断是否添加成功。 3.onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic): 当客户端请求读取特征值时调用,可以通过requestId参数发送响应,并在BluetoothGattServer.sendResponse()方法中设置相应的特征值数据。 4.onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value): 当客户端请求写入特征值时调用,可以通过requestId参数发送响应,并在BluetoothGattServer.sendResponse()方法中设置写入操作的结果。 5.onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor): 当客户端请求读取描述符时调用,可以通过requestId参数发送响应,并在BluetoothGattServer.sendResponse()方法中设置相应的描述符数据。 6.onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value): 当客户端请求写入描述符时调用,可以通过requestId参数发送响应,并在BluetoothGattServer.sendResponse()方法中设置写入操作的结果。

 /**
     * 服务事件的回调
     */
    private BluetoothGattServerCallback bluetoothGattServerCallback = new BluetoothGattServerCallback() {

        /**
         * 1.连接状态发生变化时
         * @param device
         * @param status
         * @param newState
         */
        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("1.onConnectionStateChange:status = %s, newState =%s ", status, newState));
            super.onConnectionStateChange(device, status, newState);
        }

        @Override
        public void onServiceAdded(int status, BluetoothGattService service) {
            super.onServiceAdded(status, service);
            Log.e(TAG, String.format("onServiceAdded:status = %s", status));
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
            Log.e(TAG, String.format("onCharacteristicReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset));

            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
//            super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
        }

        /**
         * 3. onCharacteristicWriteRequest,接收具体的字节
         * @param device
         * @param requestId
         * @param characteristic
         * @param preparedWrite
         * @param responseNeeded
         * @param offset
         * @param requestBytes
         */
        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {
            Log.e(TAG, String.format("3.onCharacteristicWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("3.onCharacteristicWriteRequest:requestId = %s, preparedWrite=%s, responseNeeded=%s, offset=%s, value=%s", requestId, preparedWrite, responseNeeded, offset, requestBytes.toString()));
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, requestBytes);
            //4.处理响应内容
            onResponseToClient(requestBytes, device, requestId, characteristic);
        }

        /**
         * 2.描述被写入时,在这里执行 bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS...  收,触发 onCharacteristicWriteRequest
         * @param device
         * @param requestId
         * @param descriptor
         * @param preparedWrite
         * @param responseNeeded
         * @param offset
         * @param value
         */
        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            Log.e(TAG, String.format("2.onDescriptorWriteRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("2.onDescriptorWriteRequest:requestId = %s, preparedWrite = %s, responseNeeded = %s, offset = %s, value = %s,", requestId, preparedWrite, responseNeeded, offset, value.toString()));

            // now tell the connected device that this was all successfull
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
        }

        /**
         * 5.特征被读取。当回复响应成功后,客户端会读取然后触发本方法
         * @param device
         * @param requestId
         * @param offset
         * @param descriptor
         */
        @Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
            Log.e(TAG, String.format("onDescriptorReadRequest:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("onDescriptorReadRequest:requestId = %s", requestId));
//            super.onDescriptorReadRequest(device, requestId, offset, descriptor);
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
        }

        @Override
        public void onNotificationSent(BluetoothDevice device, int status) {
            super.onNotificationSent(device, status);
            Log.e(TAG, String.format("5.onNotificationSent:device name = %s, address = %s", device.getName(), device.getAddress()));
            Log.e(TAG, String.format("5.onNotificationSent:status = %s", status));
        }

        @Override
        public void onMtuChanged(BluetoothDevice device, int mtu) {
            super.onMtuChanged(device, mtu);
            Log.e(TAG, String.format("onMtuChanged:mtu = %s", mtu));
        }

        @Override
        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
            super.onExecuteWrite(device, requestId, execute);
            Log.e(TAG, String.format("onExecuteWrite:requestId = %s", requestId));
        }
    };

    /**
     * 4.处理响应内容
     *
     * @param reqeustBytes
     * @param device
     * @param requestId
     * @param characteristic
     */
    private void onResponseToClient(byte[] reqeustBytes, BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic) {
        Log.e(TAG, String.format("4.onResponseToClient:device name = %s, address = %s", device.getName(), device.getAddress()));
        Log.e(TAG, String.format("4.onResponseToClient:requestId = %s", requestId));
//        String msg = OutputStringUtil.transferForPrint(reqeustBytes);
        Log.e(TAG, "4.收到:");
        //println("4.收到:" + msg);
        //showText("4.收到:" + msg);

        String str = new String(reqeustBytes) + " hello>";
        characteristicRead.setValue(str.getBytes());
        bluetoothGattServer.notifyCharacteristicChanged(device, characteristicRead, false);

        Log.i(TAG, "4.响应:" + str);
//        MainActivity.handler.obtainMessage(MainActivity.DEVICE, new String(reqeustBytes)).sendToTarget();
    }

ble广播

在BLE蓝牙通信中,广播是一种常见的方式,用于向周围的设备宣传自身的存在和提供的服务。通过广播,蓝牙设备可以发送包含特定数据的信标包(Beacon Packet),其他设备可以通过扫描来接收这些广播包。

BluetoothLeAdvertiser advertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
        // 创建 AdvertiseSettings 对象,用于配置广播设置
        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
                .setConnectable(true)
                .setTimeout(0)
                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
                .build();

// 创建 AdvertiseData 对象,用于携带广播数据
        AdvertiseData data = new AdvertiseData.Builder()
                .setIncludeDeviceName(true)
                .build();

// 启动广播
        advertiser.startAdvertising(settings, data, advertiseCallback);
    }
    private AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            // 广播启动成功
            Log.d(TAG, "Peripheral advertising started  okokokkk");
        }

        @Override
        public void onStartFailure(int errorCode) {
            // 广播启动失败
            Log.e(TAG, "Peripheral   nonononn advertising failed with error code: " + errorCode);
        }
    };

首先获取到BluetoothLeAdvertiser对象,然后创建了广播数据。在AdvertiseSettings中,我们设置了广播模式、发射功率和连接性,根据需要进行调整。在AdvertiseData中,我们设置了要广播的服务UUID。

最后,我们通过调用bluetoothLeAdvertiser.startAdvertising()方法开始广播,传入之前创建的AdvertiseSettings和AdvertiseData对象。同时,我们还创建了一个AdvertiseCallback对象来处理广播的启动成功和失败回调。

需要注意的是,广播需要获得相应的权限,并且在Android设备上,只有支持BLE广播的设备才能进行BLE广播操作

ble 服务端接收客户端信息

BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() {
    @Override
    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);

        // 处理客户端写特征值请求
        if (characteristic.getUuid().equals(YOUR_CHARACTERISTIC_UUID)) {
            // 处理接收到的数据value
            // ...

            // 发送响应
            bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
        }
    }
};

首先定义了一个BluetoothGattServerCallback对象来处理服务端回调。在这个回调中,我们重写了onCharacteristicWriteRequest方法,它会在客户端请求写特征值时被触发。在这个方法中,我们首先判断是否是我们要处理的特征值,然后对接收到的数据进行处理。最后,我们通过调用bluetoothGattServer.sendResponse()方法发送响应,告知客户端已经成功写入特征值。

ble 服务端向客户端发送信息

BluetoothGattCharacteristic characteristic = yourService.getCharacteristic(YOUR_CHARACTERISTIC_UUID);

// 设置特征值Notification或Indication
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
characteristic.setProperties(BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_INDICATE);

// 发送数据
boolean indicate = false; // 设置为true时使用指示方式,设置为false时使用通知方式
bluetoothGattServer.notifyCharacteristicChanged(device, characteristic, indicate);

首先获取到要发送数据的特征值,然后通过setWriteType()方法设置写入类型,通常情况下我们使用默认的WRITE_TYPE_DEFAULT即可;接着通过setProperties()方法设置通知或指示属性,这里我们设置了PROPERTY_NOTIFY和PROPERTY_INDICATE两个属性,表示可以使用通知或指示方式发送数据。

最后,我们通过调用bluetoothGattServer.notifyCharacteristicChanged()方法来向客户端发送数据,该方法接收三个参数:设备对象、特征值对象和是否使用指示方式。需要注意的是,如果使用指示方式,客户端必须先确认才能接收到数据,否则会一直处于等待状态。

############################ end ###############################