Ble低功耗蓝牙开发基础与实践

一、简介
BLE是指低功耗蓝牙(Bluetooth Low Energy),也被称为蓝牙4.0。它是蓝牙技术的一种变体,旨在消耗更少的电量,具有便携性,并且可以在无线传输距离较短的范围内实现低速数据传输。BLE技术已经广泛应用于智能家居、健康监测、物联网等场景中。

二、优点

  • 低功耗:BLE的主要特点之一是低功耗通信。它采用了一系列的技术和优化,使得设备能够以较低的能量消耗进行通信,从而延长电池寿命。这使得BLE非常适用于那些需要长时间运行且依赖电池供电的设备。
  • 快速连接和断开:BLE提供了快速建立连接和断开连接的机制,使设备能够在需要时快速建立通信连接,然后立即断开连接以降低损耗。
  • 短距离通信:BLE的传输距离通常较短,适用于近距离通信场景。这有助于减少干扰和功耗,并提供更好的隐私和安全性。
  • 简化的协议栈:BLE采用了简化的协议栈,包括L2CAP(逻辑链路控制与适配协议)、ATT(属性协议)和GATT(通用属性配置文件)。这简化了BLE设备的开发和部署,减少了开发成本和复杂性。
  • 广播和扫描:BLE支持广播和扫描机制。设备可以通过广播发送数据,其他设备可以通过扫描来发现附近的BLE设备。这种机制使得设备可以在没有建立连接的情况下进行通信和数据交换。
  • 多种应用场景:BLE被广泛应用于各种领域,包括物联网(IoT)、健康监测、智能家居、运动跟踪、位置服务等。它为这些应用提供了低功耗、快速连接和简单的数据交换。

三、相关概念
1.广播(Advertising)
BLE广播(Advertising)是一种用于设备之间的发现和连接的机制。通过广播,BLE设备可以以一种无连接的方式向周围的设备发送信息,以便其他设备能够发现它们并建立连接。

BLE广播的主要特点和原则如下:

  • 无连接广播:BLE广播是一种无连接的通信方式,不需要建立实际的BLE连接。设备可以以广播包(Advertising
    Packet)的形式发送信息,而无需事先知道周围设备的存在。
  • 广播包(Advertising
    Packet):广播包是一种特殊的数据包,用于在无连接的情况下传输数据。它包含了设备的标识符、服务信息、特征值等数据,以便其他设备能够识别和发现它。
  • 广播频率和间隔:BLE设备以一定的广播频率和间隔发送广播包。广播频率决定了广播包发送的速度,而广播间隔则决定了广播包之间的时间间隔。
  • 广播模式:BLE广播支持不同的广播模式,包括可连接模式(Connectable)、非可连接模式(Non-connectable)和可被扫描模式(Scannable)。可连接模式允许其他设备通过建立连接与广播设备进行通信,非可连接模式只用于广播信息,不接受连接请求,可被扫描模式允许其他设备扫描到广播设备。
  • 广播数据的内容:广播数据可以包含多种信息,例如设备的名称、服务UUID、厂商自定义数据等。这些信息帮助其他设备识别和区分不同的BLE设备。
  • 低功耗设计:BLE广播采用了一系列的优化措施,以减少功耗并延长设备的电池寿命。例如,设备可以选择适当的广播频率和间隔,以平衡广播的效果和功耗消耗。

通过BLE广播,设备可以宣布自身的存在、提供基本的信息和服务,以便其他设备发现和与之进行进一步的通信。广播是BLE通信的第一步,在建立连接之前,它允许设备之间进行初步的交互和识别。

在Android中,可以使用BluetoothLeScanner类来扫描和识别BLE设备。通过BluetoothGatt类进行BLE设备连接。

2.Service(服务)

服务(Service)是一种逻辑单元,用于提供BLE设备的功能和操作。每个BLE设备可以包含一个或多个服务,而每个服务又可以包含一个或多个特征值(Characteristic)。

BLE服务由以下属性组成:

  • UUID(通用唯一识别码):每个服务都有一个UUID来唯一标识。UUID可以是标准的16位或128位UUID,也可以是自定义的UUID。标准UUID通常用于常见的BLE功能,如心率监测、温度传感器等。自定义UUID用于特定的应用程序和设备。
  • 特征值(Characteristics):服务包含一个或多个特征值。特征值是BLE设备上存储数据的基本单元,用于提供具体的功能和操作。特征值的UUID和属性描述了它们的行为和功能。
  • 描述符(Descriptors):服务和特征值可以包含一个或多个描述符。描述符提供关于服务和特征值的元数据信息,如名称、单位、权限等。

BLE服务在设备的GATT(通用属性配置文件)中进行定义和管理。GATT是BLE通信的一种协议框架,用于描述设备的服务、特征值和描述符。它提供了一种标准的方式来组织和交换BLE设备的数据。

在Android中,使用BluetoothGattService类来表示和操作BLE服务。通过BluetoothGattService对象,可以获取服务的UUID、包含的特征值列表以及与特征值相关联的描述符。

以下是一些常用的服务操作方法:

  • getUuid():获取服务的UUID。
  • getCharacteristics():获取与服务相关联的特征值列表。
  • getIncludedServices():获取包含在该服务中的其他服务列表。
  • getCharacteristic(UUID uuid):根据UUID获取特定的特征值对象。
  • addCharacteristic(BluetoothGattCharacteristic
    characteristic):向服务中添加特征值。

BLE服务提供了一种组织和管理BLE设备功能的方式。通过定义不同的服务和特征值,设备可以提供不同的功能和操作,如传感器数据、控制命令等。在进行BLE开发时,需要根据具体设备和应用需求设计和实现相应的服务结构。

3.特征值(Characteristic)

特征值(Characteristic)是BLE设备上存储数据的基本单元。每个特征值都具有唯一的UUID(通用唯一识别码),用于标识特征值的类型和功能。

BLE特征值由以下属性组成

  1. UUID(通用唯一识别码):每个特征值都有一个UUID来唯一标识。UUID可以是标准的16位或128位UUID,也可以是自定义的UUID。标准UUID通常用于常见的BLE功能,如心率监测、温度传感器等。自定义UUID用于特定的应用程序和设备。

  2. 属性(Properties):特征值具有一组属性,用于描述其行为和功能。属性可以是可读、可写、通知、指示等。常见的属性包括:
    (1)可读(Read):特征值可以被读取,允许远程设备读取特征值的值。
    (2)可写(Write):特征值可以被写入,允许远程设备写入新的值到特征值。
    (3)通知(Notify):特征值可以发送通知,当特征值的值发生变化时,会向连接的设备发送通知。
    (4)指示(Indicate):类似于通知,但通知具有无法保证可靠传递的特性,而指示可以确保通知被接收。

  3. 值(Value):特征值存储的数据值。值可以是整数、浮点数、字节数组等,具体取决于特征值的用途和数据类型。

  4. 描述符(Descriptor):特征值可以包含一个或多个描述符,用于提供关于特征值的附加信息。描述符是可选的,并提供关于特征值的元数据,如单位、权限等。

在Android中,使用BluetoothGattCharacteristic类来表示和操作BLE特征值。通过BluetoothGattCharacteristic对象,可以读取特征值的值、写入新的值、启用通知和指示等操作。

以下是一些常用的特征值操作方法:

  • getValue():获取特征值的当前值。
  • setValue(byte[] value):设置特征值的值。
  • getProperties():获取特征值的属性。
  • getDescriptors():获取与特征值相关联的描述符列表。
  • getUuid():获取特征值的UUID。
  • getPermissions():获取特征值的权限。

4.描述符(Descriptor)
描述符(Descriptor)是用于提供有关BLE服务(Service)和特征值(Characteristic)的元数据信息的数据结构。描述符包含与服务和特征值相关的配置参数、权限和其他属性。

以下是关于BLE描述符的一些重要特点和功能:

  • UUID(通用唯一识别码):每个描述符都有一个UUID来唯一标识。UUID可以是标准的16位或128位UUID,也可以是自定义的UUID。标准UUID通常用于常见的BLE功能,如客户端配置描述符(Client
    Characteristic Configuration Descriptor)和报告参考描述符(Report Reference
    Descriptor)。自定义UUID用于特定的应用程序和设备。
  • 属性和权限:描述符具有一组属性和权限,用于描述和控制对服务和特征值的访问。这些属性和权限可以指示描述符的读写权限、通知和指示的配置、数据格式等。
  • 与特征值关联:描述符通常与特征值相关联,用于提供特征值的额外信息和配置参数。例如,客户端配置描述符用于启用或禁用特征值的通知或指示功能。
  • 用途和功能:BLE描述符的功能和用途因设备和应用而异。常见的描述符包括客户端配置描述符、报告参考描述符、用户描述符、环境感知描述符等。这些描述符可以提供与特征值相关的附加信息,如单位、范围、权限等。

在Android中,通过BluetoothGattDescriptor类来表示和操作BLE描述符。通过BluetoothGattDescriptor对象,可以获取描述符的UUID、读取和写入描述符的值,并将描述符与特征值关联。

以下是一些常用的描述符操作方法:

  • getUuid():获取描述符的UUID。
  • getValue():读取描述符的值。
  • setValue(byte[] value):设置描述符的值。
  • getPermissions():获取描述符的权限。
  • getCharacteristic():获取与描述符关联的特征值对象。

四、设备类型

BLE设备可以分为两种类型:中央设备(Central)和外围设备(Peripheral)。

  1. 中央设备(Central):可看作是客户端,中央设备通常是具备计算能力和连接性的设备,例如智能手机、平板电脑或计算机。中央设备负责发起BLE连接请求、扫描并发现附近的外围设备,以及与外围设备进行数据交换。
  2. 外围设备(Peripheral):可看作服务端,外围设备通常是低功耗的传感器、运动设备、健康监测器等设备。外围设备提供一些特定的功能或传感器数据,并等待来自中央设备的连接请求和命令。外围设备在收到中央设备的请求后,响应并提供数据或执行特定的操作。

中央设备和外围设备之间的BLE通信是基于GATT(通用属性配置文件)协议进行的。中央设备通过扫描和发现附近的外围设备,并根据外围设备的GATT配置进行连接和数据交换。中央设备可以读取和写入外围设备的特征值(Characteristic),以实现数据的传输和控制。

BLE设备的角色(中央设备或外围设备)通常取决于设备的功能和用途。例如,智能手机通常充当中央设备的角色,与多个外围设备建立连接并收集传感器数据。而传感器设备、健康监测器等通常充当外围设备的角色,等待来自中央设备的连接请求并提供数据。

需要注意的是,同一个设备在不同的场景下可能同时扮演中央设备和外围设备的角色,例如智能手机可以作为中央设备连接传感器设备,但也可以作为外围设备提供服务给其他设备连接。

五、安全问题
在BLE通信中,存在一些安全问题,特别是在数据传输和连接建立过程中。以下是一些常见的BLE安全问题:

  • 连接过程的安全性:在建立BLE连接时,存在连接请求和连接响应的过程。如果未采取适当的安全措施,攻击者可能会进行中间人攻击或窃听连接请求和响应,从而获得敏感信息或干扰连接的建立。
  • 数据传输的安全性:BLE通信中的数据传输可能会受到窃听、篡改和重放攻击的威胁。未经加密的数据传输可能会导致数据泄露和信息安全问题。
  • 认证和授权:BLE连接的一方需要确保对方设备的身份和权限。如果未进行适当的认证和授权过程,可能会导致恶意设备接入或未经授权的数据访问。
  • 蓝牙配对的安全性:在某些情况下,BLE设备可能需要进行蓝牙配对过程以建立安全连接。如果配对过程中的认证和密钥交换不安全,可能会导致配对数据被攻击者获取,从而破坏连接的安全性。

为了应对这些安全问题,可以采取以下措施来增强BLE通信的安全性:

  • 加密数据传输:使用安全的加密算法对敏感数据进行加密,确保数据在传输过程中的机密性和完整性。使用对称加密或公钥加密机制来保护数据的安全性。
  • 蓝牙配对安全:在蓝牙配对过程中,采用强密码和长密钥进行配对,确保认证和密钥交换的安全性。避免使用弱密码和易受攻击的配对方法。
  • 设备身份验证:在BLE连接建立过程中,进行设备身份验证,确保连接的安全性和合法性。使用数字证书或安全令牌等机制来验证设备的身份。
  • 安全的连接参数:使用适当的BLE连接参数设置,如连接间隔、连接超时等,以提高连接的安全性和可靠性。
  • 安全的广播和扫描策略:限制BLE设备的广播和扫描范围,防止未授权的设备访问。使用安全的广播模式和广播数据策略,避免泄露敏感信息。
  • 安全的固件更新:定期更新设备的固件和软件,以修复已知的安全漏洞和问题。确保使用最新版本的固件和软件,以减少潜在的攻击面。

通过综合采取这些安全措施,可以增强BLE通信的安全性,减少潜在的攻击和数据泄露风险。

六具体实践
1.声明权限
在AndroidManifest.xml文件中添加BLE相关的权限声明。

<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
 
<!-- 安卓12开始需要下列权限 compileSDK 32+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
 
<!--安卓6.0以及以上版本需要添加定位的权限 (需要在代码中动态申请)-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />
    
<!--如果你的app只为具有BLE的设备提供,请声明-->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

2.判断设备是否支持BLE以及蓝牙是否打开

/**
  *  判断设备是否支持BLE
  */
fun checkSupportBLE(context: Context):Boolean{
    val packageManager: PackageManager = context.packageManager
    return packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}
 
/**
  * 判断蓝牙是否打开
  */
fun isBluetoothEnabled(context: Context):Boolean{
    val bluetoothManager = BluetoothAdapter.getDefaultAdapter()
    return bluetoothAdapter == null || bluetoothAdapter?.isEnabled == false
}

3.进行扫描

val scanCallback: ScanCallback = object : ScanCallback() {
    override fun onScanResult(callbackType: Int, result: ScanResult?) {
         val device= result?.getDevice()
          // 处理扫描到的设备
    }
}
 
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
bluetoothAdapter.bluetoothLeScanner.startScan(scanCallback)

4.建立连接并监听
在BluetoothGattCallback进行监听相关回调

val gattCallback = object : BluetoothGattCallback() {
 
    override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // 连接成功,进行服务发现
            gatt?.discoverServices()
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // 连接断开,处理断开逻辑
        }
    }
 
    override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // 服务发现成功,处理服务和特征值
            val services = gatt?.services
            services?.let {
                for (service in it) {
                    // 处理服务和特征值
                }
            }
        } else {
            // 服务发现失败
        }
    }
 
    override fun onCharacteristicRead(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray,
        status: Int
    ) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "读取特征值")
            // 从特征值读取数据
            // characteristic 是特征值,而特征值是用 16bit 或者 128bit,16bit 是官方认证过的,128bit 是可以自定义的
            val sucString = characteristic.value
        }
    }
 
    override fun onCharacteristicWrite(
        gatt: BluetoothGatt?,
        characteristic: BluetoothGattCharacteristic?,
        status: Int
    ) {
        super.onCharacteristicWrite(gatt, characteristic, status)
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.i(TAG, "写入特征值")
        }
    }
 
    override fun onCharacteristicChanged(
        gatt: BluetoothGatt,
        characteristic: BluetoothGattCharacteristic,
        value: ByteArray
    ) {
        Log.i(TAG, "特征值${characteristic.uuid.toString()}变化")
    }
 
    override fun onDescriptorRead(
        gatt: BluetoothGatt,
        descriptor: BluetoothGattDescriptor,
        status: Int,
        value: ByteArray
    ) {
        Log.i(TAG, "描述符${descriptor.uuid.toString()}读取")
        Log.i(TAG, "描述符值:${String(descriptor.value)}")
    }
 
    override fun onDescriptorWrite(
        gatt: BluetoothGatt?,
        descriptor: BluetoothGattDescriptor?,
        status: Int
    ) {
        Log.i(TAG, "描述符${descriptor?.uuid.toString()}写入")
    }
 
    override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) {
        super.onReadRemoteRssi(gatt, rssi, status)
        //rssi值是蓝牙的信号值,离得越远信号越小
        Log.i(TAG, "蓝牙信号值:$rssi")
    }
}
 
val gatt: BluetoothGatt = bluetoothDevice.connectGatt(context, false, gattCallback)

5.读取特征值

 /**
   * 读取特征值,读取成功后将回调在BluetoothGattCallback的onCharacteristicRead方法中
   */
fun readCharacteristic(characteristic: BluetoothGattCharacteristic){
    //设置特征值变化通知,必须设置,否则无法监听特征值变化情况
    bluetoothGatt?.setCharacteristicNotification(characteristic, true)
    //读取特征值
    bluetoothGatt?.readCharacteristic(characteristic)
}

6.写入特征值

/**
  *  写入特征值,完成后将回调在BluetoothGattCallback的onCharacteristicWrite方法中
  */
fun writeCharacteristic(characteristic: BluetoothGattCharacteristic){
    bluetoothGatt?.writeCharacteristic(characteristic)
}

7.断开连接

fun disconnect(){
    bluetoothGatt?.disconnect()
    bluetoothGatt?.close()
}

8.分包
在Android BLE通信中,如果要发送的数据大小超过MTU(最大传输单元)的限制,就需要进行数据分包处理。BLE蓝牙一包数据最多为20字节,因此安卓系统下最好不要使用BLE蓝牙传输大量数据。以下是一种常见的方法来实现BLE数据分包发送:

(1)获取MTU大小:首先,通过调用BluetoothGatt对象的requestMtu()方法来请求MTU大小,例如:

val mtu = 20// 设置期望的MTU大小
bluetoothGatt.requestMtu(mtu)

注意:requestMtu设置不一定会成功,每个版本的蓝牙都有不同的最大值,设置时尽量小一点,一般在onConnectionStateChange中判断连接设备成功后调用这个方法,然后再去搜索Service

(2)监听MTU更新:在BluetoothGattCallback中的onMtuChanged()回调方法中处理MTU更新结果:

override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        // MTU更新成功,可以开始发送数据
        sendData(bluetoothGatt, data, mtu)
    } else {
        // MTU更新失败,处理失败逻辑
    }
}

(3)数据分包发送:根据MTU大小将要发送的数据拆分成多个分包,并通过BluetoothGattCharacteristic的setValue()和writeCharacteristic()方法进行发送。以下是一个简单的示例:

/**
  * 往指定特征值写数据,分包
  */
fun sendData(characteristic: BluetoothGattCharacteristic, data: ByteArray, mtu: Int) {
    Thread {
        val packetSize = mtu - 3 // 减去3个字节的包头
        // 将数据拆分为分包并发送
        var offset = 0
        while (offset < data.size) {
        val packet = data.sliceArray(offset until minOf(offset + packetSize, data.size))
            characteristic?.value = packet
            bleClient?.bluetoothGatt?.writeCharacteristic(characteristic)
            offset += packet.size
        }
        Log.d(BleClient.TAG, "发送完毕..")
    }.start()
}

在这个示例中,我们使用MTU大小减去3个字节(包头),得到每个分包的大小。然后,将要发送的数据按照分包大小拆分成多个分包,并通过setValue()方法设置分包数据,再通过writeCharacteristic()方法发送分包。

需要注意的是,每个分包的大小应该小于或等于MTU减去3个字节。另外,数据的接收端也需要对分包进行合并和处理,以确保正确接收和还原原始数据。

参考文章
Android低功耗蓝牙开发(一)
Android低功耗蓝牙开发(二)

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值