蓝牙开发之ble

ble的概念以及原理的简单理解

蓝牙是一种短距离无线通信技术,而蓝牙低功耗(BLE)是在蓝牙4.0协议上修改以适用低功耗应用场景的一种蓝牙协议。
那么4.0以后的蓝牙为什么低功耗了呢?传统蓝牙是通过广播收发状态,连接建立后通过socket建立连接,交互数据
ble相对于传统蓝牙

  • 广播频段和广播时射频开启时间的减少:传统蓝牙使用16~32个频段进行广播,而BLE仅使用3个广播频段; 每次广播时的射频
    开启时间由传统蓝牙的22ms减少为0.6~1.2ms;
  • BLE设计了深度睡眠状态(Duty-Cycle)来替代传统蓝牙的空闲时间,并且在Duty-Cycle时,发送数据间隔也被增大.
  • BLE 的连接采用先进的Sniffer-Subrating模式.
  • 传统蓝牙规范规定,若某一设备正在进行广播,则它不会响应当前正在进行的设备扫描,而BLE允许正在进行广播的设备连接正
    在扫描的设备,这有效避免了重复连接。通过对连接机制的改进,BLE连接建立过程可控制在3ms内完成。
  • BLE在每个从设备和每个数据包上使用32位的存取地址,优化了传统蓝牙一对一的连接,实现一对多(目前测试的是1带6)。
  • BLE增加了GFSK调制,降低峰值功耗。
    以上参考了https://blog.csdn.net/ZQ07506149/article/details/82380509

什么是GATT?

通用属性配置文件层(Generic Attribute profile,简写 GATT),简单点就是ble应用运行的环境或者场景,官方的专业解释,个人实在懒的贴出来,而且觉得也不好理解

GATT的层次结构

GATT通常有一个或者多个“Services”组成,一个 Characteristic 可以包含若干 Descriptor和value。而 Characteristic 定义了数值和操作。Characteristic 的操作这几种权限:读、写、通知等权限。

uuid

Service、Characteristic 还有 Descriptor 都是使用 UUID 唯一标示的。一会你可以看到在实际的操作中,都是通过uuid来查找你对应的设备

ble的开发

ble也是C/S的开发模式,特别注意一点ble开发要在主线程(底层源码注释),所以当两个设备建立连接之后,它们就处于下面两种角色之一:

  • GATT服务器:为GATT客户端提供数据服务的设备。外围设备 可以创建uuid的服务
  • GATT客户端:从GATT服务器读写应用数据的设备。中央设备

android的支持版本

官方说从android4.3版本开始,就支持了ble,但是在实际测试可不是这样,国内好多机型开始支持的版本号都不一样啊,这也是一个android碎片化的体现

GATT的连接问题

GATT连接是独占的。外设只能建立一个连接,但是客户端可以连接多台设备

GATT通信

中央设备在给外设发消息的时候,有时候只能发送一次, 因为写特征值前可以设置写的类型setWriteType(),写类型有三种,如下:

  • WRITE_TYPE_DEFAULT 默认类型,需要外围设备的确认,也就是需要外围设备的回应,这样才能继续发送写。
  • WRITE_TYPE_NO_RESPONSE 设置该类型不需要外围设备的回应,可以继续写数据。加快传输速率。
  • WRITE_TYPE_SIGNED 写特征携带认证签名,具体作用不太清楚。

适配ios

在开发ble服务端的时候,遇到过一个很坑的问题,调试了很久,需要在Characteristic特征中加入以下这个BluetoothGattCharacteristic.PROPERTY_NOTIFY,否则ios接收不到,android没事,不知道ios的ble模块是怎样实现的,有知道可以留言一下

Beacon

非连接型的ble的数据收发

外围设备代码开发

初始化

 mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(), createAdvData(), mAdvertiseCallback);

广告模式的回调

  private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            super.onStartSuccess(settingsInEffect);
            Log.d(TAG, "onStartSuccess");
        }

        @Override
        public void onStartFailure(int errorCode) {
            super.onStartFailure(errorCode);
            Log.d(TAG, "onStartFailure " + errorCode);
        }
    };

设置广播数据

private AdvertiseSettings createAdvSettings() {
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
    //手机的发射功率,简单说就是蓝牙走多远
    builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
    builder.setConnectable(true);
    builder.setTimeout(0);
    //ADVERTISE_MODE_LOW_POWER 在低功耗模式下执行蓝牙LE广告。这是默认和首选的广告模式,因为它消耗最少的电力。
    //ADVERTISE_MODE_BALANCED 在平衡电源模式下执行蓝牙LE广告。这是广告频率和功耗之间的平衡。
    //ADVERTISE_MODE_LOW_LATENCY 在低延迟,高功率模式下执行蓝牙LE广告。这是最高的功耗,不应该用于连续的背景广告。
    // TODO: 18-1-7 测试模式的值
    builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
    return builder.build();
}

设置响应数据

private AdvertiseData createAdvData() {
    AdvertiseData.Builder builder = new AdvertiseData.Builder();
    builder.addServiceUuid(ParcelUuid.fromString(UUID_SAMPLE_NAME_SERVICE));
    //这样即使定义service uuid跟别人的有冲突,也可以在中心过滤该magic number来找到符合自己需求的外围设备,,但目前x是以设备名+系列号
    byte mLeManufacturerData[] = {(byte) 0x4C, (byte) 0x00, (byte) 0x02, (byte) 0x15, (byte) 0x15, (byte) 0x15, (byte) 0x15};
    builder.addManufacturerData(0x3103 + 1, mLeManufacturerData);
    builder.setIncludeTxPowerLevel(false);
    builder.setIncludeDeviceName(true);
    return builder.build();
}

初始化GATT服务

//初始化GATT服务
BluetoothGattService nameService = new BluetoothGattService(UUID.fromString(UUID_SAMPLE_NAME_SERVICE),
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//增加读写特征
mBleGattCharacteristicWrite = new BluetoothGattCharacteristic(
UUID.fromString(UUID_SAMPLE_NAME_CHARACTERISTIC),
BluetoothGattCharacteristic.PROPERTY_READ
| BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ
| BluetoothGattCharacteristic.PERMISSION_WRITE);
nameService.addCharacteristic(mBleGattCharacteristicWrite);
mGattServer.addService(nameService);

GATT服务的回调

mGattServer = mBluetoothManager.openGattServer(mContext, new BluetoothGattServerCallback() {
            @Override
            public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
                super.onConnectionStateChange(device, status, newState);
                {
                    switch (newState) {
                        case BluetoothProfile.STATE_DISCONNECTED:
                            Log.d(TAG, device.getName() + " 断开");
                            sendHandlerMsg(device.getName() + " 断开");
                            break;
                        case BluetoothProfile.STATE_CONNECTED:
                            Log.d(TAG, device.getName() + " 连接");
                            sendHandlerMsg(device.getName() + " 连接");
                            break;
                        default:
                            break;
                    }
                }
            }

            @Override
            public void onServiceAdded(int status, BluetoothGattService service) {
                super.onServiceAdded(status, service);
                Log.d(TAG, "onServiceAdded");
            }

            /**A remote client has requested to read a local characteristic.*/
            @Override
            public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
                Log.d(TAG, "read");
            }

            /**
             * 接收消息的方法 - 接收具体的字节
             */
            @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);
                characteristic.setValue(mReceiveSuccess.getBytes());
                //触发发消息给客户端
                mGattServer.notifyCharacteristicChanged(device,characteristic,false);
                String msg = new String(value);
                Log.d(TAG, "write " + msg);
                sendHandlerMsg(msg);
                //.处理响应内容
                //onResponseToClient(requestBytes, device, requestId, characteristic);
            }

            /**
             *  特征被读取。当回复响应成功后,客户端会读取然后触发本方法
             *
             */
            @Override
            public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
                super.onDescriptorReadRequest(device, requestId, offset, descriptor);
                Log.d(TAG, "DescriptorRead");
                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
            }

            /**
             * 2.描述被写入时
             */
            @Override
            public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
                super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
                Log.d(TAG, "DescriptorWrite");
                mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
            }

            @Override
            public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
                super.onExecuteWrite(device, requestId, execute);
                Log.d(TAG, "onExecuteWrite");
            }

            @Override
            public void onNotificationSent(BluetoothDevice device, int status) {
                super.onNotificationSent(device, status);
                Log.d(TAG, "onNotificationSent");
            }
        });

客户端代码 – 中央设备

初始化-扫描-连接-连接GATT服务-搜索服务-通信

检查蓝牙打开,ble可用,得到BluetoothAdapter对象

扫描

该方法专门针对ble的扫描设备,不是传统扫描,无法扫描非ble外设,api要求21以上

  mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
  mBluetoothLeScanner.startScan(mScanCallback);
  
  private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            if (null != result) {
                BluetoothDevice device = result.getDevice();
            
            }

        }
    };

连接

public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
    return false;
}
//根据地址连接远程设备
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
        return false;
}
//连接已开启的GATT服务
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
mBluetoothDeviceAddress = address;

GATT服务的回调,成功连接后开始搜索服务 mBluetoothGatt.discoverServices();

private  BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        /**
         * 连接状态的改变
         */
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            //成功连接后开始搜索服务
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                discoverServices();
                setConnectionStatus(STATE_CONNECTED);
                if (null != mIBleGattCallback) {
                    mIBleGattCallback.onConnectionStateChange(gatt, mConnectionState);
                }
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                setConnectionStatus(STATE_DISCONNECTED);
                if (null != mIBleGattCallback) {
                    mIBleGattCallback.onConnectionStateChange(gatt, mConnectionState);
                }
            }
        }

       /**
         * 发现服务
         */
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG,"onServicesDiscovered");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (null != mIBleGattCallback) {
                    mIBleGattCallback.onServicesDiscovered(gatt);
                }
            } else {
                excuteError(status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (null != mIBleGattCallback) {
                    mIBleGattCallback.onCharacteristicRead(gatt, characteristic);
                }
            } else {
                excuteError(status);
            }
        }

        
         /**
         * 接收外设的消息
         * @param gatt           BluetoothProfile的对象
         * @param characteristic 特征
         */
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            if (null != mIBleGattCallback) {
                mIBleGattCallback.onCharacteristicChanged(gatt, characteristic);
                onReceiveMsg(characteristic);
            }
        }
    };

发数据

获取可用的特征
可以通过协定好的uuid获取,也可以遍历获取可写的服务

//判断特征可写
public boolean ifCharacteristicWritable(BluetoothGattCharacteristic characteristic){
    return ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 ||
            (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0);
}
//直接获取相应uuid的服务
UUID uuid = UUID.fromString(BleStatusConstant.UUID_SAMPLE_NAME_SERVICE);
BluetoothGattService service = gatt.getService(uuid);
UUID uuid2 = UUID.fromString(BleStatusConstant.UUID_SAMPLE_NAME_CHARACTERISTIC);
mBleCharacteristic = service.getCharacteristic(uuid2);

写入属性

/**
 * 写入属性
 *
 * @param characteristic 特征
 * @return true写入成功 false写入失败
 */
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        return false;
    }
    return mBluetoothGatt.writeCharacteristic(characteristic);
}

结束别忘了释放,如果有扫描也需要停止扫描

if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    原文作者:android老男孩
    原文地址: https://www.jianshu.com/p/17051916d02f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞