蓝牙BLE通信开发完全指南

目录

  1. BLE基础概念
  2. GATT协议深度解析
  3. BLE连接流程详解
  4. Android BLE开发实战
  5. 行业应用案例分析
  6. 性能优化实战
  7. 故障排查方案
  8. BLE技术对比
  9. 总结

一句话总结

BLE(低功耗蓝牙)是专为物联网设备设计的短距离无线通信技术,通过GATT协议实现低功耗、低成本的数据传输,广泛应用于智能穿戴、医疗健康、智能门锁等领域。


核心架构图

BLE通信架构全景
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

                 智能手机/平板(中心设备 Central)
                 ┌─────────────────────────────┐
                 │   BLE应用层 (App)           │
                 ├─────────────────────────────┤
                 │   GATT Client              │
                 │   - 读取特征值              │
                 │   - 写入特征值              │
                 │   - 订阅通知                │
                 ├─────────────────────────────┤
                 │   GAP (连接管理)            │
                 │   - 扫描设备                │
                 │   - 建立连接                │
                 │   - 断开连接                │
                 ├─────────────────────────────┤
                 │   Link Layer (链路层)       │
                 ├─────────────────────────────┤
                 │   Physical Layer (物理层)   │
                 └─────────────────────────────┘
                            ↕ 无线通信
                 ┌─────────────────────────────┐
                 │   Physical Layer (物理层)   │
                 ├─────────────────────────────┤
                 │   Link Layer (链路层)       │
                 ├─────────────────────────────┤
                 │   GAP (广播管理)            │
                 │   - 发送广播                │
                 │   - 响应连接请求            │
                 ├─────────────────────────────┤
                 │   GATT Server              │
                 │   ┌───────────────────┐    │
                 │   │ Service 1         │    │
                 │   │ ├─ Characteristic │    │
                 │   │ └─ Characteristic │    │
                 │   ├───────────────────┤    │
                 │   │ Service 2         │    │
                 │   │ ├─ Characteristic │    │
                 │   │ └─ Characteristic │    │
                 │   └───────────────────┘    │
                 ├─────────────────────────────┤
                 │   应用逻辑                  │
                 └─────────────────────────────┘
              智能手环/门锁/传感器(外围设备 Peripheral)

关键参数:
- 工作频段:2.4GHz ISM频段(2400-2483.5MHz)
- 传输距离:10-100米(取决于功率等级)
- 传输速率:1Mbps(BLE 4.x),2Mbps(BLE 5.0)
- 功耗:< 15mA(活跃),< 1μA(休眠)
- 连接延迟:6ms起(连接间隔可配置)

一、BLE基础概念

1.1 什么是BLE?

BLE(Bluetooth Low Energy,低功耗蓝牙)是蓝牙4.0规范的一部分,专为物联网设备设计的短距离无线通信技术。与传统蓝牙(Classic Bluetooth)不同,BLE专注于低功耗、低成本、低复杂度的应用场景。

核心特性

  • 超低功耗:峰值功耗<15mA,休眠功耗<1μA,纽扣电池可运行数月至数年
  • 快速连接:连接建立时间<6ms,远快于经典蓝牙的数秒
  • 灵活通信:支持广播、连接两种模式,适应不同应用场景
  • 广泛兼容:iOS、Android、Windows、Linux全平台支持

1.2 BLE vs 经典蓝牙

对比维度 BLE(低功耗蓝牙) 经典蓝牙 适用场景
功耗 < 15mA活跃,< 1μA休眠 30-100mA BLE:传感器、穿戴
经典:音频、大文件
传输速率 1Mbps(BLE 4.x)
2Mbps(BLE 5.0)
2-3Mbps BLE:遥测数据
经典:音频流
连接延迟 6ms起 100ms+ BLE:实时交互
经典:稳定传输
传输距离 10-100m 10-100m 相同
数据包大小 20-512字节 无限制 BLE:小数据
经典:大数据
应用场景 传感器、穿戴、门锁 音频、车载、文件传输 -

重要提示:BLE 4.x和5.0不兼容经典蓝牙协议栈,它们是完全独立的技术。

1.3 BLE的演进历史

BLE技术演进时间线
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

2010年 - Bluetooth 4.0
  └─ 引入BLE技术,传输速率1Mbps
  └─ iOS 5/Android 4.3开始支持

2013年 - Bluetooth 4.1
  └─ 改进连接管理,支持设备同时作为中心和外围
  └─ IPv6支持,为IoT铺路

2014年 - Bluetooth 4.2
  └─ 数据包长度扩展(20→255字节)
  └─ 隐私保护增强(地址随机化)

2016年 - Bluetooth 5.0 ⭐重大升级
  └─ 传输速率翻倍(2Mbps)
  └─ 传输距离4倍提升(400m室外)
  └─ 广播容量8倍提升(255字节)

2019年 - Bluetooth 5.1
  └─ 方向查找功能(AoA/AoD)
  └─ 室内定位精度<1m

2020年 - Bluetooth 5.2
  └─ LE Audio音频标准
  └─ 等时通道(Isochronous Channels)

2021年 - Bluetooth 5.3
  └─ 连接更新增强
  └─ 广播加密

1.4 BLE应用场景

典型应用领域

  1. 智能穿戴设备

    • 智能手环/手表:心率监测、步数统计、消息提醒
    • 运动传感器:骑行功率计、跑步足传感器
    • 耳机:TWS真无线耳机(BLE音频)
  2. 医疗健康设备

    • 血压计、血糖仪:数据同步到手机App
    • 体温计、体脂秤:健康数据管理
    • 助听器:BLE音频传输
  3. 智能家居设备

    • 智能门锁:蓝牙钥匙、开门记录
    • 温湿度传感器:环境监测
    • 智能灯泡:调光调色控制
  4. 资产追踪与定位

    • AirTag/防丢器:蓝牙信标定位
    • 仓储管理:物品位置追踪
    • 人员定位:室内导航
  5. 零售与营销

    • iBeacon:店内导航、优惠推送
    • 电子价签:动态价格更新
    • 无人售货机:支付与交互

二、GATT协议深度解析

2.1 GATT协议概述

GATT(Generic Attribute Profile,通用属性协议)是BLE通信的核心协议,定义了数据的组织方式和访问方法。GATT基于ATT(Attribute Protocol)实现,提供了层次化的数据结构。

GATT层次结构

GATT数据结构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Profile(配置文件)
  └─ Service(服务) [UUID: 0x180D - 心率服务]
      ├─ Characteristic(特征值) [UUID: 0x2A37 - 心率测量]
      │   ├─ Value(值):实际数据(如心率BPM)
      │   ├─ Properties(属性):Read/Write/Notify/Indicate
      │   └─ Descriptor(描述符) [UUID: 0x2902 - CCCD客户端特征配置]
      │       └─ Value:启用/禁用通知
      │
      └─ Characteristic [UUID: 0x2A38 - 身体传感器位置]
          ├─ Value:手腕/胸部/手指等
          └─ Properties:Read

实例:心率传感器GATT结构
┌──────────────────────────────────────────┐
│ Heart Rate Service (0x180D)              │
│ ├─ Heart Rate Measurement (0x2A37)       │
│ │   - Properties: Notify                 │
│ │   - Value: [心率值: 78 BPM]            │
│ │   - Descriptor: CCCD (0x2902)          │
│ │       └─ Value: 0x0001 (通知已启用)    │
│ │                                         │
│ └─ Body Sensor Location (0x2A38)         │
│     - Properties: Read                   │
│     - Value: 0x01 (胸部)                 │
│                                           │
│ Battery Service (0x180F)                 │
│ └─ Battery Level (0x2A19)                │
│     - Properties: Read, Notify           │
│     - Value: 85%                         │
└──────────────────────────────────────────┘

2.2 Service(服务)

Service是一组相关特征值的集合,每个Service由唯一的UUID标识。

Service类型

  1. 标准Service(SIG定义)

    • 心率服务(Heart Rate Service, 0x180D)
    • 电池服务(Battery Service, 0x180F)
    • 设备信息服务(Device Information Service, 0x180A)
    • 完整列表:Bluetooth SIG Services
  2. 自定义Service

    • 使用128位UUID(如:0000FFF0-0000-1000-8000-00805F9B34FB
    • 适用于专有功能

Service声明示例(Android BLE Server):

val heartRateService = BluetoothGattService(
    UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"), // 心率服务UUID
    BluetoothGattService.SERVICE_TYPE_PRIMARY
)

2.3 Characteristic(特征值)

Characteristic是GATT中最小的数据单元,包含一个值和若干属性。

Characteristic属性(Properties)

属性 说明 典型应用
Read 客户端可读取值 电池电量、传感器状态
Write 客户端可写入值 LED开关、设备配置
Write Without Response 写入无需响应(快速) 游戏手柄按键
Notify 服务端主动推送(无需确认) 心率数据、温度监测
Indicate 服务端主动推送(需确认) 关键告警信息
Broadcast 广播模式发送 信标定位

Characteristic声明示例

val heartRateMeasurement = BluetoothGattCharacteristic(
    UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB"), // 心率测量UUID
    BluetoothGattCharacteristic.PROPERTY_NOTIFY, // 支持通知
    BluetoothGattCharacteristic.PERMISSION_READ // 允许读取
)

// 添加CCCD描述符(用于启用通知)
val cccdDescriptor = BluetoothGattDescriptor(
    UUID.fromString("00002902-0000-1000-8000-00805F9B34FB"), // CCCD UUID
    BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
)
heartRateMeasurement.addDescriptor(cccdDescriptor)

heartRateService.addCharacteristic(heartRateMeasurement)

2.4 Descriptor(描述符)

Descriptor是描述Characteristic的元数据,最常用的是CCCD(Client Characteristic Configuration Descriptor)。

常用Descriptor类型

UUID 名称 作用
0x2902 CCCD 启用/禁用Notify/Indicate
0x2900 Characteristic Extended Properties 扩展属性
0x2901 Characteristic User Description 用户描述
0x2904 Characteristic Presentation Format 数据格式(单位、精度)

CCCD值说明

CCCD配置值
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0x0000 → 禁用通知和指示
0x0001 → 启用通知(Notification)
0x0002 → 启用指示(Indication)
0x0003 → 同时启用通知和指示

三、BLE连接流程详解

3.1 完整连接流程

BLE连接流程(从扫描到数据传输)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

阶段1:设备发现(Device Discovery)
┌────────────────────────────────────────────────┐
│ Peripheral(外围设备)                          │
│   └─ 发送广播包(Advertising Packet)          │
│       - 设备名称                                │
│       - Service UUID列表                       │
│       - Tx功率等级                             │
│       ↓ 每20ms-10s发送一次(可配置)            │
│                                                 │
│ Central(中心设备)                             │
│   └─ 扫描广播包(Scanning)                    │
│       - 被动扫描:仅监听                        │
│       - 主动扫描:发送Scan Request获取更多信息   │
└────────────────────────────────────────────────┘
                      ↓
阶段2:建立连接(Connection Establishment)
┌────────────────────────────────────────────────┐
│ Central 发送连接请求(Connect Request)         │
│   ↓                                             │
│ Peripheral 停止广播,接受连接                   │
│   ↓                                             │
│ 协商连接参数:                                  │
│   - 连接间隔(Connection Interval):7.5ms-4s  │
│   - 从机延迟(Slave Latency):0-499个间隔     │
│   - 超时时间(Supervision Timeout):100ms-32s │
└────────────────────────────────────────────────┘
                      ↓
阶段3:服务发现(Service Discovery)
┌────────────────────────────────────────────────┐
│ Central 请求发现服务(Discover Services)       │
│   ↓                                             │
│ Peripheral 返回所有Service列表                  │
│   ↓                                             │
│ Central 请求发现特征值(Discover Characteristics)│
│   ↓                                             │
│ Peripheral 返回Characteristic列表及属性         │
│   ↓                                             │
│ Central 读取Descriptor(可选)                  │
└────────────────────────────────────────────────┘
                      ↓
阶段4:数据传输(Data Transfer)
┌────────────────────────────────────────────────┐
│ ◆ 读取(Read)                                  │
│   Central → Peripheral: Read Request           │
│   Peripheral → Central: Read Response(数据)   │
│                                                 │
│ ◆ 写入(Write)                                 │
│   Central → Peripheral: Write Request + 数据    │
│   Peripheral → Central: Write Response(确认)  │
│                                                 │
│ ◆ 通知(Notify)                                │
│   1. Central 写入CCCD启用通知(0x0001)         │
│   2. Peripheral 定期发送Notification(无需确认) │
│                                                 │
│ ◆ 指示(Indicate)                              │
│   1. Central 写入CCCD启用指示(0x0002)         │
│   2. Peripheral 发送Indication                 │
│   3. Central 回复Confirmation(确认)           │
└────────────────────────────────────────────────┘
                      ↓
阶段5:断开连接(Disconnection)
┌────────────────────────────────────────────────┐
│ Central 或 Peripheral 发送断开请求              │
│   ↓                                             │
│ 连接终止,Peripheral 恢复广播状态               │
└────────────────────────────────────────────────┘

3.2 连接参数详解

连接间隔(Connection Interval)

  • 定义:Central多久轮询一次Peripheral
  • 范围:7.5ms - 4s
  • 影响
    • 小间隔(如7.5ms):低延迟,高功耗
    • 大间隔(如1s):高延迟,低功耗
  • 典型值
    • 实时控制(游戏手柄):20-40ms
    • 数据同步(健康设备):100-200ms
    • 低功耗监控(传感器):1-2s

从机延迟(Slave Latency)

  • 定义:Peripheral可跳过的连接事件数(不响应轮询)
  • 范围:0-499
  • 作用:在无数据传输时节省功耗
  • 计算:实际唤醒间隔 = 连接间隔 × (1 + 从机延迟)

示例

连接间隔:100ms
从机延迟:4
→ Peripheral每500ms唤醒一次(100ms × 5)
→ 功耗降低80%

超时时间(Supervision Timeout)

  • 定义:多久未收到对方数据包则判定连接断开
  • 范围:100ms - 32s
  • 约束:必须 > (1 + Slave Latency) × Connection Interval × 2

3.3 广播包结构

广播包(Advertising Packet)格式

BLE广播包结构(最大31字节)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌─────────────────────────────────────────────────┐
│ Preamble (1 byte)       │ 前导码                │
├─────────────────────────┼───────────────────────┤
│ Access Address (4 bytes)│ 访问地址              │
├─────────────────────────┼───────────────────────┤
│ PDU Header (2 bytes)    │ PDU头部               │
├─────────────────────────┼───────────────────────┤
│ MAC Address (6 bytes)   │ 设备MAC地址           │
├─────────────────────────┼───────────────────────┤
│ Advertising Data (0-31) │ 广播数据              │
│   ┌──────────────────────────────────────┐      │
│   │ AD Structure 1                       │      │
│   │ ├─ Length (1 byte)                   │      │
│   │ ├─ Type (1 byte) → 0x09 (完整设备名) │      │
│   │ └─ Data (N bytes) → "MyDevice"       │      │
│   ├──────────────────────────────────────┤      │
│   │ AD Structure 2                       │      │
│   │ ├─ Length (1 byte)                   │      │
│   │ ├─ Type (1 byte) → 0x07 (128位UUID)  │      │
│   │ └─ Data (16 bytes) → Service UUID    │      │
│   └──────────────────────────────────────┘      │
├─────────────────────────┼───────────────────────┤
│ CRC (3 bytes)           │ 校验码                │
└─────────────────────────┴───────────────────────┘

常用AD Type:
0x01 - Flags(设备角色标志)
0x02 - 部分16位Service UUID列表
0x03 - 完整16位Service UUID列表
0x06 - 部分128位Service UUID列表
0x07 - 完整128位Service UUID列表
0x08 - 缩短设备名
0x09 - 完整设备名
0x0A - Tx功率等级
0xFF - 厂商自定义数据

广播包示例(智能手环):

// 原始广播包(十六进制)
val advPacket = byteArrayOf(
    0x02, 0x01, 0x06,          // Flags: 一般可发现模式,仅支持BLE
    0x03, 0x03, 0x0D, 0x18,    // 16位Service UUID: 0x180D(心率服务)
    0x0A, 0x09, 0x4D, 0x79,    // 完整设备名: "MyBand2024"
    0x42, 0x61, 0x6E, 0x64,
    0x32, 0x30, 0x32, 0x34
)

四、Android BLE开发实战

4.1 权限申请

所需权限(AndroidManifest.xml):

<!-- 蓝牙基础权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- Android 12+ 新权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <!-- 仅广播设备需要 -->

<!-- 位置权限(扫描BLE设备需要) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- 声明BLE特性 -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

运行时权限申请

class BleActivity : AppCompatActivity() {
    private val requiredPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        arrayOf(
            Manifest.permission.BLUETOOTH_SCAN,
            Manifest.permission.BLUETOOTH_CONNECT,
            Manifest.permission.ACCESS_FINE_LOCATION
        )
    } else {
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.BLUETOOTH,
            Manifest.permission.BLUETOOTH_ADMIN
        )
    }

    private val permissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        if (permissions.all { it.value }) {
            startBleScan()
        } else {
            Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        permissionLauncher.launch(requiredPermissions)
    }
}

4.2 扫描BLE设备

扫描实现(完整代码):

class BleScanner(private val context: Context) {
    private val bluetoothAdapter: BluetoothAdapter? by lazy {
        (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
    }

    private val bleScanner: BluetoothLeScanner? by lazy {
        bluetoothAdapter?.bluetoothLeScanner
    }

    private val scanResults = mutableMapOf<String, BluetoothDevice>()
    private var isScanning = false

    // 扫描回调
    private val scanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            val device = result.device
            val rssi = result.rssi
            val scanRecord = result.scanRecord

            // 解析广播包
            val deviceName = scanRecord?.deviceName ?: "Unknown"
            val serviceUuids = scanRecord?.serviceUuids?.map { it.toString() } ?: emptyList()

            Log.d("BleScanner", """
                发现设备:
                - 名称: $deviceName
                - 地址: ${device.address}
                - RSSI: $rssi dBm
                - Services: $serviceUuids
            """.trimIndent())

            scanResults[device.address] = device
            onDeviceFound?.invoke(device, rssi)
        }

        override fun onScanFailed(errorCode: Int) {
            Log.e("BleScanner", "扫描失败: $errorCode")
            isScanning = false
            onScanError?.invoke(errorCode)
        }
    }

    // 回调接口
    var onDeviceFound: ((BluetoothDevice, Int) -> Unit)? = null
    var onScanError: ((Int) -> Unit)? = null

    /**
     * 开始扫描(带过滤器)
     */
    @SuppressLint("MissingPermission")
    fun startScan(
        serviceUuid: UUID? = null,
        deviceName: String? = null,
        timeoutMs: Long = 10000 // 10秒超时
    ) {
        if (isScanning) {
            Log.w("BleScanner", "扫描已在进行中")
            return
        }

        // 构建扫描过滤器
        val filters = mutableListOf<ScanFilter>()
        serviceUuid?.let {
            filters.add(ScanFilter.Builder().setServiceUuid(ParcelUuid(it)).build())
        }
        deviceName?.let {
            filters.add(ScanFilter.Builder().setDeviceName(it).build())
        }

        // 扫描设置
        val scanSettings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 低延迟模式
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE) // 积极匹配
            .build()

        // 开始扫描
        scanResults.clear()
        isScanning = true
        bleScanner?.startScan(filters, scanSettings, scanCallback)

        Log.d("BleScanner", "开始扫描BLE设备...")

        // 自动停止扫描
        Handler(Looper.getMainLooper()).postDelayed({
            if (isScanning) stopScan()
        }, timeoutMs)
    }

    /**
     * 停止扫描
     */
    @SuppressLint("MissingPermission")
    fun stopScan() {
        if (!isScanning) return

        bleScanner?.stopScan(scanCallback)
        isScanning = false
        Log.d("BleScanner", "停止扫描,共发现 ${scanResults.size} 个设备")
    }
}

4.3 连接与通信

完整GATT客户端实现

class BleClient(private val context: Context, private val device: BluetoothDevice) {
    private var bluetoothGatt: BluetoothGatt? = null
    private val serviceMap = mutableMapOf<UUID, BluetoothGattService>()

    // 连接状态回调
    var onConnectionStateChange: ((Int) -> Unit)? = null
    var onServicesDiscovered: ((List<BluetoothGattService>) -> Unit)? = null
    var onCharacteristicRead: ((UUID, ByteArray) -> Unit)? = null
    var onCharacteristicChanged: ((UUID, ByteArray) -> Unit)? = null

    private val gattCallback = object : BluetoothGattCallback() {
        @SuppressLint("MissingPermission")
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            when (newState) {
                BluetoothProfile.STATE_CONNECTED -> {
                    Log.d("BleClient", "已连接到 ${device.address}")
                    onConnectionStateChange?.invoke(newState)
                    // 发现服务
                    Handler(Looper.getMainLooper()).postDelayed({
                        gatt.discoverServices()
                    }, 600) // 等待600ms再发现服务(兼容性优化)
                }
                BluetoothProfile.STATE_DISCONNECTED -> {
                    Log.d("BleClient", "已断开连接")
                    onConnectionStateChange?.invoke(newState)
                    bluetoothGatt?.close()
                    bluetoothGatt = null
                }
            }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val services = gatt.services
                services.forEach { service ->
                    serviceMap[service.uuid] = service
                    Log.d("BleClient", "发现服务: ${service.uuid}")
                    service.characteristics.forEach { char ->
                        Log.d("BleClient", "  - 特征值: ${char.uuid}, 属性: ${char.properties}")
                    }
                }
                onServicesDiscovered?.invoke(services)
            } else {
                Log.e("BleClient", "服务发现失败: $status")
            }
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val data = characteristic.value
                Log.d("BleClient", "读取特征值: ${characteristic.uuid}, 数据: ${data.toHexString()}")
                onCharacteristicRead?.invoke(characteristic.uuid, data)
            }
        }

        override fun onCharacteristicWrite(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d("BleClient", "写入成功: ${characteristic.uuid}")
            } else {
                Log.e("BleClient", "写入失败: $status")
            }
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic
        ) {
            val data = characteristic.value
            Log.d("BleClient", "收到通知: ${characteristic.uuid}, 数据: ${data.toHexString()}")
            onCharacteristicChanged?.invoke(characteristic.uuid, data)
        }
    }

    /**
     * 连接设备
     */
    @SuppressLint("MissingPermission")
    fun connect() {
        bluetoothGatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
    }

    /**
     * 断开连接
     */
    @SuppressLint("MissingPermission")
    fun disconnect() {
        bluetoothGatt?.disconnect()
    }

    /**
     * 读取特征值
     */
    @SuppressLint("MissingPermission")
    fun readCharacteristic(serviceUuid: UUID, characteristicUuid: UUID): Boolean {
        val characteristic = serviceMap[serviceUuid]
            ?.getCharacteristic(characteristicUuid) ?: return false
        return bluetoothGatt?.readCharacteristic(characteristic) == true
    }

    /**
     * 写入特征值
     */
    @SuppressLint("MissingPermission")
    fun writeCharacteristic(serviceUuid: UUID, characteristicUuid: UUID, data: ByteArray): Boolean {
        val characteristic = serviceMap[serviceUuid]
            ?.getCharacteristic(characteristicUuid) ?: return false
        characteristic.value = data
        characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
        return bluetoothGatt?.writeCharacteristic(characteristic) == true
    }

    /**
     * 启用通知
     */
    @SuppressLint("MissingPermission")
    fun enableNotification(serviceUuid: UUID, characteristicUuid: UUID): Boolean {
        val characteristic = serviceMap[serviceUuid]
            ?.getCharacteristic(characteristicUuid) ?: return false

        // 启用本地通知
        val success = bluetoothGatt?.setCharacteristicNotification(characteristic, true) == true
        if (!success) return false

        // 写入CCCD启用远程通知
        val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
        val descriptor = characteristic.getDescriptor(cccdUuid) ?: return false
        descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
        return bluetoothGatt?.writeDescriptor(descriptor) == true
    }

    // 工具函数:字节数组转十六进制字符串
    private fun ByteArray.toHexString() = joinToString("") { "%02X".format(it) }
}

4.4 使用示例

完整使用流程

class MainActivity : AppCompatActivity() {
    private lateinit var bleScanner: BleScanner
    private var bleClient: BleClient? = null

    // 心率服务UUID
    private val HEART_RATE_SERVICE_UUID = UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB")
    private val HEART_RATE_MEASUREMENT_UUID = UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化扫描器
        bleScanner = BleScanner(this).apply {
            onDeviceFound = { device, rssi ->
                runOnUiThread {
                    addDeviceToList(device, rssi)
                }
            }
        }

        // 开始扫描
        btnScan.setOnClickListener {
            bleScanner.startScan(serviceUuid = HEART_RATE_SERVICE_UUID)
        }

        // 连接设备
        btnConnect.setOnClickListener {
            val device = getSelectedDevice() // 从列表获取选中设备
            connectToDevice(device)
        }
    }

    private fun connectToDevice(device: BluetoothDevice) {
        bleClient = BleClient(this, device).apply {
            // 监听连接状态
            onConnectionStateChange = { state ->
                runOnUiThread {
                    when (state) {
                        BluetoothProfile.STATE_CONNECTED -> {
                            tvStatus.text = "已连接"
                        }
                        BluetoothProfile.STATE_DISCONNECTED -> {
                            tvStatus.text = "已断开"
                        }
                    }
                }
            }

            // 监听服务发现
            onServicesDiscovered = { services ->
                runOnUiThread {
                    tvStatus.text = "发现 ${services.size} 个服务"
                    // 启用心率通知
                    enableNotification(HEART_RATE_SERVICE_UUID, HEART_RATE_MEASUREMENT_UUID)
                }
            }

            // 监听心率数据
            onCharacteristicChanged = { uuid, data ->
                if (uuid == HEART_RATE_MEASUREMENT_UUID) {
                    val heartRate = parseHeartRate(data)
                    runOnUiThread {
                        tvHeartRate.text = "$heartRate BPM"
                    }
                }
            }

            connect()
        }
    }

    // 解析心率数据(符合Bluetooth SIG标准)
    private fun parseHeartRate(data: ByteArray): Int {
        val flag = data[0].toInt()
        return if ((flag and 0x01) == 0) {
            // 8位心率值
            data[1].toInt() and 0xFF
        } else {
            // 16位心率值
            ((data[2].toInt() and 0xFF) shl 8) or (data[1].toInt() and 0xFF)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        bleScanner.stopScan()
        bleClient?.disconnect()
    }
}

五、行业应用案例分析

案例1:智能门锁蓝牙开锁系统

项目背景
某智能家居厂商的智能门锁产品需要实现蓝牙开锁功能,替代传统钥匙。

技术方案

智能门锁BLE架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

手机App(GATT Client)
  │
  ├─ 蓝牙握手认证
  │   └─ 交换加密密钥(AES-128)
  │
  ├─ 开锁指令
  │   └─ 写入特征值:{cmd: "unlock", token: "xxx"}
  │
  └─ 开锁日志
      └─ 读取特征值:{time: "2024-02-24 10:30", user: "张三"}

          ↓ BLE通信(加密)

智能门锁(GATT Server)
  │
  ├─ 认证服务(Custom Service)
  │   └─ 密钥交换特征值
  │
  ├─ 锁控制服务
  │   ├─ 开锁特征值(Write)
  │   └─ 锁状态特征值(Notify)
  │
  └─ 日志服务
      └─ 开锁记录特征值(Read)

GATT服务定义(门锁端):

// 门锁控制服务
val lockService = BluetoothGattService(
    UUID.fromString("0000FFF0-0000-1000-8000-00805F9B34FB"),
    BluetoothGattService.SERVICE_TYPE_PRIMARY
)

// 开锁特征值
val unlockCharacteristic = BluetoothGattCharacteristic(
    UUID.fromString("0000FFF1-0000-1000-8000-00805F9B34FB"),
    BluetoothGattCharacteristic.PROPERTY_WRITE,
    BluetoothGattCharacteristic.PERMISSION_WRITE
)

// 锁状态特征值
val lockStateCharacteristic = BluetoothGattCharacteristic(
    UUID.fromString("0000FFF2-0000-1000-8000-00805F9B34FB"),
    BluetoothGattCharacteristic.PROPERTY_NOTIFY,
    BluetoothGattCharacteristic.PERMISSION_READ
)

lockService.addCharacteristic(unlockCharacteristic)
lockService.addCharacteristic(lockStateCharacteristic)

加密开锁流程(手机端):

class SmartLockController(private val bleClient: BleClient) {
    private val LOCK_SERVICE_UUID = UUID.fromString("0000FFF0-0000-1000-8000-00805F9B34FB")
    private val UNLOCK_CHAR_UUID = UUID.fromString("0000FFF1-0000-1000-8000-00805F9B34FB")

    // AES加密工具
    private val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
    private val secretKey = SecretKeySpec("1234567890abcdef".toByteArray(), "AES")

    /**
     * 发送开锁指令
     */
    fun unlock(userId: String) {
        // 构造开锁指令
        val command = JSONObject().apply {
            put("cmd", "unlock")
            put("user_id", userId)
            put("timestamp", System.currentTimeMillis())
        }.toString()

        // AES加密
        cipher.init(Cipher.ENCRYPT_MODE, secretKey)
        val encryptedData = cipher.doFinal(command.toByteArray())

        // 通过BLE发送
        bleClient.writeCharacteristic(LOCK_SERVICE_UUID, UNLOCK_CHAR_UUID, encryptedData)
    }
}

实施效果

指标 数据 说明
开锁响应时间 <500ms 从触发到门锁打开
连接成功率 98.5% 3米内稳定连接
电池续航 12个月 CR123A电池(1500mAh)
安全性 AES-128加密 符合金融级加密标准

案例2:医疗级血糖仪数据同步

项目背景
某医疗器械公司的血糖仪需将测量数据同步到手机App,供医生远程查看。

技术方案

血糖仪BLE数据同步架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

血糖仪(GATT Server)
  │
  ├─ 血糖服务(Glucose Service, 0x1808)
  │   ├─ 血糖测量特征值(0x2A18)
  │   │   - Properties: Notify
  │   │   - Value: {glucose: 5.6 mmol/L, time: "2024-02-24 08:00"}
  │   │
  │   └─ 血糖上下文特征值(0x2A34)
  │       - Properties: Notify
  │       - Value: {meal: "breakfast", medication: "insulin"}
  │
  └─ 电池服务(0x180F)
      └─ 电池电量(0x2A19)

          ↓ BLE Notify(自动推送)

手机App(GATT Client)
  │
  ├─ 接收血糖数据
  │   └─ 本地存储 + 云端同步
  │
  └─ 生成血糖趋势图
      └─ 异常值告警(低血糖<3.9, 高血糖>11.1)

血糖数据格式(符合Bluetooth SIG标准):

data class GlucoseMeasurement(
    val sequenceNumber: Int,          // 序列号
    val baseTime: Date,               // 测量时间
    val glucoseConcentration: Float,  // 血糖浓度(mmol/L)
    val type: GlucoseType,            // 样本类型(毛细血管/静脉)
    val sampleLocation: SampleLocation, // 采样位置(手指/手臂)
    val sensorStatus: Int             // 传感器状态
)

// 解析血糖数据(符合GATT Glucose Service规范)
fun parseGlucoseMeasurement(data: ByteArray): GlucoseMeasurement {
    val buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)

    val flags = buffer.get().toInt()
    val sequenceNumber = buffer.short.toInt()

    // 解析时间戳(7字节)
    val year = buffer.short.toInt()
    val month = buffer.get().toInt()
    val day = buffer.get().toInt()
    val hour = buffer.get().toInt()
    val minute = buffer.get().toInt()
    val second = buffer.get().toInt()
    val baseTime = Calendar.getInstance().apply {
        set(year, month - 1, day, hour, minute, second)
    }.time

    // 解析血糖浓度(SFLOAT格式,2字节)
    val glucoseRaw = buffer.short.toInt()
    val glucoseConcentration = decodeSFloat(glucoseRaw)

    // 解析类型和位置
    val typeAndLocation = if ((flags and 0x02) != 0) buffer.get().toInt() else 0
    val type = GlucoseType.values()[(typeAndLocation and 0x0F)]
    val sampleLocation = SampleLocation.values()[((typeAndLocation shr 4) and 0x0F)]

    return GlucoseMeasurement(
        sequenceNumber, baseTime, glucoseConcentration, type, sampleLocation, 0
    )
}

// SFLOAT解码(IEEE-11073格式)
fun decodeSFloat(value: Int): Float {
    val mantissa = value and 0x0FFF
    val exponent = (value shr 12) and 0x0F
    return mantissa * 10f.pow(exponent - 3)
}

数据同步实现(手机端):

class GlucoseMeterManager(private val bleClient: BleClient) {
    private val GLUCOSE_SERVICE_UUID = UUID.fromString("00001808-0000-1000-8000-00805F9B34FB")
    private val GLUCOSE_MEASUREMENT_UUID = UUID.fromString("00002A18-0000-1000-8000-00805F9B34FB")

    private val glucoseHistory = mutableListOf<GlucoseMeasurement>()

    fun startMonitoring() {
        // 启用血糖测量通知
        bleClient.enableNotification(GLUCOSE_SERVICE_UUID, GLUCOSE_MEASUREMENT_UUID)

        // 监听数据
        bleClient.onCharacteristicChanged = { uuid, data ->
            if (uuid == GLUCOSE_MEASUREMENT_UUID) {
                val measurement = parseGlucoseMeasurement(data)
                handleGlucoseData(measurement)
            }
        }
    }

    private fun handleGlucoseData(measurement: GlucoseMeasurement) {
        // 存储到本地数据库
        glucoseHistory.add(measurement)

        // 异常值告警
        when {
            measurement.glucoseConcentration < 3.9f -> {
                showAlert("低血糖警告", "当前血糖: ${measurement.glucoseConcentration} mmol/L")
            }
            measurement.glucoseConcentration > 11.1f -> {
                showAlert("高血糖警告", "当前血糖: ${measurement.glucoseConcentration} mmol/L")
            }
        }

        // 同步到云端
        uploadToCloud(measurement)
    }
}

实施效果

指标 数据 说明
数据传输成功率 99.2% 500次测试
数据同步延迟 <2s 从测量到App显示
符合医疗标准 FDA认证 满足ISO 15197:2013
电池续航 1000次测量 两节AAA电池

案例3:运动手环心率监测

项目背景
某运动品牌的智能手环需实时传输心率数据到手机App。

技术方案

  • 采样频率:1Hz(每秒1次)
  • 传输方式:BLE Notify(无需确认,降低延迟)
  • 功耗优化:动态调整连接间隔

连接参数优化

// 运动模式:低延迟
val sportModeParams = ConnectionParams(
    intervalMin = 20,  // 20ms
    intervalMax = 40,  // 40ms
    latency = 0,       // 无延迟
    timeout = 2000     // 2s超时
)

// 日常模式:低功耗
val dailyModeParams = ConnectionParams(
    intervalMin = 100,  // 100ms
    intervalMax = 200,  // 200ms
    latency = 4,        // 跳过4个事件
    timeout = 5000      // 5s超时
)

// 动态切换
fun adjustConnectionParams(isExercising: Boolean) {
    val params = if (isExercising) sportModeParams else dailyModeParams
    bluetoothGatt?.requestConnectionPriority(
        if (isExercising) BluetoothGatt.CONNECTION_PRIORITY_HIGH
        else BluetoothGatt.CONNECTION_PRIORITY_BALANCED
    )
}

实施效果

模式 连接间隔 延迟 功耗 续航
运动模式 20-40ms <50ms 8mA 24小时
日常模式 100-200ms <1s 0.5mA 7天

案例4:TWS耳机低延迟音频传输

项目背景
某音频厂商的TWS耳机需实现<80ms的低延迟音频传输(游戏模式)。

技术方案

  • 协议:BLE 5.2 LE Audio(LC3编码)
  • 连接间隔:7.5ms(最小值)
  • 音频帧大小:120字节/帧
  • 编码延迟:10ms(LC3)

延迟分解

TWS耳机音频延迟分解
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

手机端:
  ├─ 音频采集           10ms
  ├─ LC3编码            10ms
  └─ BLE发送缓冲        7.5ms

BLE传输:
  └─ 空中传输时间       7.5ms(1个连接间隔)

耳机端:
  ├─ BLE接收处理        5ms
  ├─ LC3解码            10ms
  └─ DAC播放缓冲        20ms

总延迟:10 + 10 + 7.5 + 7.5 + 5 + 10 + 20 = 70ms

实施效果

指标 数据 对比
音频延迟 70ms 传统蓝牙:200-300ms
音质 LC3 @ 160kbps 接近SBC @ 328kbps
功耗 5mA(播放) 传统蓝牙:8mA
续航 6小时(单次) 充电盒30小时

案例5:iBeacon室内定位系统

项目背景
某商场部署iBeacon信标,实现顾客室内导航和精准营销。

技术方案

iBeacon室内定位架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

iBeacon信标(固定位置)
  │
  ├─ 信标A(入口)
  │   UUID: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
  │   Major: 1, Minor: 1
  │   Tx Power: -59 dBm
  │
  ├─ 信标B(服装区)
  │   Major: 1, Minor: 2
  │
  └─ 信标C(电子区)
      Major: 1, Minor: 3

          ↓ BLE广播

顾客手机App
  │
  ├─ 扫描iBeacon信标
  │   └─ 根据RSSI计算距离
  │
  ├─ 三边定位算法
  │   └─ 确定顾客位置(精度3-5m)
  │
  └─ 推送附近商品信息
      └─ "您身边的XX商品正在打折!"

iBeacon广播包格式

// iBeacon广播包结构(31字节)
val iBeaconPacket = byteArrayOf(
    // 标准前缀
    0x02, 0x01, 0x06,           // Flags
    0x1A, 0x FF,                // 厂商数据长度+类型
    0x4C, 0x00,                 // Apple公司ID
    0x02, 0x15,                 // iBeacon标识

    // UUID(16字节)
    0xE2.toByte(), 0xC5.toByte(), 0x6D.toByte(), 0xB5.toByte(),
    0xDF.toByte(), 0xFB.toByte(), 0x48.toByte(), 0xD2.toByte(),
    0xB0.toByte(), 0x60.toByte(), 0xD0.toByte(), 0xF5.toByte(),
    0xA7.toByte(), 0x10.toByte(), 0x96.toByte(), 0xE0.toByte(),

    // Major(2字节)
    0x00, 0x01,                 // Major = 1

    // Minor(2字节)
    0x00, 0x01,                 // Minor = 1

    // Tx Power(1字节)
    0xC5.toByte()               // -59 dBm
)

距离计算(基于RSSI):

fun calculateDistance(rssi: Int, txPower: Int): Double {
    if (rssi == 0) return -1.0

    val ratio = rssi * 1.0 / txPower
    return if (ratio < 1.0) {
        ratio.pow(10.0)
    } else {
        (0.89976) * ratio.pow(7.7095) + 0.111
    }
}

// 示例
val distance1 = calculateDistance(-65, -59) // ≈ 2.5m
val distance2 = calculateDistance(-75, -59) // ≈ 8.0m
val distance3 = calculateDistance(-85, -59) // ≈ 20.0m

实施效果

指标 数据 说明
定位精度 3-5m 三个信标三边定位
响应速度 <1s 从进入区域到推送
信标续航 2年 CR2032电池(225mAh)
营销转化率提升 35% 精准推送效果

六、性能优化实战

6.1 连接稳定性优化

问题:连接频繁断开,特别是在信号较弱或干扰较多的环境。

优化策略

class ConnectionStabilityManager(private val bluetoothGatt: BluetoothGatt) {

    /**
     * 策略1:自动重连机制
     */
    private var reconnectAttempts = 0
    private val maxReconnectAttempts = 3

    @SuppressLint("MissingPermission")
    fun handleDisconnection() {
        if (reconnectAttempts < maxReconnectAttempts) {
            reconnectAttempts++
            Handler(Looper.getMainLooper()).postDelayed({
                bluetoothGatt.connect() // 自动重连
            }, 2000L * reconnectAttempts) // 指数退避:2s, 4s, 6s
        } else {
            Log.e("BLE", "重连失败,已达最大尝试次数")
        }
    }

    /**
     * 策略2:连接参数优化
     */
    @SuppressLint("MissingPermission")
    fun optimizeConnectionParams() {
        // 高优先级连接(降低丢包率)
        bluetoothGatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)

        // 延迟600ms后再发现服务(等待连接稳定)
        Handler(Looper.getMainLooper()).postDelayed({
            bluetoothGatt.discoverServices()
        }, 600)
    }

    /**
     * 策略3:MTU协商(减少分包)
     */
    @SuppressLint("MissingPermission")
    fun requestLargerMtu() {
        // 请求更大的MTU(默认23字节,最大512字节)
        bluetoothGatt.requestMtu(512)
    }
}

实施效果

优化前 优化后 提升
连接成功率:85% 连接成功率:98% +15%
平均断线次数:5次/小时 平均断线次数:<1次/小时 -80%

6.2 数据传输效率优化

问题:传输大量数据时速度慢,耗时长。

优化策略

/**
 * 策略1:MTU优化(减少分包)
 */
fun negotiateMtu(gatt: BluetoothGatt) {
    // 协商更大的MTU
    gatt.requestMtu(512) // 512字节(BLE 4.2+)

    // MTU协商回调
    override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d("BLE", "MTU已更新: $mtu 字节")
            // 有效载荷 = MTU - 3(ATT头部)
            val maxPayload = mtu - 3 // 509字节
        }
    }
}

/**
 * 策略2:数据分块传输
 */
class ChunkedDataTransfer(private val bleClient: BleClient) {
    private val CHUNK_SIZE = 509 // MTU=512时的有效载荷

    fun sendLargeData(data: ByteArray, serviceUuid: UUID, charUuid: UUID) {
        val chunks = data.toList().chunked(CHUNK_SIZE)
        Log.d("BLE", "分块传输: ${chunks.size} 块")

        chunks.forEachIndexed { index, chunk ->
            val chunkData = chunk.toByteArray()
            bleClient.writeCharacteristic(serviceUuid, charUuid, chunkData)

            // 等待写入完成(避免队列溢出)
            Thread.sleep(20) // 20ms间隔

            Log.d("BLE", "已发送: ${index + 1}/${chunks.size}")
        }
    }
}

/**
 * 策略3:使用Write Without Response(快速写入)
 */
fun fastWrite(characteristic: BluetoothGattCharacteristic, data: ByteArray): Boolean {
    characteristic.value = data
    characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
    return bluetoothGatt?.writeCharacteristic(characteristic) == true
}

传输速度对比

方案 MTU 写入类型 速度 适用场景
默认 23字节 Write 5 KB/s 小数据
MTU优化 512字节 Write 40 KB/s 需可靠性
极速模式 512字节 No Response 80 KB/s 可容忍丢包

6.3 功耗优化

问题:外围设备(传感器/手环)电池续航不足。

优化策略

/**
 * 策略1:动态调整连接间隔
 */
class PowerOptimizer {
    fun adjustConnectionInterval(isActive: Boolean, gatt: BluetoothGatt) {
        val priority = if (isActive) {
            BluetoothGatt.CONNECTION_PRIORITY_HIGH      // 7.5-30ms(高功耗)
        } else {
            BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER // 100-125ms(低功耗)
        }
        gatt.requestConnectionPriority(priority)
    }

    /**
     * 策略2:使用Slave Latency
     */
    fun configureSlave Latency(): ConnectionParams {
        return ConnectionParams(
            intervalMin = 200,  // 200ms连接间隔
            intervalMax = 400,  // 400ms
            latency = 4,        // 跳过4个事件 → 实际2s唤醒一次
            timeout = 10000     // 10s超时
        )
        // 功耗降低:80%
        // 代价:延迟增加到2s
    }

    /**
     * 策略3:广播间隔优化
     */
    fun setAdvertisingInterval(): AdvertisingSettings {
        return AdvertisingSettings.Builder()
            .setAdvertiseMode(AdvertisingSettings.ADVERTISE_MODE_LOW_POWER) // 1s间隔
            .setTxPowerLevel(AdvertisingSettings.ADVERTISE_TX_POWER_ULTRA_LOW) // 超低功率
            .setConnectable(true)
            .build()
        // 续航提升:3倍
    }
}

功耗对比(典型传感器):

模式 连接间隔 从机延迟 平均功耗 续航(500mAh电池)
实时模式 20ms 0 8mA 2.6天
平衡模式 100ms 2 2mA 10.4天
省电模式 400ms 4 0.5mA 41.7天

七、故障排查方案

问题1:扫描不到设备

现象:调用startScan()后,onScanResult回调从未触发。

排查步骤

// 步骤1:检查权限
fun checkPermissions(): Boolean {
    val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        arrayOf(
            Manifest.permission.BLUETOOTH_SCAN,
            Manifest.permission.ACCESS_FINE_LOCATION
        )
    } else {
        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
    }

    return permissions.all {
        ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
    }
}

// 步骤2:检查蓝牙和位置服务
fun checkPrerequisites(): Boolean {
    val bluetoothAdapter = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
    val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager

    return bluetoothAdapter?.isEnabled == true &&
           locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
}

// 步骤3:检查扫描过滤器
fun debugScanFilters() {
    // 移除所有过滤器,尝试扫描所有设备
    bleScanner?.startScan(
        emptyList(),  // 空过滤器列表
        ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build(),
        scanCallback
    )
}

常见原因与解决方案

原因 解决方案
未授予位置权限 动态申请ACCESS_FINE_LOCATION
GPS未打开 提示用户打开位置服务
Android 12+未授予BLUETOOTH_SCAN 申请新蓝牙权限
扫描过滤器过严 先用空过滤器测试
设备未广播 检查外围设备是否正常工作

问题2:连接失败(Error 133)

现象onConnectionStateChange返回status=133,连接失败。

原因:Error 133是Android BLE栈的通用错误,可能由多种原因引起。

排查与解决

class ConnectionErrorHandler(private val device: BluetoothDevice) {

    @SuppressLint("MissingPermission")
    fun handleError133() {
        // 解决方案1:清除GATT缓存(反射调用)
        fun clearGattCache(gatt: BluetoothGatt): Boolean {
            return try {
                val refreshMethod = gatt.javaClass.getMethod("refresh")
                refreshMethod.invoke(gatt) as Boolean
            } catch (e: Exception) {
                false
            }
        }

        // 解决方案2:延迟重连
        Handler(Looper.getMainLooper()).postDelayed({
            device.connectGatt(context, false, gattCallback)
        }, 1000)

        // 解决方案3:使用autoConnect=true(后台连接)
        device.connectGatt(context, true, gattCallback) // 自动连接模式
    }
}

常见原因与解决方案

原因 解决方案
GATT缓存损坏 调用gatt.refresh()清除缓存
设备已被其他App连接 确保设备断开其他连接
连接请求过快 延迟1s后重试
设备距离过远 靠近设备后重试
蓝牙栈异常 重启蓝牙或重启手机

问题3:通知未收到

现象:调用enableNotification()后,onCharacteristicChanged回调从未触发。

排查步骤

@SuppressLint("MissingPermission")
fun debugNotification(serviceUuid: UUID, charUuid: UUID) {
    val characteristic = bluetoothGatt?.getService(serviceUuid)
        ?.getCharacteristic(charUuid)

    // 检查1:特征值是否支持Notify
    val supportsNotify = (characteristic?.properties?.and(
        BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0)
    Log.d("BLE", "支持Notify: $supportsNotify")

    // 检查2:CCCD是否存在
    val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
    val cccd = characteristic?.getDescriptor(cccdUuid)
    Log.d("BLE", "CCCD存在: ${cccd != null}")

    // 检查3:是否正确写入CCCD
    if (cccd != null) {
        cccd.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
        val success = bluetoothGatt?.writeDescriptor(cccd)
        Log.d("BLE", "写入CCCD: $success")
    }

    // 检查4:是否启用了本地通知
    val localEnabled = bluetoothGatt?.setCharacteristicNotification(characteristic, true)
    Log.d("BLE", "启用本地通知: $localEnabled")
}

常见原因与解决方案

原因 解决方案
特征值不支持Notify 检查GATT定义,改用Read轮询
未写入CCCD 必须写入0x0001启用
写入顺序错误 setCharacteristicNotificationwriteDescriptor
外围设备未发送 检查设备端代码

问题4:数据丢失或乱序

现象:Notify数据有时丢失,或收到的数据顺序混乱。

原因与解决

/**
 * 解决方案1:添加序列号
 */
data class Packet(
    val sequenceNumber: Int,  // 序列号
    val payload: ByteArray    // 实际数据
)

fun sendWithSequence(data: ByteArray) {
    val packet = ByteBuffer.allocate(data.size + 2).apply {
        putShort(currentSequence++)  // 序列号(2字节)
        put(data)                    // 数据
    }.array()

    bluetoothGatt?.writeCharacteristic(characteristic, packet)
}

/**
 * 解决方案2:使用Indicate代替Notify
 */
fun enableIndication(characteristic: BluetoothGattCharacteristic) {
    // Indicate需要确认,保证可靠性
    val cccd = characteristic.getDescriptor(CCCD_UUID)
    cccd.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
    bluetoothGatt?.writeDescriptor(cccd)
}

/**
 * 解决方案3:客户端重组数据
 */
class DataReassembler {
    private val buffer = mutableMapOf<Int, ByteArray>()

    fun addChunk(seq: Int, data: ByteArray) {
        buffer[seq] = data

        // 检查是否连续
        if (buffer.keys.sorted() == (0 until buffer.size).toList()) {
            val completeData = buffer.values.fold(byteArrayOf()) { acc, chunk ->
                acc + chunk
            }
            onDataComplete(completeData)
            buffer.clear()
        }
    }
}

八、BLE技术对比

8.1 BLE vs WiFi vs Zigbee

对比维度 BLE WiFi Zigbee
功耗 ⭐⭐⭐⭐⭐
< 15mA
⭐⭐
100-300mA
⭐⭐⭐⭐
30-50mA
传输速率 ⭐⭐⭐
1-2 Mbps
⭐⭐⭐⭐⭐
11-600 Mbps

250 Kbps
传输距离 ⭐⭐⭐
10-100m
⭐⭐⭐⭐
50-300m
⭐⭐⭐
10-100m
连接延迟 ⭐⭐⭐⭐⭐
6ms
⭐⭐⭐
50-100ms
⭐⭐⭐
15-30ms
设备成本 ⭐⭐⭐⭐⭐
$1-5
⭐⭐
$5-20
⭐⭐⭐
$3-10
网络拓扑 星型(1对多) 星型 Mesh(自组网)
典型应用 穿戴、医疗、近场交互 视频、大文件、智能家居 智能家居、工业自动化
平台支持 iOS/Android/Win/Mac/Linux 全平台 需网关

选型建议

  • 选BLE:低功耗优先、需手机直连、小数据量、成本敏感
  • 选WiFi:需高带宽(视频/音频)、已有WiFi网络、实时性要求不高
  • 选Zigbee:需Mesh组网、设备数量多(>50)、不需要手机直连

8.2 BLE 4.x vs BLE 5.x

特性 BLE 4.2 BLE 5.0 BLE 5.2
传输速率 1 Mbps 2 Mbps 2 Mbps
传输距离 100m 400m(4倍) 400m
广播容量 31字节 255字节(8倍) 255字节
数据包长度 255字节 255字节 255字节
方向查找 不支持 不支持 支持AoA/AoD
LE Audio 不支持 不支持 支持
室内定位精度 5-10m 5-10m <1m
发布时间 2014 2016 2020

升级收益

  • BLE 5.0:适用于需要更远距离(仓储追踪)或更大广播包(信标数据)的场景
  • BLE 5.2:适用于音频设备(TWS耳机)或室内定位(商场导航)

九、总结

3句话记住BLE

  1. BLE是专为IoT设计的低功耗蓝牙技术,通过GATT协议实现短距离数据传输,广泛应用于穿戴、医疗、智能家居。
  2. 核心优势是超低功耗(<15mA活跃,<1μA休眠),纽扣电池可运行数月至数年,连接延迟<6ms。
  3. GATT层次结构(Service → Characteristic → Descriptor)定义了数据组织方式,Notify机制实现低延迟实时推送。

核心要点

技术选型建议
场景 是否选BLE 原因
智能手环/手表 ✅ 推荐 低功耗、手机直连、小数据
智能门锁 ✅ 推荐 安全、低功耗、成本低
血糖仪/血压计 ✅ 推荐 医疗标准支持、数据同步
实时视频监控 ❌ 不推荐 带宽不足,选WiFi
大型智能家居(>50设备) ❌ 不推荐 Mesh网络弱,选Zigbee/Thread
车载音频 ⚠️ 谨慎 延迟高,选BLE 5.2 LE Audio
最佳实践总结
  1. 连接管理

    • 使用autoConnect=true实现后台自动重连
    • 协商更大MTU(512字节)提升传输效率
    • 动态调整连接间隔平衡功耗与延迟
  2. 数据传输

    • 小数据用Notify(无需确认,低延迟)
    • 关键数据用Indicate(需确认,保证可靠)
    • 大数据分块传输,每块<MTU-3字节
  3. 功耗优化

    • 利用Slave Latency(跳过无数据事件)
    • 广播间隔设置为1s或更长
    • 非活跃时切换到低功耗连接模式
  4. 安全性

    • 使用AES-128加密敏感数据(如门锁指令)
    • 配对时启用MITM保护
    • 定期更新加密密钥
关键注意事项
  1. Android 12权限变更:必须申请BLUETOOTH_SCANBLUETOOTH_CONNECT
  2. 位置权限:扫描BLE设备需要ACCESS_FINE_LOCATION(即使不使用GPS)
  3. GATT缓存:连接异常时调用gatt.refresh()清除缓存
  4. 线程安全:所有BLE操作都在主线程回调,避免阻塞UI
  5. 内存泄漏:及时调用gatt.close()释放资源

扩展阅读

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐