精华内容
下载资源
问答
  • 蓝牙通信协议

    2013-08-30 11:21:23
    可以更详细的了解蓝牙通信协议,方便编写蓝牙通信
  • 百度智能手环蓝牙私有通信协议.用于开发手环使用,用于开发手环使用
  • 小伙伴觉得iOS的蓝牙不够用,最近又跟我提了要求做一个Android的版本。想了想最近Kotlin好像开始流行起来了,就想要不顺便用Kotlin写一个基于蓝牙BLE的上位机App。 开始着手这个工作后,我稍微有点后悔,我发现虽然...

    小伙伴觉得iOS的蓝牙不够用,最近又跟我提了要求做一个Android的版本。想了想最近Kotlin好像开始流行起来了,就想要不顺便用Kotlin写一个基于蓝牙BLE的上位机App。
    开始着手这个工作后,我稍微有点后悔,我发现虽然都是BLE,但是Android的实现起来其实挺麻烦的。查了很多资料,包括谷歌官方的,很多关键问题都没有说清楚,只能试着边写边做了。
    好在最后还是花了3天,东拼西凑的顺利完成。

    Kotlin的语法我这里不过多描述,毕竟从谷歌推Kotlin到现在也有好几年的时间,相关的资料和书籍其实有很多了,所以你可以自己去找一找。如果你做过Swift类似的开发,那么熟悉Kotlin其实应该很快的。

    我觉得最简单的还是直接扔代码得了,你可以自己复制粘贴后自己测试一下,不出意外应该都能顺利执行。至于其他扩展功能,你就自己去想想办法吧。

    首先,最关键的是蓝牙类:

    package com.seagosoft.kotlin.android.app
    
    import android.bluetooth.BluetoothAdapter
    import android.bluetooth.BluetoothGatt
    import android.bluetooth.BluetoothGattCharacteristic
    import android.bluetooth.le.ScanSettings
    import android.content.Context
    import android.os.Build
    import androidx.annotation.RequiresApi
    
    
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    open class BluetoothHandler {
    
        // ble适配器
        protected val adapter: BluetoothAdapter by lazy {
            BluetoothAdapter.getDefaultAdapter()
        }
    
        // 通用属性协议,定义了BLE通讯的基本规则和操作
        private var selectedGatt: BluetoothGatt? = null
    
        // bluetooth gattcallback
        private val gattCallback = BlueGattCallbackImp()
    
        // bluetooth scancallback
        private val scanCallback = BluetoothScanCallbackImp()
    
    
        // 开始搜索设备
        fun startScanPeripherals() {
    
            // 搜索条件
            val scanSettings = ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                    .build()
    
            scanCallback.cleanPerals()
            adapter.bluetoothLeScanner.startScan(null, scanSettings, scanCallback)
        }
    
    
        // 停止搜索设备
        fun stopScanPeripherals() {
            adapter.bluetoothLeScanner.stopScan(scanCallback)
        }
    
    
        // 返回给用户查找到的设备名称
        fun deviceNames(): MutableList<String> {
            val devices = mutableListOf<String>()
    
            for (item in scanCallback.peripherals) {
                devices.add(item.key)
            }
            return devices
        }
    
    
        // 通过设备名连结到给定的设备
        fun connectDeviceByName(context: Context, name: String) {
            if (! scanCallback.peripherals.containsKey(name)) return // 不存在指定的设备
    
            // 断开设备连结
            disconnectDevice()
    
            // 有指定的设备
            val device = scanCallback.peripherals[name]
    
            // 连结至BLE设备
            if (device != null) {
                selectedGatt = device.connectGatt(context, false, gattCallback)
            }
        }
    
    
        // 和BLE设备断开连结
        fun disconnectDevice() {
            if (selectedGatt != null) {
                selectedGatt?.disconnect()
                selectedGatt?.close()
            }
        }
    
    	// 用户调用数据发送接口
        fun send(msg: String) {
            val writeChar = gattCallback.writeCharacteristic
    
            if (writeChar != null) {
                writeChar.setValue(msg)
                selectedGatt?.writeCharacteristic(writeChar)
            }
        }
    }
    

    然后分别是两个回调函数的实现,下面这个是搜索蓝牙设备时的回调函数实现

    package com.seagosoft.kotlin.android.app
    
    import android.bluetooth.BluetoothDevice
    import android.bluetooth.le.ScanCallback
    import android.bluetooth.le.ScanResult
    import android.os.Build
    import android.util.Log
    import androidx.annotation.RequiresApi
    
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    class BluetoothScanCallbackImp:  ScanCallback() {
    
        // 外置ble设备列表
        val peripherals = HashMap<String, BluetoothDevice>()
    
        // 清理设备列表
        fun cleanPerals() {
            peripherals.clear()
        }
    
        // BLE设备搜索回调类
        override fun onScanResult(callbackType: Int, result: ScanResult?) {
            super.onScanResult(callbackType, result)
    
            val device = result?.device
    
            if (device != null && device.name != null && device.address != null) {
                // for debug
                Log.d("ScanCallback", "found device: ${device.name} address: ${device.address}")
    
                // append device to the list
                peripherals[device.name] = device
            }
        }
    }
    

    然后是通信消息回调的实现

    package com.seagosoft.kotlin.android.app
    
    import android.bluetooth.*
    import android.os.Build
    import android.util.Log
    import androidx.annotation.RequiresApi
    import java.util.*
    
    
    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    class BlueGattCallbackImp : BluetoothGattCallback() {
    
        var writeCharacteristic:    BluetoothGattCharacteristic? = null
        var notifyCharacteristic:   BluetoothGattCharacteristic? = null
    
        companion object {
            private const val SERVICES_UUID   = "0000ffe0-xxxx"    //服务的UUID
            private const val WRITE_UUID      = "0000ffe2-xxxx"    //写入特征的UUID
            private const val NOTIFY_UUID     = "0000ffe1-xxxx"    //监听特征的UUID
            private const val NOTIFY_SERVICE_UUID = "00002902-xxxx" // service
        }
    
    
        // 与BLE设备连结后,设备连结状态发生改变时
        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
            super.onConnectionStateChange(gatt, status, newState)
    
            val deviceAddress = gatt?.device?.address
    
            if (status == BluetoothGatt.GATT_SUCCESS) {
    
                // Discovers services offered by a remote device as well as
                // their characteristics and descriptors.
                gatt?.discoverServices()
    
                when(newState) {
                    BluetoothProfile.STATE_CONNECTED -> {
                        Log.w("BluetoothGattCallback", "Successfully connected to $deviceAddress")
                    }
    
                    BluetoothProfile.STATE_DISCONNECTED -> {
                        Log.w("BluetoothGattCallback", "Successfully disconnected from $deviceAddress")
                        gatt?.close()
                    }
                }
            } else {
                Log.w("BluetoothGattCallback", "Error $status encountered for $deviceAddress! Disconnecting...")
                gatt?.close()
            }
        }
    
    
        // 向BLE设备发送信号
        override fun onCharacteristicWrite(
                gatt: BluetoothGatt?,
                characteristic: BluetoothGattCharacteristic?,
                status: Int) {
            super.onCharacteristicWrite(gatt, characteristic, status)
    
            val uuid = characteristic?.uuid
            val value = characteristic?.value
    
            val strValue = value?.let { String(it) }
    
            when (status) {
                BluetoothGatt.GATT_SUCCESS -> {
                    Log.i("BluetoothGattCallback", "Wrote to characteristic $uuid | value: $strValue")
                }
                BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH -> {
                    Log.e("BluetoothGattCallback", "Write exceeded connection ATT MTU!")
                }
                BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
                    Log.e("BluetoothGattCallback", "Write not permitted for $uuid!")
                }
                else -> {
                    Log.e("BluetoothGattCallback", "Characteristic write failed for $uuid, error: $status")
                }
            }
        }
    
    
        // 从BLE设备读取数据
        override fun onCharacteristicRead(
                gatt: BluetoothGatt?,
                characteristic: BluetoothGattCharacteristic?,
                status: Int) {
            super.onCharacteristicRead(gatt, characteristic, status)
    
            val uuid = characteristic?.uuid
            val value = characteristic?.value
    
            val strVal = value?.let { String(it) }
    
            when (status) {
                BluetoothGatt.GATT_SUCCESS -> {
                    Log.i("BluetoothGattCallback", "Read characteristic $uuid:\n$strVal")
                }
                BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
                    Log.e("BluetoothGattCallback", "Read not permitted for $uuid!")
                }
                else -> {
                    Log.e("BluetoothGattCallback", "Characteristic read failed for $uuid, error: $status")
                }
            }
        }
    
    
        // BLE设备的Characteristic发生改变时
        override fun onCharacteristicChanged(
                gatt: BluetoothGatt?,
                characteristic: BluetoothGattCharacteristic?) {
            super.onCharacteristicChanged(gatt, characteristic)
    
            val uuid = characteristic?.uuid
            val value = characteristic?.value
    
            val strValue = value?.let { String(it) }
    
            Log.i("BluetoothGattCallback", "Characteristic $uuid changed | value: $strValue")
        }
    
    
        // 发现BLE服务
        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
            super.onServicesDiscovered(gatt, status)
    
            val services = gatt?.services
            val device = gatt?.device
    
            Log.w("BluetoothGattCallback", "Discovered ${services?.size} services for ${device?.address}")
    
            if (services != null) {
                printGattTable(services) // See implementation just above this section
    
                // 注册ble服务
                setupBLEService(gatt, services)
            }
        }
    
    
        // 打印服务
        private fun printGattTable(services: MutableList<BluetoothGattService>) {
            if (services.isEmpty()) {
                Log.i("printGattTable", "No service and characteristic available, call discoverServices() first?")
                return
            }
    
            services.forEach { service ->
                val characteristicsTable = service.characteristics.joinToString(
                        separator = "\n|--",
                        prefix = "|--"
                ) { it.uuid.toString() }
    
                Log.i("printGattTable", "\nService ${service.uuid}\nCharacteristics:\n$characteristicsTable")
            }
        }
    
    
        // 设置BLE收发服务及特征
        private fun setupBLEService(gatt: BluetoothGatt, services: MutableList<BluetoothGattService>) {
    
            // 设置serviceUUID
            val service = gatt.getService(UUID.fromString(Companion.SERVICES_UUID))
            // 设置写入特征UUID
            writeCharacteristic = service.getCharacteristic(UUID.fromString(WRITE_UUID))
            // 设置监听特征UUID
            notifyCharacteristic = service.getCharacteristic(UUID.fromString(NOTIFY_UUID))
    
            // 开启消息监听
            gatt.setCharacteristicNotification(notifyCharacteristic,true)
    
            // 设置消息监听描述符,使得传入的数据通过回调函数得以体现
            val descriptor = notifyCharacteristic?.getDescriptor(UUID.fromString(NOTIFY_SERVICE_UUID))
            descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
            gatt.writeDescriptor(descriptor)
    
            // debug
            Log.d("BluetoothGattCallback", "Connection to server done");
        }
    }
    

    你需要注意的是UUID,不同的设备和服务的UUID是不一样的,你要自己去调试,得到BLE设备的UUID,具体的我就不细说了。

    最后,你需要取得Android的权限控制

        <!--Allows the app to connect to Bluetooth devices. -->
        <uses-permission android:name="android.permission.BLUETOOTH" />
    
        <!-- Allows the app to scan for and bond with Bluetooth devices. -->
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    
        <!-- As of Android M (6.0) and above, location permission is required for the
        app to get BLE scan results. -->
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    
        <!-- Declare that app uses BLE features on Android devices. -->
        <uses-feature
            android:name="android.hardware.bluetooth_le"
            android:required="true" />
    

    觉得有用,能帮到你的话,点个赞👍再走~

    展开全文
  • Android蓝牙通信

    千次下载 热门讨论 2011-12-09 17:22:11
    Android蓝牙通信,连个手机直接通信,收发数据,界面简洁友好,代码易懂。使用的是虚拟串口通信,波特率9600,也可以和其他非手机端蓝牙设备通信,只要使用协议相同就行了。我的博客有说明...
  • 这几天小伙伴又给我提了一个新需求,要求给他们的项目提供一个iOS端的蓝牙通信App,虽然iOS也做过一些,Objective C和Swift多少也知道一些,不过并没有深入的研究过iOS的开发,为了节省工期囫囵吞枣的看了一些资料,...

    这几天小伙伴又给我提了一个新需求,要求给他们的项目提供一个iOS端的蓝牙通信App,虽然iOS也做过一些,Objective C和Swift多少也知道一些,不过并没有深入的研究过iOS的开发,为了节省工期囫囵吞枣的看了一些资料,像之前那篇文章一样,我这里就不舞大刀,我把我参考的资料就放在下面这里,你如果需要可以去看看。

    《[iOS/swift]蓝牙连接》

    Github代码仓库:MichaelLynx/BleDemo

    《Swift CoreBluetooth 蓝牙库》

    我参考这些资料和代码的基础上,重写了其中一部分代码,并且做了一定的封装,你可以直接复制黏贴,并且在自己的工程里使用。版本为Swift的,不使用Objective C是因为觉得写起来比较浪费时间,而且如果遇到了需要和底层数据打交到的地方,我依然可以用Objective C做封装就好了。

    首先是这个BluetoothLowEnergy类的实现。

    //
    //  BluetoothLowEnergyHandler.swift
    //  PetoiSerialSwift
    //
    //  Created by Orlando Chen on 2021/3/23.
    //
    
    import Foundation
    import CoreBluetooth
    
    
    class BluetoothLowEnergy: NSObject {
        
        // 一个蓝牙设备当中可能包含多个信道,一个UUID就是一个信道标记
        var uuids: [CBCharacteristic] = []
        
        // 中心对象
        var central : CBCentralManager!
        
        // 把中心设备扫描的外置设备保存起来
        var deviceList: [CBPeripheral] = []
    
        // 接收到的数据
        var peripheralData: Data?
        
        
        // MARK: 0. 初始化
        override init() {
            super.init()
            
            // 初始化中心设备管理器
            // 它的delegate函数为centralManagerDidUpdateState
            // 其回调消息处理函数为:centralManager
            self.central = CBCentralManager.init(delegate:self, queue:nil, options:[CBCentralManagerOptionShowPowerAlertKey:false])
            
            // 初始化设备列表
            self.deviceList = []
        }
    
        // MARK: 1. 扫描设备
        func startScanPeripheral(serviceUUIDS: [CBUUID]?,
                                 options: [String: AnyObject]?) {
            // 清空列表
            deviceList = []  // 清空设备列表
            uuids = [] // 清空信道列表
            
            // 开始进行扫描
            self.central?.scanForPeripherals(withServices: serviceUUIDS, options: options)
        }
         
        // MARK: 2. 停止扫描
        func stopScanPeripheral() {
            self.central?.stopScan()
        }
        
        // MARK: 3.1. 获取搜索到的外接设备
        func getPeripheralList()-> [CBPeripheral] {
            return deviceList
        }
        
        // MARK: 3.2. 获取到当前蓝牙设备可用的消息信道
        func getCharacteristic() -> [CBCharacteristic] {
            return uuids
        }
        
        // MARK: 3.3. 指定监听信道
        func setNotifyCharacteristic(peripheral: CBPeripheral, notify: CBCharacteristic) {
            peripheral.setNotifyValue(true, for: notify)
        }
        
        // MARK: 4.1. 连结设备
        // 连接设备之前要先设置代理,正常情况,当第一次获取外设peripheral的时候就会同时设置代理
        func connect(peripheral: CBPeripheral) {
            if (peripheral.state != CBPeripheralState.connected) {
                central?.connect(peripheral , options: nil)
                
                // 将外接设备的回掉函数连结到self
                // 回掉消息处理函数为:peripheral
                peripheral.delegate = self
            }
        }
        
        // MARK: 4.2. 检测是否建立了连结
        func isConnected(peripheral: CBPeripheral) -> Bool {
            return peripheral.state == CBPeripheralState.connected
        }
        
        // MARK: 5.1. 发送数据
        func sendData(data: Data, peripheral: CBPeripheral, characteristic: CBCharacteristic,
                      type: CBCharacteristicWriteType = CBCharacteristicWriteType.withResponse) {
            
            let step = 20
            for index in stride(from: 0, to: data.count, by: step) {
                var len = data.count - index
                if len > step {
                    len = step
                }
                let pData: Data = (data as NSData).subdata(with: NSRange(location: index, length: len))
                peripheral.writeValue(pData, for: characteristic, type: type)
            }
        }
    
        // MARK: 5.2. 接收数据
        func recvData() -> Data {
            return peripheralData ?? Data([0x00])
        }
        
        // MARK: 6. 断开连结
        func disconnect(peripheral: CBPeripheral) {
            central?.cancelPeripheralConnection(peripheral)
        }
    }
    
    extension BluetoothLowEnergy: CBCentralManagerDelegate {
        
        // MARK: 检查运行这个App的设备是不是支持BLE。
        func centralManagerDidUpdateState(_ central: CBCentralManager) {
         
            switch central.state {
            case .poweredOn:
                NSLog("BLE poweredOn")
            case .poweredOff:
                NSLog("BLE powered off")
            case .unknown:
                NSLog("BLE unknown")
            case .resetting:
                NSLog("BLE ressetting")
            case .unsupported:
                NSLog("BLE unsupported")
            case .unauthorized:
                NSLog("BLE unauthorized")
            @unknown default:
                NSLog("BLE default")
            }
        }
        
        // MARK: 以ANCS协议请求的端,授权状态发生改变
        func centralManager(_ central: CBCentralManager, didUpdateANCSAuthorizationFor peripheral: CBPeripheral) {
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
            
            // TODO
        }
        
        // MARK: 状态的保存或者恢复
        func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(dict)")
            
            // TODO
        }
        
        // MARK:
        func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n  peripheral:\(peripheral)")
            
            // TODO
        }
        
        // 开始扫描之后会扫描到蓝牙设备,扫描到之后走到这个代理方法
        // MARK: 中心管理器扫描到了设备
        func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
            
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
            
            guard !deviceList.contains(peripheral), let deviceName = peripheral.name, deviceName.count > 0 else {
                return
            }
            
            // 把设备加入到列表中
            deviceList.append(peripheral)
            
            // TODO
        }
           
        // MARK: 连接外设成功,开始发现服务
        func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(peripheral)")
            
             // 设置代理
             peripheral.delegate = self
             
             // 开始发现服务
             peripheral.discoverServices(nil)
        }
    
           
        // MARK: 连接外设失败
        func centralManager(_ central: CBCentralManager, didFailToConnect peripheral:
                                CBPeripheral, error: Error?) {
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
            
            // TODO
        }
           
        // MARK: 连接丢失
        func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
            
    //        NSLog("\(#file) \(#line) \(#function)\n central:\(central)\n peripheral:\(String(describing: peripheral.name))\n  error:\(String(describing: error))")
            
           // TODO
        }
    }
    
    
    // MARK: 外置设备被绑定后的事件响应
    extension BluetoothLowEnergy: CBPeripheralDelegate {
        
        // MARK: 匹配对应服务UUID
        func peripheral(_ peripheral: CBPeripheral,
                        didDiscoverServices error: Error?) {
            
            if error != nil { // failed
    //            NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n error:\(String(describing: error))")
                return
            }
    
    //        NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))")
            
            
            for service in peripheral.services ?? [] {
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
        
        
        // MARK: 服务下的特征
        func peripheral(_ peripheral: CBPeripheral,
                        didDiscoverCharacteristicsFor service:
                            CBService, error: Error?) {
            
            if error != nil { // failed
    //            NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n service:\(String(describing: service))\n error:\(String(describing: error))")
                return
            }
            
    //        NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n service:\(String(describing: service))")
              
            for characteristic in service.characteristics ?? [] {
                uuids.append(characteristic)
            }
        }
    
        
        // MARK: 获取外设发来的数据
        // 注意,所有的,不管是 read , notify 的特征的值都是在这里读取
        func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
            
            if error != nil {
    //            NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))\n error:\(String(describing: error))")
                return
            }
            
    //        NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))")
            
            if let data = characteristic.value {
                self.peripheralData = data
            }
        }
        
        //MARK: 检测中心向外设写数据是否成功
        func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
            if let error = error {
    //            NSLog("\(#file) \(#line) \(#function)\n peripheral:\(String(describing: peripheral.name))\n characteristic:\(String(describing: characteristic.description))\n error:\(String(describing: error))")
            }
            
            // TODO
        }
    }
    
    

    所有的NSlog都是可以根据需要打开,方便调试使用的。TODO部分是你可以在上面进一步扩展的地方,当然修改原代码也是可以的。

    至于如何使用?

    首先,在iOS工程需要先在plist增加

    Privacy - Bluetooth Always Usage Description
    

    然后在某个类里实例化这个BLE类,如果是全局都需要进行蓝牙数据传输,那么就把这个类实例在AppDelegate文件里,并且设置为静态,这样就可以全局使用了。

    用户需要具体调用的几个函数如下:

    // MARK: 1. 扫描设备
    func startScanPeripheral(serviceUUIDS: [CBUUID]?, options: [String: AnyObject]?) 
         
    // MARK: 2. 停止扫描
    func stopScanPeripheral()
        
    // MARK: 3. 获取搜索到的外接设备
    func getPeripheralList()-> [CBPeripheral] 
        
    // MARK: 4.1. 连结设备
    func connect(peripheral: CBPeripheral)
        
    // MARK: 4.2. 检测是否建立了连结
    func isConnected(peripheral: CBPeripheral) -> Bool
    
    // MARK: 4.3. 获取到当前蓝牙设备可用的消息信道
    func getCharacteristic() -> [CBCharacteristic]
    
    // MARK: 4.4. 指定监听信道
    func setNotifyCharacteristic(peripheral: CBPeripheral, notify: CBCharacteristic)
        
    // MARK: 5.1. 发送数据
    func sendData(data: Data, peripheral: CBPeripheral, characteristic: CBCharacteristic, type: CBCharacteristicWriteType = CBCharacteristicWriteType.withResponse) 
    
    // MARK: 5.2. 接收数据
    func recvData() -> Data 
        
    // MARK: 6. 断开连结
    func disconnect(peripheral: CBPeripheral)
    

    由于蓝牙设备的通信过程是一个异步过程,等价于创建了一个后台线程对数据服务进行监听,所以如果对于音频流这一类实时会接受大量数据的应用,你可能需要增加一个标记或者其他方式以便处理,具体处理函数为

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
    

    只不对对于文本数据,这个代码已经足够使用了,比较简单的一种处理方法,就是用个定时器,每隔几十毫秒查询一次数据;而比较实时的处理方法,就是做一个回调函数,不过这些都由你自己决定怎么做吧,好运!

    展开全文
  • 用PDA控制下位机(基于单片机、DSP的仪器)操作,用蓝牙接口通信,PDA为主模式,下位机为从模式,串口收发,帧格式定义。
  • 基于蓝牙通信协议

    2014-08-12 17:22:38
    基于android的蓝牙通信协议,很好用的
  • 蓝牙BLE协议中文版.pdf

    2020-06-16 13:54:08
    蓝牙BLE协议中文版,帮助初学者更快掌握协议内容。协议为SIG官方协议的中文版,所有协议内容均与英文原版协议内容保持一致
  • 在一次聊天中,突发奇想,解决了一个蓝牙通讯的协议问题。如果有需要制定硬件和App交互协议, 同样可以参考本文。 问题概述: 1:多套协议,多套解析 蓝牙通讯时都会定义自己和硬件的通讯协议,不同的硬件,协议...

    在一次聊天中,突发奇想,解决了一个蓝牙通讯的协议问题。如果有需要制定硬件和App交互协议,
    同样可以参考本文。

    问题概述:


    1:多套协议,多套解析
    蓝牙通讯时都会定义自己和硬件的通讯协议,不同的硬件,协议也不一样,对应APP解析也不一样
    2:数据改变,协议改变
    协议一般是不会变的,但是会变的情况还是很多的,例如需求增加,硬件改动,都会造成数据变更,
    进而导致协议变更,导致硬件组帧改动,导致手机端软件解析代码变更。

    举个例子:
    原来的老协议:

    由于数据太多需要一个字节:

    需要解决的问题:


    1:数据改变,协议不变,解析不变
    2:使用一套协议兼容所有的协议,使用一套解析兼容所有的解析

    解决方法:


    1:增加数据域的类型个数可变字段
    2:增加数据域个数的字段
    3:增加数据域中每个类型长度的字段
    4:增加数据域中每个类型对应解析字段

    数据域的类型个数:
    假设原来的数据域为 3D2A5511226F81
    我要解析出“      用量   电压  日期   信号”四个字段
    那 ‘数据域的类型个数’ 就是 04(16进制表示)

    每个类型长度字段:
    用量:需要截取 2个字节
    电压:需要截取 1个字节
    日期:需要截取 2个字节
    信号:需要截取 2个字节

    每个类型对应解析:
    我自己定义的解析规则如下
    01    16进制转10进制        
    02    BCD码        
    03    反转的BCD码        
    04    16进制转10进制保留1位小数        
    05    16进制转10进制保留2位小数        
    06    16进制转10进制保留3位小数
    这个可以根据实际需求定制

    如果使用新协议再组织老的数据帧应该是这样的

    回到最开始那个问题,数据发生变化后,数据帧应该是这样的

    那我的解析规则不用变化了,因为我解析是根据
    数据域的类型个数,每个类型长度字段,每个类型对应解析这三个
    对应的数据去解析的,现在只是类型长度变了,我在截取数据也相应多
    截取一个就行了,代码完全不用变!

    展开全文
  • 蓝牙蓝牙协议

    万次阅读 多人点赞 2019-03-14 09:46:25
    蓝牙协议学习整理(一)蓝牙的概述 转自: https://blog.csdn.net/guoxiaolongonly/article/details/78414870 传送门:(一)蓝牙的概述(二)蓝牙协议规范(射频、基带链路控制、链路管理)(三)蓝牙协议规范...

    蓝牙协议学习整理(一)蓝牙的概述

     

    转自: https://blog.csdn.net/guoxiaolongonly/article/details/78414870

    传送门: 
    (一)蓝牙的概述 
    (二)蓝牙协议规范(射频、基带链路控制、链路管理) 
    (三)蓝牙协议规范(HCI、L2CAP、SDP、RFOCMM) 
    (四)蓝牙协议规范(irOBEX、BNEP、AVDTP、AVCTP)

    引用自xubin的博客

     

    第一章 蓝牙的概述

    一、蓝牙版本信息

    蓝牙共有六个版本1.1/1.2/2.0/2.1/3.0/4.0

    1、1.1版本

    传输率约在748~810kb/s,因是早期设计,容易受到同频率之产品所干扰下影响通讯质量。

    2、1.2版本

    同样是只有748~810kb/s 的传输率,但在加上了(改善 Software)抗干扰跳频功能。

    3、2.0+EDR版本

    是1.2的改良提升版,传输率约在1.8M/s~2.1M/s,开始支持双工模式——即一面作语音通讯,同时亦可以传输档案/高质素图片,2.0 版本当然也支持 Stereo 运作。

    应用最为广泛的是Bluetooth2.0+EDR标准,该标准在2004年已经推出,支持Bluetooth 2.0+EDR标准的产品也于2006年大量出现。虽然Bluetooth 2.0+EDR标准在技术上作了大量的改进,但从1.X标准延续下来的配置流程复杂和设备功耗较大的问题依然存在。

    4、2.1版本

    更佳的省电效果:蓝牙2.1版加入了SniffSubrating的功能,透过设定在2个装置之间互相确认讯号的发送间隔来达到节省功耗的目的。

    5、3.0+HS版本

    2009年4月21日,蓝牙技术联盟(Bluetooth SIG)正式颁布了新一代标准规范”Bluetooth Core Specification Version 3.0 High Speed”(蓝牙核心规范3.0版 ),蓝牙3.0的核心是”GenericAlternate MAC/PHY”(AMP),这是一种全新的交替射频技术,允许蓝牙协议栈针对任一任务动态地选择正确射频。最初被期望用于新规范的技术包括802.11以及UMB,但是新规范中取消了UMB的应用。

    6、4.0 版本

    蓝牙4.0包括三个子规范,即传统蓝牙技术、高速蓝牙和新的蓝牙低功耗技术。蓝牙 4.0的改进之处主要体现在三个方面,电池续航时间、节能和设备种类上。拥有低成本,跨厂商互操作性,3毫秒低延迟、100米以上超长距离、AES-128加密等诸多特色此外,蓝牙4.0的有效传输距离也有所提升。3.0版本的蓝牙的有效传输距离为10米(约 32英尺),而蓝牙4.0的有效传输距离最高可达到100米(约328英尺)。

    7、典型蓝牙与BLE蓝牙对比

    蓝牙与BLE对比

    二、蓝牙的技术特点

    简单地说,蓝牙是一种短程宽带无线电技术,是实现语音和数据无线传输的全球开放性标准。它使用跳频扩谱(FHSS)、时分多址(TDMA)、码分多址(CDMA)等先进技术,在小范围内建立多种通信与信息系统之间的信息传输。

    1、Bluetooth的主要技术特点:

    (1)、工作频段 :2.4GHz的工科医(ISM)频段,无需申请许可证。大多数国家使用79个频点,载频为(2402+k)MHz(k=0,1, 2…78),载频间隔1MHz。采用TDD时分双工方式。 
    (2)、传输速率:1Mb/s(V2.0以上版本) 
    (3)、调试方式:BT=0.5的GFSK调制,调制指数为0.28-0.35。 
    (4)、采用跳频技术:跳频速率为1600跳/秒,在建链时(包括寻呼和查询)提高为3200跳/秒。蓝牙通过快跳频和短分组技术减少同频干扰,保证传输的可靠性。 
    (5)、语音调制方式:连续可变斜率增量调制(CVSD,ContinuousVariable Slope Delta Modulation),抗衰落性强,即使误码率达到4%,话音质量也可接受。 
    (6)、支持电路交换和分组交换业务:蓝牙支持实时的同步定向联接(SCO链路)和非实时的异步不定向联接(ACL链路),前者主要传送语音等实时性强的信息,后者以数据包为主。语音和数据可以单独或同时传输。蓝牙支持一个异步数据通道,或三个并发的同步话音通道,或同时传送异步数据和同步话音的通道。每个话音通道支持64kbps的同步话音;异步通道支持723.2/57.6kbps的非对称双工通信或433.9kbps的对称全双工通信。 
    (7)、支持点对点及点对多点通信:蓝牙设备按特定方式可组成两种网络:微微网(Piconet)和分布式网络(Scatternet),其中微微网的建立由两台设备的连接开始,最多可由八台设备组成。在一个微微网中,只有一台为主设备(Master),其它均为从设备(Slave),不同的主从设备对可以采用不同的链接方式,在一次通信中,链接方式也可以任意改变。几个相互独立的微微网以特定方式链接在一起便构成了分布式网络。所有的蓝牙设备都是对等的,所以在蓝牙中没有基站的概念。 
    (8)、工作距离:蓝牙设备分为三个功率等级,分别是:100mW(20dBm)、2.5mW(4dBm)和1mW(0dBm),相应的有效工作范围为:100米、10米和1米。

    三、Bluetooth的系统构成

    构成

    1、无线射频单元(Radio):

    负责数据和语音的发送和接收,特点是短距离、低功耗。蓝牙天线一般体积小、重量轻,属于微带天线。

    2、基带或链路控制单元(LinkController):

    进行射频信号与数字或语音信号的相互转化,实现基带协议和其它的底层连接规程。

    3、链路管理单元(LinkManager):

    负责管理蓝牙设备之间的通信,实现链路的建立、验证、链路配置等操作。

    4、蓝牙软件协议实现:

    如上图紫色部分,这个后面我们做详细说明。 
    这里写图片描述

    四、蓝牙协议规范

    传输协议、中介协议、应用协议;

    1、传输协议

    负责蓝牙设备间,互相确认对方的位置,以及建立和管理蓝牙设备间的物理链路;

    • 底层传输协议:蓝牙射频(Radio)部分、基带链路管理控制器(Baseband&Link Controller)、链路管理协议(Link ManagerProtocol LMP)。负责语言、数据无线传输的物理实现以及蓝牙设备间的联网组网。

    • 高层传输协议:逻辑链路控制与适配器(LogicalLink Control and Adaptation Protocol)L2CAP 、主机控制接口(HostControl Interface,HCI)。为高层应用屏蔽了跳频序列选择等底层传输操作,为高层程序提供有效、有利于实现数据分组格式。

    2、中介协议

    为高层应用协议或者程序,在蓝牙逻辑链路上工作提供必要的支持,为应用提供不同标准接口。

    • 串口仿真协议:RFCOMM、服务发现协议:SDP、互操作协议IrDA、网络访问协议:PPP、IP、TCP、UDP、电话控制协议:TCS、AT指令集。

    这里写图片描述

    3、应用协议

    蓝牙协议栈之上的应用软件和所涉及到的协议,如:拨号上网、语言功能的应用程序。

    蓝牙的应用框架如下:

    1. 通用应用类框架:查询、建立连接服务等;
    2. 蓝牙电话应用类框架:电话控制、语言;
    3. 蓝牙连网应用类框架:网络应用相关;
    4. 对象交互服务类框架:IrDA、OBEX;
    5. 蓝牙音视频控制类框架。

    五、硬件接口

    一般蓝牙芯片通过UART、USB、SDIO、I2S、PcCard和主控芯片通信。如下图所示,通过UART和主控芯片通信。 
    这里写图片描述

     

    第二章 蓝牙协议规范(射频、基带链路控制、链路管理)


    蓝牙协议是蓝牙设备间交换信息所应该遵守的规则。与开放系统互联(OSI)模型一样,蓝牙技术的协议体系也采用了分层结构,从底层到高层形成了蓝牙协议栈,各层协议定义了所完成的功能和使用数据分组格式,以保证蓝牙产品间的互操作性。

    一、射频协议
    射频位置如上图红色部分。

    1、工作频率
    蓝牙工作在2.4GHz ISM频段上,蓝牙采用跳频扩谱技术主动的避免工作频段受干扰(微波炉的工作频率也是2.4GHz)。 


    地理位置    ISM频段范围    射频信道频率
    中国、美国、欧洲    2400.0~2483.5MHz    F=(2402+k)MHz,k在0、1、……78中随机取值
    法国    2446.5~2483.5MHz    F=(2454+k)MHz,k在0、1、……22中随机取值
    日本    2471.0~2497.0MHz    F=(2473+k)MHz,k在0、1、……22中随机取值
    西班牙    2445.0~2475.0MHz    F=(2449+k)MHz,k在0、1、……22中随机取值
    我国的蓝牙频率在2.402GHz~2.483GHz,蓝牙每个频道的宽度为1MHz,为了减少带外辐射的干扰,保留上、下保护为3.5MHz和2MHz,79个跳频点中至少75个伪随机码跳动,30S内任何一个频点使用时长不能超过0.4S。

    2、跳频技术、发射功率、时隙
    (1)、发射功率:蓝牙发射功率分三级:一级功率100mW(20dBm);二级功率2.5mW(4dBm);三级功率1mW(0dBm); 
    (2)、物理信道:蓝牙物理信道有伪随机序列控制的79个跳频点构成,不同跳频序列代表不同的信道。 


    (3)、时隙:蓝牙跳频速率为1600次/s,每个时间为625uS(1S/1600)称为一个时隙; 


    二、基带与链路控制协议


    蓝牙发送数据时,基带部分将来自高层的数据进行信道编码,向下发给射频进行发送;接收数据时,将解调恢复空中数据并上传给基带,基带进行信道编码传送给上层。

    作用:跳频选择、蓝牙编址、链路类型、信道编码、收发规则、信道控制、音频规范、安全设置。

    1、蓝牙分组编码为小端模式;
    2、蓝牙地址
    **BD_ADDR:**BluetoothDevice Address;
    **LAP:**LowerAddress Part 低地址部分;
    UAP: UpperAddress Part 高地址部分;
    NAP: Non-significantAddress Part 无效地址部分。


    3、蓝牙时钟

    每个蓝牙设备都有一个独立运行的内部系统时钟,称为本地时钟(Local Clock),决定定时器的收发跳频。为了与其他设备同步,本地时钟要加一个偏移量(offset),提供给其他设备同步。

    蓝牙基带四个关键周期:312.5uS、625uS、1.25mS、1.28S。

    CLKN:本地时钟:
    CLKE:预计时钟,扫描寻呼过程中用到;
    CLK:设备实际运行的时钟频率。

    CLKE、CLK由CLKN加上一个偏移量得到的。

    4、蓝牙物理链路:
    通信设备间物理层的数据连接通道就是物理链路。

    ACL(Asynchronous Connectionless)异步无连接链路;对时间要求不敏感的数据通信,如文件数据、控制信令等。

    SCO(Synochronous Connection Oriented)同步面向连接链路;对时间比较敏感的通信,如:语音;最多只支持3条SCO链路,不支持重传。

    ACL用于数据传输;

    5、蓝牙基带分组:
    基带分组至少包括:接入码、分组头、有效载荷;
    1


    (1)、接入码用于同步、直流、载频泄漏偏置补偿标识; 
    (2)、分组头包含链路信息,确保纠正较多的错误。 
    分组类型如下:

    分组类别    Type(b3b2b1b0)    时隙    SCO    ACL
    链路控制分组    0000    1    NULL    NULL
    0001    POLL    POLL
    0010    FHS    FHS
    0011    DM1    DM1
    单时隙分组    0100    1    未定义    NULL
    0101    HV1
    0110    HV2
    0111    HV3
    1000    DV
    1001    NULL    AUX1
    3时隙分组    1010    3    未定义    DM3
    1011    DH3
    1100    未定义
    1101
    5时隙分组    1010    5    未定义    DM5
    1111
    ACL分组形式为:D(M|H)(1|3|5),D代表数据分组,M代表用2/3比例的FEC的中等速率分组;H代表不使用纠错码的高速率分组;1、3、5分别代表分组所占用的时隙数目; 
    DM1、DM3、DM5、DH1、DH3、DH5

    SCO分组形式为:HV(1|2|3)。HV代表高质量语言分组,1、2、3有效载荷所采用的纠错码方法。1为1/3比例FEC,设备2个时隙发送一个单时隙分组;2为2/3比例FEC,设备4个时隙发送一个单时隙分组;3为不使用纠错码,设备6个时隙发送一个单时隙分组

    HV1、HV2、HV3

    ALC 分组:

    类型    有效载荷头/字节    用户有效载荷/字节    FEC    CRC    对称最大速率/kbps    非对称速率/kbps
    前向    后向
    DM1    1    0~17    2/3    有    108.8    108.8    108.8
    DH1    1    0~27    无    有    172.8    172.8    172.8
    DM3    2    0~121    2/3    有    258.1    387.2    54.4
    DH3    2    0~183    无    有    390.4    585.6    86.4
    DM5    2    0~224    2/3    有    286.7    477.8    36.3
    MH5    2    0~339    无    有    433.9    723.2    57.6
    AUX1    1    0~29    无    无    185.6    185.6    185.6
    SCO分组:

    类型    有效载荷头/字节    用户有效载荷/字节    FEC    CRC    有效载荷长度    同步速率/kbps    占用Tsco数目/语言长度
    HV1    无    10    1/3        240位    64    2/1.25ms
    HV2    20    2/3    4/2.5ms
    HV3    30    无    6/3.75ms
    DV    1D    10+(0-9)D    2/3D    有D    64+57.6D    
    注释:D 只对数据段有用,DV分组包含数据段,也包含语言段。
    1
    (3)、有效载荷 分语言有效载荷、数据有效载荷。

    6、蓝牙的逻辑信道
    链路控制信道:LinkControl LC
    链路管理信道:Link Manage LM
    用户异步数据信道:User AsynchronizationUA
    用户同步数据信道:UserSynchronization US
    用户等时数据信道:UserIsochronous UI UI
    7、蓝牙的收发规则
     
    上图为RX缓存。 
     
    上图为TX缓存。 
    新分组到达时,ACL链路的RX缓存器要流量控制,SCO数据不需要流量控制;

    8、蓝牙基带信道和网络控制
    (1)、链路控制器状态: 
    待机、连接 
    寻呼page、寻呼扫描pagescan、查询inquiry、查询扫描inquiry scan、主设备相应Master Response、从设备相应Slave Response、查询相应inquiry response 
    (2) 、连接状态 
    激活模式active、呼吸模式sniff、保持模式hold、休眠模式park。 
    (3)、待机状态 
    待机状态是蓝牙设备缺省低功耗状态,此状态下本地时钟以低精度运行。蓝牙从待机转入寻呼扫描状态,对其他寻呼进行响应成为从设备;也可以从待机状态进入查询扫描状态,完成一个完整的寻呼,成为主设备。

    9、接入过程
    注释: 
    IAC Inquiry AccessCode 查询接入码; 
    GIAC:通用查询接入码 DIAC:专用查询接入码; 
    DAC:DeviceAccess Code 设备接入码; 
    LAP: 
    建立连接,必须使用查询、寻呼;查询过程使用IAC,发现覆盖区域内的设备、设备的地址及其时钟;连接过程使用DAC,建立连接的设备处理寻呼过程,成为主设备。

    (1)、查询过程 
    蓝牙设备通过查询来发现通信范围内的其他蓝牙设备。查询信息分为GIAC、DIAC两种。查询发起设备收集所有相应设备的地址、时钟信息。 
    一设备进入查询状态去发现其他设备,查询状态下连续不断的在不同频点发送查询消息。查询的跳频序列有GIAC的LAP导出。 
    一设备想被其他设备发现,就要周期性进入 查询扫描状态,以便相应查询消息。如:我们选择设备多长时间可见,其实就是 进入查询扫描状态。 
    A、查询扫描 
    查询扫描状态下,接收设备扫描接入码的时间长度,足以完成对16个频率的扫描。扫描区间长度Twindow inquiry scan。扫描在同一个频率上进行,查询过程用32跳专用查询跳频序列,此序列有通用查询的地址决定,相位有本地时钟决定,每隔1.28S变化一次。 
    B、查询 
    与寻呼类似,TX用查询跳频序列、RX用查询相应跳频序列。 
    C、查询相应 
    从设备响应查询操作。每个设备都有自己的时钟,使用查询序列相位相同的几率比较小。为了避免多个设备在同一查询跳频信道同时激活,从设备查询响应规定:从设备收到查询消息,产生0-1023只觉得额一个随机数,锁定当时相位输入值进行跳频选择,从设备此后的RAND时隙中返回到连接或者待机状态。

    (2)、寻呼扫描 
    DAC:DeviceAccess Code 设备接入码 
    寻呼扫描状态下的设备扫描窗口Twindowpage scan内监听自己的DAC。监听只在一个跳频点进行。Twindow page scan足够覆盖16个寻呼扫描频点。 
    寻呼扫描状态,扫描在同一个频率上进行,持续1.28S,在选择另一个不同频率。

    SR模式    Tpage scan    寻呼次数Npage
    R0    连续    >=1
    R1    <=1.28S    >=128
    R2    <=2.56S    >=256
    预留    –    –
    (3)、寻呼 
    主设备使用寻呼发起一个主—从设备连接,通过在不同的跳频点上重复发送从设备DAC来扑捉从设备,从设备在寻呼扫描状态被唤醒,接收寻呼。 
    (4)、寻呼相应过程

    三、链路管理器
     
    如上图红色部分,负责完成设备:功率管理、链路质量管理、链路控制管理、数据分组管理、链路安全管理。

    1、链路管理协议数据单元
    蓝牙链路管理器接收到高层的控制信息后,不是向自身的基带部分分发控制信息,就是与另一台设备的链路管理器进行协商管理。这些控制信息封装在链路管理协议数据单元LMP_PDU中,由ACL分组的有效载荷携带。

    2、链路管理器协议规范
    (1)、设备功率管理 
    RSSI保持模式、呼吸模式、休眠模式。 
    (2)、链路质量管理 QoSQuality of Service 
    A、ACL链路。 
    B、SCO链路。 
    (3)、链路控制管理 
    设备寻呼模式、设备角色转换、时钟计时设置、信息交换:版本信息、支持特性、设备名称;建立连接、链路释放。 
    (4)、数据分组管理

    第二章 蓝牙协议规范(射频、基带链路控制、链路管理)


    蓝牙协议是蓝牙设备间交换信息所应该遵守的规则。与开放系统互联(OSI)模型一样,蓝牙技术的协议体系也采用了分层结构,从底层到高层形成了蓝牙协议栈,各层协议定义了所完成的功能和使用数据分组格式,以保证蓝牙产品间的互操作性。

    一、射频协议
    射频位置如上图红色部分。

    1、工作频率
    蓝牙工作在2.4GHz ISM频段上,蓝牙采用跳频扩谱技术主动的避免工作频段受干扰(微波炉的工作频率也是2.4GHz)。 


    地理位置    ISM频段范围    射频信道频率
    中国、美国、欧洲    2400.0~2483.5MHz    F=(2402+k)MHz,k在0、1、……78中随机取值
    法国    2446.5~2483.5MHz    F=(2454+k)MHz,k在0、1、……22中随机取值
    日本    2471.0~2497.0MHz    F=(2473+k)MHz,k在0、1、……22中随机取值
    西班牙    2445.0~2475.0MHz    F=(2449+k)MHz,k在0、1、……22中随机取值
    我国的蓝牙频率在2.402GHz~2.483GHz,蓝牙每个频道的宽度为1MHz,为了减少带外辐射的干扰,保留上、下保护为3.5MHz和2MHz,79个跳频点中至少75个伪随机码跳动,30S内任何一个频点使用时长不能超过0.4S。

    2、跳频技术、发射功率、时隙
    (1)、发射功率:蓝牙发射功率分三级:一级功率100mW(20dBm);二级功率2.5mW(4dBm);三级功率1mW(0dBm); 
    (2)、物理信道:蓝牙物理信道有伪随机序列控制的79个跳频点构成,不同跳频序列代表不同的信道。 


    (3)、时隙:蓝牙跳频速率为1600次/s,每个时间为625uS(1S/1600)称为一个时隙; 


    二、基带与链路控制协议


    蓝牙发送数据时,基带部分将来自高层的数据进行信道编码,向下发给射频进行发送;接收数据时,将解调恢复空中数据并上传给基带,基带进行信道编码传送给上层。

    作用:跳频选择、蓝牙编址、链路类型、信道编码、收发规则、信道控制、音频规范、安全设置。

    1、蓝牙分组编码为小端模式;
    2、蓝牙地址
    **BD_ADDR:**BluetoothDevice Address;
    **LAP:**LowerAddress Part 低地址部分;
    UAP: UpperAddress Part 高地址部分;
    NAP: Non-significantAddress Part 无效地址部分。


    3、蓝牙时钟

    每个蓝牙设备都有一个独立运行的内部系统时钟,称为本地时钟(Local Clock),决定定时器的收发跳频。为了与其他设备同步,本地时钟要加一个偏移量(offset),提供给其他设备同步。

    蓝牙基带四个关键周期:312.5uS、625uS、1.25mS、1.28S。

    CLKN:本地时钟:
    CLKE:预计时钟,扫描寻呼过程中用到;
    CLK:设备实际运行的时钟频率。

    CLKE、CLK由CLKN加上一个偏移量得到的。

    4、蓝牙物理链路:
    通信设备间物理层的数据连接通道就是物理链路。

    ACL(Asynchronous Connectionless)异步无连接链路;对时间要求不敏感的数据通信,如文件数据、控制信令等。

    SCO(Synochronous Connection Oriented)同步面向连接链路;对时间比较敏感的通信,如:语音;最多只支持3条SCO链路,不支持重传。

    ACL用于数据传输;

    5、蓝牙基带分组:
    基带分组至少包括:接入码、分组头、有效载荷;
    1


    (1)、接入码用于同步、直流、载频泄漏偏置补偿标识; 
    (2)、分组头包含链路信息,确保纠正较多的错误。 
    分组类型如下:

    分组类别    Type(b3b2b1b0)    时隙    SCO    ACL
    链路控制分组    0000    1    NULL    NULL
    0001    POLL    POLL
    0010    FHS    FHS
    0011    DM1    DM1
    单时隙分组    0100    1    未定义    NULL
    0101    HV1
    0110    HV2
    0111    HV3
    1000    DV
    1001    NULL    AUX1
    3时隙分组    1010    3    未定义    DM3
    1011    DH3
    1100    未定义
    1101
    5时隙分组    1010    5    未定义    DM5
    1111
    ACL分组形式为:D(M|H)(1|3|5),D代表数据分组,M代表用2/3比例的FEC的中等速率分组;H代表不使用纠错码的高速率分组;1、3、5分别代表分组所占用的时隙数目; 
    DM1、DM3、DM5、DH1、DH3、DH5

    SCO分组形式为:HV(1|2|3)。HV代表高质量语言分组,1、2、3有效载荷所采用的纠错码方法。1为1/3比例FEC,设备2个时隙发送一个单时隙分组;2为2/3比例FEC,设备4个时隙发送一个单时隙分组;3为不使用纠错码,设备6个时隙发送一个单时隙分组

    HV1、HV2、HV3

    ALC 分组:

    类型    有效载荷头/字节    用户有效载荷/字节    FEC    CRC    对称最大速率/kbps    非对称速率/kbps
    前向    后向
    DM1    1    0~17    2/3    有    108.8    108.8    108.8
    DH1    1    0~27    无    有    172.8    172.8    172.8
    DM3    2    0~121    2/3    有    258.1    387.2    54.4
    DH3    2    0~183    无    有    390.4    585.6    86.4
    DM5    2    0~224    2/3    有    286.7    477.8    36.3
    MH5    2    0~339    无    有    433.9    723.2    57.6
    AUX1    1    0~29    无    无    185.6    185.6    185.6
    SCO分组:

    类型    有效载荷头/字节    用户有效载荷/字节    FEC    CRC    有效载荷长度    同步速率/kbps    占用Tsco数目/语言长度
    HV1    无    10    1/3        240位    64    2/1.25ms
    HV2    20    2/3    4/2.5ms
    HV3    30    无    6/3.75ms
    DV    1D    10+(0-9)D    2/3D    有D    64+57.6D    
    注释:D 只对数据段有用,DV分组包含数据段,也包含语言段。
    1
    (3)、有效载荷 分语言有效载荷、数据有效载荷。

    6、蓝牙的逻辑信道
    链路控制信道:LinkControl LC
    链路管理信道:Link Manage LM
    用户异步数据信道:User AsynchronizationUA
    用户同步数据信道:UserSynchronization US
    用户等时数据信道:UserIsochronous UI UI
    7、蓝牙的收发规则
     
    上图为RX缓存。 
     
    上图为TX缓存。 
    新分组到达时,ACL链路的RX缓存器要流量控制,SCO数据不需要流量控制;

    8、蓝牙基带信道和网络控制
    (1)、链路控制器状态: 
    待机、连接 
    寻呼page、寻呼扫描pagescan、查询inquiry、查询扫描inquiry scan、主设备相应Master Response、从设备相应Slave Response、查询相应inquiry response 
    (2) 、连接状态 
    激活模式active、呼吸模式sniff、保持模式hold、休眠模式park。 
    (3)、待机状态 
    待机状态是蓝牙设备缺省低功耗状态,此状态下本地时钟以低精度运行。蓝牙从待机转入寻呼扫描状态,对其他寻呼进行响应成为从设备;也可以从待机状态进入查询扫描状态,完成一个完整的寻呼,成为主设备。

    9、接入过程
    注释: 
    IAC Inquiry AccessCode 查询接入码; 
    GIAC:通用查询接入码 DIAC:专用查询接入码; 
    DAC:DeviceAccess Code 设备接入码; 
    LAP: 
    建立连接,必须使用查询、寻呼;查询过程使用IAC,发现覆盖区域内的设备、设备的地址及其时钟;连接过程使用DAC,建立连接的设备处理寻呼过程,成为主设备。

    (1)、查询过程 
    蓝牙设备通过查询来发现通信范围内的其他蓝牙设备。查询信息分为GIAC、DIAC两种。查询发起设备收集所有相应设备的地址、时钟信息。 
    一设备进入查询状态去发现其他设备,查询状态下连续不断的在不同频点发送查询消息。查询的跳频序列有GIAC的LAP导出。 
    一设备想被其他设备发现,就要周期性进入 查询扫描状态,以便相应查询消息。如:我们选择设备多长时间可见,其实就是 进入查询扫描状态。 
    A、查询扫描 
    查询扫描状态下,接收设备扫描接入码的时间长度,足以完成对16个频率的扫描。扫描区间长度Twindow inquiry scan。扫描在同一个频率上进行,查询过程用32跳专用查询跳频序列,此序列有通用查询的地址决定,相位有本地时钟决定,每隔1.28S变化一次。 
    B、查询 
    与寻呼类似,TX用查询跳频序列、RX用查询相应跳频序列。 
    C、查询相应 
    从设备响应查询操作。每个设备都有自己的时钟,使用查询序列相位相同的几率比较小。为了避免多个设备在同一查询跳频信道同时激活,从设备查询响应规定:从设备收到查询消息,产生0-1023只觉得额一个随机数,锁定当时相位输入值进行跳频选择,从设备此后的RAND时隙中返回到连接或者待机状态。

    (2)、寻呼扫描 
    DAC:DeviceAccess Code 设备接入码 
    寻呼扫描状态下的设备扫描窗口Twindowpage scan内监听自己的DAC。监听只在一个跳频点进行。Twindow page scan足够覆盖16个寻呼扫描频点。 
    寻呼扫描状态,扫描在同一个频率上进行,持续1.28S,在选择另一个不同频率。

    SR模式    Tpage scan    寻呼次数Npage
    R0    连续    >=1
    R1    <=1.28S    >=128
    R2    <=2.56S    >=256
    预留    –    –
    (3)、寻呼 
    主设备使用寻呼发起一个主—从设备连接,通过在不同的跳频点上重复发送从设备DAC来扑捉从设备,从设备在寻呼扫描状态被唤醒,接收寻呼。 
    (4)、寻呼相应过程

    三、链路管理器
     
    如上图红色部分,负责完成设备:功率管理、链路质量管理、链路控制管理、数据分组管理、链路安全管理。

    1、链路管理协议数据单元
    蓝牙链路管理器接收到高层的控制信息后,不是向自身的基带部分分发控制信息,就是与另一台设备的链路管理器进行协商管理。这些控制信息封装在链路管理协议数据单元LMP_PDU中,由ACL分组的有效载荷携带。

    2、链路管理器协议规范
    (1)、设备功率管理 
    RSSI保持模式、呼吸模式、休眠模式。 
    (2)、链路质量管理 QoSQuality of Service 
    A、ACL链路。 
    B、SCO链路。 
    (3)、链路控制管理 
    设备寻呼模式、设备角色转换、时钟计时设置、信息交换:版本信息、支持特性、设备名称;建立连接、链路释放。 
    (4)、数据分组管理
    --------------------- 

    第三章 蓝牙协议规范(HCI、L2CAP、SDP、RFOCMM)
    一、主机控制接口协议 HCI
    蓝牙主机-主机控模型 


    蓝牙软件协议栈堆的数据传输过程: 


    1、蓝牙控制器接口数据分组:
    指令分组、事件分组、数据分组

    (1)、指令分组 


    如:Accpet Connection Request

    Opcode为:0x0409
    参数长度为: 07
    参数中蓝牙地址为:00:0d:fd:5f:16:9f
    角色为:从设备 0x01
    大端数据模式

    指令为:09 04 07 9f 16 5f fd 0d 00 01
    (2)、事件分组 


     
    如上图:

    Opcode :0x0409
    状态: 0x00
    总长度: 4字节
    命令状态:0x0f
    (3)、数据分组 
    ACL 数据分组

    连接句柄(12bit)    PB(2bit)    BC(2bit)    数据长度(16bit)
    数据…………
    注:PB  Packet_Boundary  BC Broadcast Flag
    1
    SCO 数据分组

    连接句柄(12bit)    PB(2bit)    BC(2bit)    数据长度(16bit)
    数据…………
    (4)、RS232分组指示器:

    HCI 分组类型    RS232分组指示器
    HCI指令分组    0x01
    HCI ACL数据分组    0x02
    HCI SCO数据分组    0x03
    HCI事件分组    0x04
    HCI错误消息分组    0x05
    HCI协商分组    0x06
    2、HCI控制命令
    (1)、链路控制指令

    命令    OCF    概述
    Inquiry    0x0001    蓝牙设备进入查询模式,搜索临近设备
    Inquiry Cancel    0x0002    退出查询模式
    Periodic Inquiry Mode    0x0003    蓝牙设备在指定周期内自动查询
    Exit Periodic Inquiry Mode    0x0004    退出自动查询模式
    Create Connection    0x0005    按指定蓝牙设备的BD_ADDR创建ACL链路
    Disconnect    0x0006    终止现有连接
    Add SCO Connection    0x0007    利用连接句柄参数指定的ACL连接创建SCO
    Cancel Create Connection    0x0008    
    Accept Connection Request    0x0009    接收新的呼入连接请求
    Reject Connection Request    0x000A    拒绝新的呼入连接请求
    Link Key Request Reply    0x000B    应答从主机控制器发出的链路密钥请求事件,并指定存储在主机上的链路密钥做为与BD_ADDR指定的蓝牙设备进行连接使用的链路密钥请求事件
    Link Key Request Negative Reply    0x000C    如果主机上没有存储链路密钥,作为与BD_ADDR指定的蓝牙设备进行连接使用的链路密钥,就应答从主机控制器发出的链路密钥请求事件
    PIN Code Request Reply    0x000D    应答从主机控制器发出的PIN请求事件,并指定用于连接的PIN
    PIN Code Request Negative Reply    0x000E    当主机不能指定连接的PIN时,应回答从机控制器发出的PIN请求事件
    Change Connection Packet Type    0x000F    改变正在建立连接的分组类型
    Authentication Request    0x0011    指定连接句柄关联的两个蓝牙设备之间建立身份鉴权
    Set Connection Encryption    0x0013    建立取消连接加密
    Change Connection Link Key    0x0015    强制关联了连接句柄的两个设备建立连接,并生成一个新的链路密钥
    Master Link Key    0x0017    强制关联了连接句柄的两个设备利用主设备时链路密钥或常规密钥
    Remote Name Request    0x0019    获取远端设备的名称
    Cancel Remote Name Request        
    Read Remote Supported Features    0x001B    请求远端设备所支持的特性列表
    Read Remote Extended Features        
    Read Remote Version Information    0x001D    从远端设备读取版本信息
    Read Clock Offset    0x001F    读取远端的时钟信息
    (2)、链路策略指令

    命令    OCF    简介
    Hold Mode    0x0001    改变LM状态和本地及远程设备为主模式的LM位置
    Sniff Mode    0x0003    改变LM状态和本地及远程设备为呼吸模式的LM位置
    Exit Sniff Mode    0x0004    结束连接句柄在当前呼吸模式里的呼吸模式
    Park State    0x0005    改变LM状态和本地及远程设备为休眠模式的LM位置
    Exit Park State    0x0006    切换从休眠模式返回到激活模式的蓝牙设备
    QoS Setup    0x0007    指出连接句柄的服务质量参数
    Role Discovery    0x0009    蓝牙设备连接后确定自己的主从角色
    Switch Role    0x000B    角色互换
    Read Link Policy Settings    0x000C    为指定连接句柄读链路策略设置。链路策略设置允许主机控制器指定用于连接句柄的LM连接模式
    Write Link Policy Settings    0x000D    为指定连接句柄写链路策略设置。链路策略设置允许主机控制器指定用于连接句柄的LM连接模式
    Read Default Link Policy Settings    0x000E    
    Write Default Link Policy Settings    0x000F    
    Flow Specification    0X0010    
    (3)、主机控制器与基带指令

    Set Event Mark    0x0001    使能主机过滤HCI产生的事件
    Reset    0x0003    复位蓝牙控制器、链路管理器、基带链路管理器
    Set Event Filter    0x0005    使能主机指定不同事件过滤
    Flush    0x0008    针对指定的蓝牙句柄,放弃所有作为当前待传输数据,甚至当前是属于多个在主机控制器里的L2CAP指令的数据块
    Read PIN Type    0x0009    主机读取指定主机的PIN类型是可变的还是固定的
    Write PIN Type    0x000A    主机写入指定主机的PIN类型是可变的还是固定的
    Create New Unit Key    0x000B    创建新的单一密钥
    Read Stored Link Key    0x000D    读取存放在蓝牙控制器中的单个或者多个密钥
    Write Stored Link Key    0x0011    写入存放在蓝牙控制器中的单个或者多个密钥
    Delete Stored Link Key    0x0012    删除存放在蓝牙控制器中的单个或者多个密钥
    Write Local Name    0x0013    修改蓝牙设备名称
    Read Local Name    0x0014    读取蓝牙设备名称
    Read Connection Accept Timeout    0x0015    读连接识别超时参数值,定时器终止后蓝牙硬件自动拒绝连接
    Write Connection Accept Timeout    0x0016    写连接识别超时参数值,定时器终止后蓝牙硬件自动拒绝连接
    Read Page Timeout    0x0017    读寻呼超时参数值,本地设备返回连接失败前,该值是允许蓝牙硬件定义等待远程设备连接申请时间
    Write Page Timeout    0x0018    写寻呼超时参数值,本地设备返回连接失败前,该值是允许蓝牙硬件定义等待远程设备连接申请时间
    Read Scan Enable    0x0019    写出扫描允许参数值—用来控制蓝牙设备周期性查询
    Write Scan Enable    0x001A    读出扫描允许参数值—用来控制蓝牙设备周期性查询
    Read Page Scan Activity    0x001B    读寻呼扫描间隔、寻呼扫描区间参数
    Write Page Scan Activity    0x001C    写寻呼扫描间隔、寻呼扫描区间参数
    Read Inquiry Scan Activity    0x001D    读查询扫描间隔、查询扫描区间参数
    Write Inquiry Scan Activity    0x001E    写查询扫描间隔、查询扫描区间参数
    Read Authentication Enable    0x001F    读取鉴权允许参数—控制蓝牙设备是否对每个连接进行鉴权
    Write Authentication Enable    0x0020    写取鉴权允许参数—控制蓝牙设备是否对每个连接进行鉴权
    Read Encryption Mode    0x0021    读加密模式数值—控制蓝牙设备是否对每个连接进行加密
    Write Encryption Mode    0x0022    写加密模式数值—控制蓝牙设备是否对每个连接进行加密
    Read Class Of Device    0x0023    读取设备类型参数值,用于区别设备能力
    Write Class Of Device    0x0024    写设备类型参数值,用于区别设备能力
    Read Voice Setting    0x0025    读取语音设置参数值,控制语音连接的各种设置
    Write Voice Setting    0x0026    写语音设置参数值,控制语音连接的各种设置
    Read Automatic Flush Timeout    0x0027    对指定句柄,读取刷新超时值
    Write Automatic Flush Timeout    0x0028    对指定句柄,写入刷新超时值
    Read Num Broadcast Retransmissions    0x0029    读取设备的广播重复发送次数,重复发送提高广播消息的可靠性
    Write Num Broadcast Retransmissions    0x002A    写入设备的广播重复发送次数,重复发送提高广播消息的可靠性
    Read Hold Mode Activity    0x002B    读取Hold Mode Activity的参数值,用来确定Hold挂起的时间
    Write Hold Mode Activity    0x002C    写入Hold Mode Activity的参数值,用来确定Hold挂起的时间
    Read Transmit Power Level    0x002D    对指定句柄,读取传输功率的参数值
    Read Synchronous Flow Control Enable    0x002E    读取SCO流量控制设置。通过使用该设置,主机控制器决定是否主机控制器发送与SCO连接句柄相关的完成分组事件的数量
    Write Synchronous Flow Control Enable    0x002F    读写入SCO流量控制设置。通过使用该设置,主机控制器决定是否主机控制器发送与SCO连接句柄相关的完成分组事件的数量
    Set Host Controller To Host Flow Control    0x0031    主机控制器的打开、关闭,主机控制器到主机的流量控制
    Host Buffer Size    0x0033    主机通知主机控制器自己的ACL、SCO数据缓冲区大小。主机控制器分段传输数据,而数据不会超出这个范围
    Host Number Of Completed Packets    0x0035    当主机对于任何连接的句柄准备接受较多的HCI指令时,该指令用于通过主机指出主机控制器
    Read Link Supervision Timeout    0x0036    读取连接管理超时参数。主从蓝牙设备用该参数监视链路丢失情况
    Write Link Supervision Timeout    0x0037    写入连接管理超时参数。主从蓝牙设备用该参数监视链路丢失情况
    Read Number of Supported IAC    0x0038    读取查询扫描期间本地蓝牙扫描的查询识别码(ICA)的数值
    Read Current IAC LAP    0x0039    读取创建在查询扫描期间本地蓝牙设备正同时扫描的蓝牙识别码的LAP
    Write Current IAC LAP    0x003A    写入创建在查询扫描期间本地蓝牙设备正同时扫描的蓝牙识别码的LAP
    Read Page Scan Period Mode    0x003B    读取本地蓝牙设备的强制寻呼扫描区间模式
    Write Page Scan Period Mode    0x003C    写入本地蓝牙设备的强制寻呼扫描区间模式
    Read Page Scan Mode    0x003D    读取本地蓝牙设备的默认寻呼扫描区间模式
    Write Page Scan Mode    0x003E    写入本地蓝牙设备的默认寻呼扫描区间模式
    (4)、信息指令参数

    Read Local Version Information    0x0001    读取本地蓝牙版本信息
    Read Local Supported Features    0x0003    读取本地蓝牙设备特征表
    Read Local Extended Features    0x0004    
    Read Buffer Size    0x0005    返回HCI缓冲容量。缓冲容量用于传输缓冲数据
    Read Country Code [Deprecated]    0x0007    读取国家代码状态参数值
    Read BD ADDR    0x0009    读取BD_ADDR的参数值
    (5)、状态指令参数

    Read Failed Contact Count    0x0001    读取对于其他设备特殊连接的Failed Contact Count参数值。Failed Contact Count记录在刷新时终止及当前正在传输的L2CAP数据指令被自动刷新后,主单元或从单元不能连续响应事件次数
    Reset Failed Contact Count    0x0002    复位时对于其他设备的连接的Failed Contact Count的参数值。Failed Contact Count记录在刷新时终止及当前正在传输的L2CAP数据指令被自动刷新后,主单元或从单元不能连续响应事件次数
    Get Link Quality    0x0003    读取指定连接句柄的Link Quality的值
    Read RSSI    0x0005    读取对于其他蓝牙设备连接句柄的接收信号强度指示
    Read AFH Channel Map    0x0007    
    Read BD Clock    0x0009    
    (6)、测试指令

    Read Loopback Mode    0x0001    读取主机端控制器会送模式的设置值。回送模式设置可以确定信息发送路径
    Write Loopback Mode    0x0002    写入主机控制器会送模式的设置值。回送模式设置可以确定信息发送路径
    Enable Device Under Test Mode    0x0003    允许本地蓝牙设备模块通过LMP测试指令接入测试模式。当主机要求本地设备作为待测试设备,实现蓝牙测试模式文件中规定测试场景,则发送该指令
    (7)、错误代码

    错误代码    错误含义    错误代码    错误含义
    0x01    位置HCI指令    0x14    由于另一端引起连接中断:资源限制
    0x02    不能连接    0x15    由于另一端引起连接中断:关机
    0x03    硬件故障    0x16    本机中断连接
    0x04    寻呼超时    0x17    重复尝试
    0x05    身份验证失败    0x18    不允许匹配
    0x06    键丢失    0x19    未知LMP PDU
    0x07    存储器已满    0x1A    不支持远端特性
    0x08    连接超时    0x1B    拒绝SCO补偿
    0x09    最大连接数    0x1C    拒绝SCO间歇模式
    0x0A    连接到设备A的最大SCO连接数    0x1D    拒绝SCO无线模式
    0x0B    ACL连接已存在    0x1E    非法链路管理参数
    0x0C    指令非法    0x1F    未特别指明错误
    0x0D    由于资源有限,主机被拒绝    0x20    不支持链路管理器协议参数
    0x0E    由于安全原因,主机被拒绝    0x21    不允许角色改变
    0x0F    由于远端设备单连接设备,主机拒绝    0x22    链路管理响应超时
    0x10    主机超时    0x23    链路管理错误处理事务冲突
    0x11    不支持特性或参数值    0x24    不允许LMP PDU
    0x12    非法主机控制接口指令    0x25~0xFF    保留
    0x13    由于另一端引起连接中断:用户中断连接        
    二、逻辑链路控制与适配协议 L2CAP
    L2CAP位于基带之上,将基带的数据分组转换为便于高层应用的数据分组格式,并提供协议复用和服务质量交换等功能。L2CAP只支持ACL数据传输,不支持SCO数据。

    L2CAP本身不提供加强信道可靠性和保证数据完整性的机制,其信道的可靠性依靠基带提供。

    1、协议复用:
    底层传输协议没有提供对高层协议的复用机制,因而L2CAP支持高层协议复用,L2CAP层可以区分其上的SDP、RFCOMM、TCS等。

    2、分段重组:
    L2CAP层帮助实现基带的短PDU和高层的长PDU相互传输,L2CAP本身不完成任何PDU的分段重组,具体的分段重组有低层和高层来完成。

    3、服务质量
    Qualityof Serivce 信息的交换:蓝牙建立连接的过程中,L2CAP允许交互蓝牙所期望的服务质量,建立完成后,通过监视资源的使用情况,来保证服务质量。

    4、组抽象:
    L2CAP忽略地址组概念,他只关心数据。 
    L2CAP信道有三种类型: 
    **A、面向连接信道:**Connection-OrientedCO,用于两个设备之间的数据通信。 
    **B、无连接信道:**Connection-LessCL,用来向一组设备广播方式传输数据。CID为固定值:0x0002。 
    **C、信令信道:**Signaling,用于创建CO通道,可以通过协商改变CO信道的特性。 
    CL信道的L2CAP_PDU

    长度(2bytes)    信道ID(0x0002)    PSM(最小为2bytes)    有效载荷
    PSM为 协议/服务复用器Protocol/Service Multiplexer,一般为SDP、RFCOMM、TCS等中介协议复用。小于0x1000的值,0x0001对应SDP,0x0003对应RFCOMM、0x0005对应TCS。

    (1)、蓝牙逻辑链路控制与适配协议信令: 
    L2CAP的信令通道的CID为0x0001 
    信令指令分组:

    长度(2byte)    CID(0x0001)    指令1    指令2    ……………    指令n
    L2CAP 分组头部分    
    信令指令格式:

    代码(1byte)    标识符(1byte)    长度(2byte)    数据
    信令指令头


    如上图所示,一条L2CAP信令,1为L2CAP分组头,2为信令指令头,3为数据部分。 
    L2CAP: 
    Role:Master 
    Address:11 
    PDULength: 6 //指令的长度,值为06 00 
    ChannelID: 0x0001 (Signaling)//L2CAP的信令通道,值为01 00 
    Code:Information request//信息请求,值为0a 
    Identifier:1//标识符,值为01 
    CommandLength: 2//命令长度,值为02 00 
    InfoType:Extended features supported//02 00 
    所以这条指令完整的为: 
    06 00 01 00 0a 01 02 00 02 00

    信令的其他操作如下: 


    L2CAP信令指令码:

    Code    Description    
    0x00    RESERVED    保留
    0x01    Command reject    拒绝命令
    0x02    Connection request    连接请求
    0x03    Connection response    连接响应
    0x04    Configure request    配置请求
    0x05    Configure response    配置响应
    0x06    Disconnection request    断开请求
    0x07    Disconnection response    断开响应
    0x08    Echo request    
    0x09    Echo response    
    0x0A    Information request    信息请求
    0x0B    Information response    信息响应
    1)、连接请求Connection_Request Code=0x02

    代码(0x02)    标识符(1字节)    长度(2字节)    PSM(2字节)    源CID(2字节)
    例如:SDP 连接请求

    如上红框所示:

    代码(0x02)    标识符(1字节)    长度(2字节)    PSM(2字节)    源CID(2字节)
    0x02    3    4    SDP    0x0040
    2)、连接相应Connection_Response Code=0x03

    代码(0x03)    标识符    长度    目标CID    源CID    结果    状态
    例如:SDP请求响应 
     
    如上面红框所示:

    代码(0x03)    标识符    长度    目标CID    源CID    结果    状态
    0x03    3    8    0x0040    0x0040    0x0000    
    (2)、MTU MAXIMUMTRANSMISSION UNIT最大传输单元

     
    MTU最大传输单元,L2CAP应用必须支持最小为48字节的MTU,默认值为672 
    (3)、QoS 服务质量

    三、服务发现协议SDP
    SDP两种服务发现模式: 
    1)、服务搜索:查询具有特定服务属性的服务; 
    2)、服务浏览:简单的浏览全部可用服务。

    1 、PDU 格式:(协议数据单元)
    PDU ID(1byte)    Transaction ID(2byte)    参数长度(2byte)    参数1    ……    参数N
    Header    
    不同PDU ID实现SDP的不同功能,概述如下表格:

    Value    Parameter Descirption    
    0x00    Reserved    保留
    0x01    SDP_ErrorResponse    错误响应
    0x02    SDP_ServiceSearchRequest    服务搜索请求
    0x03    SDP_ServiceSearchResponse    服务搜索响应
    0x04    SDP_ServiceAttributeRequest    服务属性请求
    0x05    SDP_ServiceAttributeResponse    服务属性响应
    0x06    SDP_ServiceSearchAttributeRequest    服务搜索属性请求
    0x07    SDP_ServiceSearchAttributeResponse    服务搜索属性响应
    0x08-0xff    Reserved    保留
    2、服务记录表
    SDP的服务记录表对每一个服务进行描述,每条记录包含服务句柄、一组服务属性: 


    Service Record Attributes:服务记录;
    Service Record Handle 服务句柄;


    四、串口仿真协议 RFCOMM

    为建立在串口之上的传统应用提供环境接口,使他们可以做比较少协议改动就可以在蓝牙无线通信无线链路上工作。多路串口仿真是RFCOMM的重要功能,通过多路复用器(multiplexer),一条L2CAP链路可以同时 多个串行应用。 
    两台设备间的串口仿真: 


    RFCOMM 两个蓝牙设备之间可以支持多达60多路仿真串口。

    RFCOMM帧类型如下:

    SABM    异步平衡模式设置指令
    UA    未加编号的确认响应
    DM    断开连接模式响应
    DISC    断开连接指令
    UIH    带头校验的未编号信息命令和响应

     

    展开全文
  • 在android官网可以了解到android4.2新增了部分新功能,但是对于BT熟悉的人或许开始头疼了,那就是Android4.2引入了一个新的蓝牙协议栈针。谷歌和Broadcom之间的合作,开发新的蓝牙协议栈,取代了基于堆栈的Bluez。...
  • 蓝牙协议简介

    千次阅读 2021-04-20 13:45:25
    从左到右依次为:经典蓝牙(BR/EDR)、双模蓝牙(同时支持BR/EDR/LE)和低功耗蓝牙(BLE)。其中经典蓝牙和低功耗蓝牙互不兼容。 其实看结构也可以看出双模蓝牙是经典蓝牙和低功耗蓝牙的合集。 (二)、蓝牙原理及...
  • BLE通信协议

    2021-06-07 16:38:00
    服务与特征Service UUID:6E400001-B5A3-F393-E0A9-E50E24DCCA9ECharacteristic 1UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E属性:通知(Notify)数据长度:最大20字节数据意义:RX特征字Characteristic 2UUID: 6E...
  • 蓝牙相关协议

    千次阅读 2018-12-19 15:33:32
    与开放系统互联(OSI)模型一样,蓝牙技术的协议体系也采用了分层结构,从底层到高层形成了蓝牙协议栈,各层协议定义了所完成的功能和使用数据分组格式,以保证蓝牙产品间的互操作性。 蓝牙协议主要有传输协议、...
  • ZigBee简介 Zigbee是IEEE 802.15.4协议的简称,它来源于蜜蜂的八字舞,蜜蜂(bee)是通过飞翔和“嗡嗡”(zig)抖动翅膀的“舞蹈”来与同伴传递花粉所在方位信息,而ZigBee协议的方式特点与其类似便更名为ZigBee。...
  • 使用设备 基本概念 基本流程本文意在介绍蓝牙开发的主要流程,学习使用蓝牙...下一篇文章还有Android开发之蓝牙(二)——基于BLE协议蓝牙模块通信: http://blog.csdn.net/wzhworld/article/details/76324738使用设
  • 蓝牙可以分为经典蓝牙和低功耗蓝牙,... 物理层是BLE协议栈最底层,规定了BLE通信的基础射频参数,包括信号频率、调制方案等。BLE4的物理层是1Mbps的GFSK调制。在BLE 5的物理层除了兼容原来1Mbps 的GFSK外,还增加了...
  • 蓝牙HSP协议

    2018-05-11 18:43:47
    蓝牙官网HSP协议标准,官方原版。HSP 描述了Bluetooth 耳机如何与计算机或其它Bluetooth 设备(如手机)通信。连接和配置好后,耳机可以作为远程设备的音频输入和输出接口。
  • 蓝牙协议栈分层

    千次阅读 2019-05-29 19:23:24
    蓝牙协议栈分层 蓝牙协议栈的分层, 所有 Profile (配置文件层)和应用都建构在GAP或GATT之上。 PHY——物理层(Physical Layer):1Mbps自适应跳频GFSK(高斯频移键控调制),运行在免征的2.4GHz 频段。 LL——链路层...
  • 蓝牙耳机的两种通讯协议及奥秘

    千次阅读 2020-12-23 11:24:02
    蓝牙耳机的两种通讯协议及奥秘现象两种常见的蓝牙音频协议使用上的尴尬境地 以下内容翻译整理节选自Reddit上面一篇详细的文章,有兴趣的同学可以翻看原文(英文,较长) ...现象 随着TWS(True Wireless Stereo真无线...
  • 蓝牙协议(1)-- 基本协议

    万次阅读 多人点赞 2018-11-30 11:56:36
    蓝牙协议分析(1)基本概念  蓝牙4.1,是一个大杂烩:BR/EDR沿用旧的蓝牙规范;LE抄袭802.15.4;AMP直接使用802.11。而这一切的目的,就是以兼容性和易用性为基础,在功耗和传输速率之间左右为难。 1.蓝牙技术的...
  • 蓝牙协议分析(一)--转

    千次阅读 2018-07-08 20:48:33
    一、蓝牙核心协议 蓝牙支持点对点和点对多点的通信蓝牙最基本的网络结构是匹克网(Picnet)。匹克网实际上是一种个人网络,它以个人区域(即办公室区域)为应用环境。需要指出的是,匹克网并不能够代替局域网,它...
  • BLE--蓝牙协议结构

    千次阅读 2019-03-25 21:58:47
    一、Configurations(结构) ... 1、根据蓝牙协议支持划分 1)、BR/EDR(经典蓝牙) 随着蓝牙协议1.0而发展起来的无线标准 2)、BLE(低功耗蓝牙蓝牙协议4.0引进低功耗无线标准,并且有以...
  • 了解蓝牙的人都知道,在经典蓝牙中,保持连接(Connection)是一个相当消耗资源(power和带宽)的过程。特别是当没有数据传输的时候,所消耗的资源完全被浪费了。因而,对很多蓝牙设备来说(特别是功耗敏感的设备)...
  • 蓝牙串行端口基于SPP协议(Serial Port Profile),能在蓝牙设备之间创建串口进行数据传输2.SPP的UUID:00001101-0000-1000-8000-00805F9B34FB3.Android手机一般以客户端的角色主动连接SPP协议设备连接流程1.检测蓝牙...
  • 此文档主要是介绍蓝牙双向通讯sdk的调用方法以及蓝牙双向传输的核心功能逻辑,用户可以根据需要自定义协议进行蓝牙双向通讯 二、SDK接入步骤: 1: 增加sdk库依赖: a)libs文件夹下增加sdk依赖库 b)应用app...
  • HC05主从蓝牙通信的配置步骤

    千次阅读 2019-04-29 20:30:52
    HC05主从一体化蓝牙模块,可以配置为一个主蓝牙和一个从蓝牙,两个蓝牙之间实现互相通信。 拿到蓝牙之后只需接VCC GND RX TX。 首先让蓝牙进入AT模式 先按住蓝牙上的微动开关,然后给蓝牙上电。蓝牙上的红灯慢闪...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,451
精华内容 8,980
关键字:

蓝牙通信协议