nrf52832 学习笔记(七)蓝牙协议层级理解

nrf52832 学习笔记(七)蓝牙协议层级理解

本文主要由一下几篇文档摘录汇总而成 ,如有错误欢迎斧正

  • da14531 蓝牙协议文档
  • 深入浅出低功耗蓝牙(BLE)协议栈
  • 低功耗蓝牙ATT/GATT/Profile/Service/Characteristic规格解读
  • 三种蓝牙架构实现方案(蓝牙协议栈方案)

蓝牙协议,一定要明白蓝牙协议是蓝牙通信的协议,通信协议就是定义通信数据结构和字段含义的一个规定,LL、L2CAP、ATT、GATT、GAP等都是通信协议。

nrf52832 学习笔记(七)蓝牙协议层级理解_第1张图片
单模BLE设备分为三个模块:控制器(Controller)、主机(Host)和应用程序(Application)。

控制器(Controller)

PHY(物理层)

PHY层用来指定BLE所用的无线频段,调制解调方式和方法等。PHY层做得好不好,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。

LL(链路层)

LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。LL提供的功能通常包括:前导码,访问地址和空中协议框架,CRC生成和验证,数据白化,随机数生成和AES加密,并且控制射频模块状态( 待机、广播、扫描、正在启动或已连接 五选一),定义四种角色(广播设备、扫描设备、主设备、从设备)

像Nordic的BLE协议栈能同时支持20个link(连接),就是LL层的功劳。LL层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP或者GATT。

HCI( 主机控制器接口)

协议栈有三种架构,只有 host+controller双芯片标准架构才有HCI层

host+controller双芯片标准架构

nrf52832 学习笔记(七)蓝牙协议层级理解_第2张图片
蓝牙的Host 和 Controller 分别在不同的芯片上运行,Host 和 Controller就需要一种标准化的接口进行通信,这个接口就是HCI

单芯片整体方案

nrf52832 学习笔记(七)蓝牙协议层级理解_第3张图片
host和controller都放在同一颗芯片上,由于host和controller都在同一颗芯片上,因此物理HCI就没有存在的必要性,host和controller之间直接通过API来交互。像Nordic的蓝牙协议栈Softdevice,就是采用这种模式。

自定义双芯片架构

nrf52832 学习笔记(七)蓝牙协议层级理解_第4张图片
和 host+controller双芯片标准架构 类似,只是Host 和 Controller 之间通信使用的是自定义协议

主机(Host)

L2CAP

对ATT层和SM层来的数据进行分包重组形成标准的BLE数据包发送给LL层, 反之接收LL层数据包重组传输到上层,并且还要对连接间隔进行管理。

ATT

nrf52832 学习笔记(七)蓝牙协议层级理解_第5张图片
ATT,全称attribute protocol(数据交互协议)。BLE采用了client/server (C/S) 架构来进行数据交互,一般而言从机提供服务,因此从机是server,手机使用从机提供的服务,因此手机是client。比如蓝牙体温计,它可以提供 “体温” 数据服务,因此是一个server,而手机则可以请求“体温”数据以显示在手机上,因此手机是一个client。

注意这里的server端和client端和主从机没有对应关系,主机可以是 ATT server端或 ATT client端,而从机也可以是 ATT server端或 ATT client端。从机也可以同时成为 ATT server端和 ATT client端。

服务是以数据为载体的,所以说server端提供服务其实就是提供各种有价值的数据。客户端要访问某一个数据,就发送一个request/请求(其实就是一条命令或者PDU),服务端再把该数据返回给客户端(一条response/响应命令或者PDU),这个request 和 response 命令也叫做ATT命令,而ATT则由一系列ATT命令组成。

request 和 response有很多种:
nrf52832 学习笔记(七)蓝牙协议层级理解_第6张图片
nrf52832 学习笔记(七)蓝牙协议层级理解_第7张图片

常用的包括4类:读,写,notify(通知)和indicate(指示)。这些命令又可以分成两种:

  • 如果它需要response,那么会在相应命令后面加上request;
  • 如果它只需要ACK而不需要response,那么它的后面就不会带request。

nrf52832 学习笔记(七)蓝牙协议层级理解_第8张图片

这里要特别强调一点,ATT所有命令都是“必达”的,也就是说每个命令发出去之后,会立马等ACK信息,如果收到了ACK包,发送方认为命令完成;否则发送方会一直重传该命令直到超时导致BLE连接断开。换句话说,只要你的BLE连接没有断开,那么你之前发送的数据包,不管它是用什么ATT PDU来发送的,它肯定被对方收到了。

估计很多人对此会产生疑问,因为大家经常碰到丢包的情况,其实碰到的“丢包”,不是空中把包丢了或者包在空中被干扰了,而是大家发送的代码写得有问题,导致要发送的包没有被安全送达到协议栈射频FIFO中,从而出现所谓的“丢包”。以后大家碰到丢包情况,请先检查代码,保证数据包正确完整安全地送达到协议栈射频FIFO中,只要数据包放到了协议栈射频FIFO中,蓝牙协议栈就能保证该数据包“必达”对方。

既然每个ATT命令都必达对方,那么还需要request类型的命令做什么?

如果一个命令带有request后缀,那么发起方就可以收到命令的response包,这个response包在应用层是有回调事件的,而前述的ACK包在应用层是没有回调事件的。换句话说,不带request的命令,虽然协议栈底层确保了该命令必达对方,但应用层其实并不知道(私有实现方法除外),当你需要实现一个通信序列的时候,这种命令就显得不足了。而采用request/response方式的命令对,request命令发出去之后,必须等到相应的response命令回复才能进行下一步操作,比如发送下一个request命令,这样应用层可以严格按照规定逻辑执行一系列的操作,这个在很多应用场合是非常有用的。

Request/response命令对还有一个副作用:大大降低通信的有效速率(吞吐率),因为request/response命令必须在不同的连接间隔中出现,也就是说,你在间隔1中发送了一个request命令,那么response包必须在间隔2或者稍后间隔中回复,而不能在间隔1中回复,这就导一个数据包的发送需要跨两个连接间隔甚至更多。而不带request后缀的ATT命令就没有这个限制,ACK可以在同一个连接间隔中回复,这样一个连接间隔中可以同时发出多个数据包,这样将大大提高通信速率。大家可以参考下图来理解request和非request命令的区别:

在这里插入图片描述

注:第1个连接间隔中的蓝色包为request命令,旁边的灰色包是该request的ACK;第2个连接间隔的绿色包是response包,而它的ACK是第3个连接间隔中的蓝色包
nrf52832 学习笔记(七)蓝牙协议层级理解_第9张图片

注:图中的绿色包就是非request命令,而紧随其后的灰色包就是它的ACK

attribute

ATT,全称attribute protocol(数据交互协议),也就是操作 attribute 的协议。那么attribute 是什么?
nrf52832 学习笔记(七)蓝牙协议层级理解_第10张图片
蓝牙 server 中的数据都是以 attribute 为单位存储的,attribute 包含一下四部分:

  • Attribute Handle

    Attribute句柄,16-bit长度。Client端要访问Server端的Attribute,都是通过这个句柄来访问的,也就是说ATT PDU一般都包含handle的值。用户在软件代码添加characteristic的时候,系统会自动按顺序地为相关attribute生成句柄。Attribute Handle 相当于一个序号,可以方便查找Attribute 并且可以区分相同Attribute Type 的 Attribute

  • Attribute Type

    Attribute类型,2字节或者16字节长,也就是常说的蓝牙UUID。SIG小组用UUID和世间万物(目前只定义了一小部分)做了一个对照表,将UUID和事物联系起来。例如 心率计 的UUID是 0x180D

  • Attribute Value

    数据真正的值,0到512字节长。

  • Attribute Permissions

    权限属性

Attribute Type 已定义的UUID

/** @defgroup BLE_UUID_VALUES Assigned Values for BLE UUIDs
 * @{ */
/* Generic UUIDs, applicable to all services */
#define BLE_UUID_UNKNOWN                              0x0000 /**< Reserved UUID. */
#define BLE_UUID_SERVICE_PRIMARY                      0x2800 /**< Primary Service. */
#define BLE_UUID_SERVICE_SECONDARY                    0x2801 /**< Secondary Service. */
#define BLE_UUID_SERVICE_INCLUDE                      0x2802 /**< Include. */
#define BLE_UUID_CHARACTERISTIC                       0x2803 /**< Characteristic. */
#define BLE_UUID_DESCRIPTOR_CHAR_EXT_PROP             0x2900 /**< Characteristic Extended Properties Descriptor. */
#define BLE_UUID_DESCRIPTOR_CHAR_USER_DESC            0x2901 /**< Characteristic User Description Descriptor. */
#define BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG        0x2902 /**< Client Characteristic Configuration Descriptor. */
#define BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG        0x2903 /**< Server Characteristic Configuration Descriptor. */
#define BLE_UUID_DESCRIPTOR_CHAR_PRESENTATION_FORMAT  0x2904 /**< Characteristic Presentation Format Descriptor. */
#define BLE_UUID_DESCRIPTOR_CHAR_AGGREGATE_FORMAT     0x2905 /**< Characteristic Aggregate Format Descriptor. */
/* GATT specific UUIDs */
#define BLE_UUID_GATT                                 0x1801 /**< Generic Attribute Profile. */
#define BLE_UUID_GATT_CHARACTERISTIC_SERVICE_CHANGED  0x2A05 /**< Service Changed Characteristic. */
/* GAP specific UUIDs */
#define BLE_UUID_GAP                                  0x1800 /**< Generic Access Profile. */
#define BLE_UUID_GAP_CHARACTERISTIC_DEVICE_NAME       0x2A00 /**< Device Name Characteristic. */
#define BLE_UUID_GAP_CHARACTERISTIC_APPEARANCE        0x2A01 /**< Appearance Characteristic. */
#define BLE_UUID_GAP_CHARACTERISTIC_RECONN_ADDR       0x2A03 /**< Reconnection Address Characteristic. */
#define BLE_UUID_GAP_CHARACTERISTIC_PPCP              0x2A04 /**< Peripheral Preferred Connection Parameters Characteristic. */
#define BLE_UUID_GAP_CHARACTERISTIC_CAR               0x2AA6 /**< Central Address Resolution Characteristic. */
#define BLE_UUID_GAP_CHARACTERISTIC_RPA_ONLY          0x2AC9 /**< Resolvable Private Address Only Characteristic. */
/** @} */

/** @defgroup UUID_SERVICES Service UUID definitions
 * @{ */
#define BLE_UUID_ALERT_NOTIFICATION_SERVICE                      0x1811     /**< Alert Notification service UUID. */
#define BLE_UUID_BATTERY_SERVICE                                 0x180F     /**< Battery service UUID. */
#define BLE_UUID_BLOOD_PRESSURE_SERVICE                          0x1810     /**< Blood Pressure service UUID. */
#define BLE_UUID_CURRENT_TIME_SERVICE                            0x1805     /**< Current Time service UUID. */
#define BLE_UUID_CYCLING_SPEED_AND_CADENCE                       0x1816     /**< Cycling Speed and Cadence service UUID. */
#define BLE_UUID_LOCATION_AND_NAVIGATION_SERVICE                 0x1819     /**< Location and Navigation service UUID. */
#define BLE_UUID_DEVICE_INFORMATION_SERVICE                      0x180A     /**< Device Information service UUID. */
#define BLE_UUID_GLUCOSE_SERVICE                                 0x1808     /**< Glucose service UUID. */
#define BLE_UUID_HEALTH_THERMOMETER_SERVICE                      0x1809     /**< Health Thermometer service UUID. */
#define BLE_UUID_HEART_RATE_SERVICE                              0x180D     /**< Heart Rate service UUID. */
#define BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE                  0x1812     /**< Human Interface Device service UUID. */
#define BLE_UUID_IMMEDIATE_ALERT_SERVICE                         0x1802     /**< Immediate Alert service UUID. */
#define BLE_UUID_LINK_LOSS_SERVICE                               0x1803     /**< Link Loss service UUID. */
#define BLE_UUID_NEXT_DST_CHANGE_SERVICE                         0x1807     /**< Next Dst Change service UUID. */
#define BLE_UUID_PHONE_ALERT_STATUS_SERVICE                      0x180E     /**< Phone Alert Status service UUID. */
#define BLE_UUID_REFERENCE_TIME_UPDATE_SERVICE                   0x1806     /**< Reference Time Update service UUID. */
#define BLE_UUID_RUNNING_SPEED_AND_CADENCE                       0x1814     /**< Running Speed and Cadence service UUID. */
#define BLE_UUID_SCAN_PARAMETERS_SERVICE                         0x1813     /**< Scan Parameters service UUID. */
#define BLE_UUID_TX_POWER_SERVICE                                0x1804     /**< TX Power service UUID. */
#define BLE_UUID_IPSP_SERVICE                                    0x1820     /**< Internet Protocol Support service UUID. */
#define BLE_UUID_BMS_SERVICE                                     0x181E     /**< BOND MANAGEMENT service UUID*/
#define BLE_UUID_CGM_SERVICE                                     0x181F     /**< Continuous Glucose Monitoring service UUID*/
#define BLE_UUID_PLX_SERVICE                                     0x1822     /**< Pulse Oximeter Service UUID*/
#define BLE_UUID_OTS_SERVICE                                     0x1825     /**< Object Transfer Service UUID*/

/** @} */

/** @defgroup UUID_CHARACTERISTICS Characteristic UUID definitions
 * @{ */
#define BLE_UUID_REMOVABLE_CHAR                                  0x2A3A     /**< Removable characteristic UUID. */
#define BLE_UUID_SERVICE_REQUIRED_CHAR                           0x2A3B     /**< Service Required characteristic UUID. */
#define BLE_UUID_ALERT_CATEGORY_ID_CHAR                          0x2A43     /**< Alert Category Id characteristic UUID. */
#define BLE_UUID_ALERT_CATEGORY_ID_BIT_MASK_CHAR                 0x2A42     /**< Alert Category Id Bit Mask characteristic UUID. */
#define BLE_UUID_ALERT_LEVEL_CHAR                                0x2A06     /**< Alert Level characteristic UUID. */
#define BLE_UUID_ALERT_NOTIFICATION_CONTROL_POINT_CHAR           0x2A44     /**< Alert Notification Control Point characteristic UUID. */
#define BLE_UUID_ALERT_STATUS_CHAR                               0x2A3F     /**< Alert Status characteristic UUID. */
#define BLE_UUID_BATTERY_LEVEL_CHAR                              0x2A19     /**< Battery Level characteristic UUID. */
#define BLE_UUID_BLOOD_PRESSURE_FEATURE_CHAR                     0x2A49     /**< Blood Pressure Feature characteristic UUID. */
#define BLE_UUID_BLOOD_PRESSURE_MEASUREMENT_CHAR                 0x2A35     /**< Blood Pressure Measurement characteristic UUID. */
#define BLE_UUID_BODY_SENSOR_LOCATION_CHAR                       0x2A38     /**< Body Sensor Location characteristic UUID. */
#define BLE_UUID_BOOT_KEYBOARD_INPUT_REPORT_CHAR                 0x2A22     /**< Boot Keyboard Input Report characteristic UUID. */
#define BLE_UUID_BOOT_KEYBOARD_OUTPUT_REPORT_CHAR                0x2A32     /**< Boot Keyboard Output Report characteristic UUID. */
#define BLE_UUID_BOOT_MOUSE_INPUT_REPORT_CHAR                    0x2A33     /**< Boot Mouse Input Report characteristic UUID. */
#define BLE_UUID_CURRENT_TIME_CHAR                               0x2A2B     /**< Current Time characteristic UUID. */
#define BLE_UUID_DATE_TIME_CHAR                                  0x2A08     /**< Date Time characteristic UUID. */
#define BLE_UUID_DAY_DATE_TIME_CHAR                              0x2A0A     /**< Day Date Time characteristic UUID. */
#define BLE_UUID_DAY_OF_WEEK_CHAR                                0x2A09     /**< Day Of Week characteristic UUID. */
#define BLE_UUID_DST_OFFSET_CHAR                                 0x2A0D     /**< Dst Offset characteristic UUID. */
#define BLE_UUID_EXACT_TIME_256_CHAR                             0x2A0C     /**< Exact Time 256 characteristic UUID. */
#define BLE_UUID_FIRMWARE_REVISION_STRING_CHAR                   0x2A26     /**< Firmware Revision String characteristic UUID. */
#define BLE_UUID_GLUCOSE_FEATURE_CHAR                            0x2A51     /**< Glucose Feature characteristic UUID. */
#define BLE_UUID_GLUCOSE_MEASUREMENT_CHAR                        0x2A18     /**< Glucose Measurement characteristic UUID. */
#define BLE_UUID_GLUCOSE_MEASUREMENT_CONTEXT_CHAR                0x2A34     /**< Glucose Measurement Context characteristic UUID. */
#define BLE_UUID_HARDWARE_REVISION_STRING_CHAR                   0x2A27     /**< Hardware Revision String characteristic UUID. */
#define BLE_UUID_HEART_RATE_CONTROL_POINT_CHAR                   0x2A39     /**< Heart Rate Control Point characteristic UUID. */
#define BLE_UUID_HEART_RATE_MEASUREMENT_CHAR                     0x2A37     /**< Heart Rate Measurement characteristic UUID. */
#define BLE_UUID_HID_CONTROL_POINT_CHAR                          0x2A4C     /**< Hid Control Point characteristic UUID. */
#define BLE_UUID_HID_INFORMATION_CHAR                            0x2A4A     /**< Hid Information characteristic UUID. */
#define BLE_UUID_IEEE_REGULATORY_CERTIFICATION_DATA_LIST_CHAR    0x2A2A     /**< IEEE Regulatory Certification Data List characteristic UUID. */
#define BLE_UUID_INTERMEDIATE_CUFF_PRESSURE_CHAR                 0x2A36     /**< Intermediate Cuff Pressure characteristic UUID. */
#define BLE_UUID_INTERMEDIATE_TEMPERATURE_CHAR                   0x2A1E     /**< Intermediate Temperature characteristic UUID. */
#define BLE_UUID_LOCAL_TIME_INFORMATION_CHAR                     0x2A0F     /**< Local Time Information characteristic UUID. */
#define BLE_UUID_MANUFACTURER_NAME_STRING_CHAR                   0x2A29     /**< Manufacturer Name String characteristic UUID. */
#define BLE_UUID_MEASUREMENT_INTERVAL_CHAR                       0x2A21     /**< Measurement Interval characteristic UUID. */
#define BLE_UUID_MODEL_NUMBER_STRING_CHAR                        0x2A24     /**< Model Number String characteristic UUID. */
#define BLE_UUID_UNREAD_ALERT_CHAR                               0x2A45     /**< Unread Alert characteristic UUID. */
#define BLE_UUID_NEW_ALERT_CHAR                                  0x2A46     /**< New Alert characteristic UUID. */
#define BLE_UUID_PNP_ID_CHAR                                     0x2A50     /**< PNP Id characteristic UUID. */
#define BLE_UUID_PROTOCOL_MODE_CHAR                              0x2A4E     /**< Protocol Mode characteristic UUID. */
#define BLE_UUID_RECORD_ACCESS_CONTROL_POINT_CHAR                0x2A52     /**< Record Access Control Point characteristic UUID. */
#define BLE_UUID_REFERENCE_TIME_INFORMATION_CHAR                 0x2A14     /**< Reference Time Information characteristic UUID. */
#define BLE_UUID_REPORT_CHAR                                     0x2A4D     /**< Report characteristic UUID. */
#define BLE_UUID_REPORT_MAP_CHAR                                 0x2A4B     /**< Report Map characteristic UUID. */
#define BLE_UUID_RINGER_CONTROL_POINT_CHAR                       0x2A40     /**< Ringer Control Point characteristic UUID. */
#define BLE_UUID_RINGER_SETTING_CHAR                             0x2A41     /**< Ringer Setting characteristic UUID. */
#define BLE_UUID_SCAN_INTERVAL_WINDOW_CHAR                       0x2A4F     /**< Scan Interval Window characteristic UUID. */
#define BLE_UUID_SCAN_REFRESH_CHAR                               0x2A31     /**< Scan Refresh characteristic UUID. */
#define BLE_UUID_SERIAL_NUMBER_STRING_CHAR                       0x2A25     /**< Serial Number String characteristic UUID. */
#define BLE_UUID_SOFTWARE_REVISION_STRING_CHAR                   0x2A28     /**< Software Revision String characteristic UUID. */
#define BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR               0x2A47     /**< Supported New Alert Category characteristic UUID. */
#define BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR            0x2A48     /**< Supported Unread Alert Category characteristic UUID. */
#define BLE_UUID_SYSTEM_ID_CHAR                                  0x2A23     /**< System Id characteristic UUID. */
#define BLE_UUID_TEMPERATURE_MEASUREMENT_CHAR                    0x2A1C     /**< Temperature Measurement characteristic UUID. */
#define BLE_UUID_TEMPERATURE_TYPE_CHAR                           0x2A1D     /**< Temperature Type characteristic UUID. */
#define BLE_UUID_TIME_ACCURACY_CHAR                              0x2A12     /**< Time Accuracy characteristic UUID. */
#define BLE_UUID_TIME_SOURCE_CHAR                                0x2A13     /**< Time Source characteristic UUID. */
#define BLE_UUID_TIME_UPDATE_CONTROL_POINT_CHAR                  0x2A16     /**< Time Update Control Point characteristic UUID. */
#define BLE_UUID_TIME_UPDATE_STATE_CHAR                          0x2A17     /**< Time Update State characteristic UUID. */
#define BLE_UUID_TIME_WITH_DST_CHAR                              0x2A11     /**< Time With Dst characteristic UUID. */
#define BLE_UUID_TIME_ZONE_CHAR                                  0x2A0E     /**< Time Zone characteristic UUID. */
#define BLE_UUID_TX_POWER_LEVEL_CHAR                             0x2A07     /**< TX Power Level characteristic UUID. */
#define BLE_UUID_CSC_FEATURE_CHAR                                0x2A5C     /**< Cycling Speed and Cadence Feature characteristic UUID. */
#define BLE_UUID_CSC_MEASUREMENT_CHAR                            0x2A5B     /**< Cycling Speed and Cadence Measurement characteristic UUID. */
#define BLE_UUID_RSC_FEATURE_CHAR                                0x2A54     /**< Running Speed and Cadence Feature characteristic UUID. */
#define BLE_UUID_SC_CTRLPT_CHAR                                  0x2A55     /**< Speed and Cadence Control Point UUID. */
#define BLE_UUID_RSC_MEASUREMENT_CHAR                            0x2A53     /**< Running Speed and Cadence Measurement characteristic UUID. */
#define BLE_UUID_SENSOR_LOCATION_CHAR                            0x2A5D     /**< Sensor Location characteristic UUID. */
#define BLE_UUID_EXTERNAL_REPORT_REF_DESCR                       0x2907     /**< External Report Reference descriptor UUID. */
#define BLE_UUID_REPORT_REF_DESCR                                0x2908     /**< Report Reference descriptor UUID. */
#define BLE_UUID_LN_FEATURE_CHAR                                 0x2A6A     /**< Location Navigation Service, Feature characteristic UUID. */
#define BLE_UUID_LN_POSITION_QUALITY_CHAR                        0x2A69     /**< Location Navigation Service, Position quality UUID. */
#define BLE_UUID_LN_LOCATION_AND_SPEED_CHAR                      0x2A67     /**< Location Navigation Service, Location and Speed characteristic UUID. */
#define BLE_UUID_LN_NAVIGATION_CHAR                              0x2A68     /**< Location Navigation Service, Navigation characteristic UUID. */
#define BLE_UUID_LN_CONTROL_POINT_CHAR                           0x2A6B     /**< Location Navigation Service, Control point characteristic UUID. */
#define BLE_UUID_BMS_CTRLPT                                      0x2AA4     /**< BMS Control Point characteristic UUID. */
#define BLE_UUID_BMS_FEATURE                                     0x2AA5     /**< BMS Feature characteristic UUID. */
#define BLE_UUID_CGM_MEASUREMENT                                 0x2AA7     /**< CGM Service, Measurement characteristic UUID*/
#define BLE_UUID_CGM_FEATURE                                     0x2AA8     /**< CGM Service, Feature characteristic UUID*/
#define BLE_UUID_CGM_STATUS                                      0x2AA9     /**< CGM Service, Status characteristic UUID*/
#define BLE_UUID_CGM_SESSION_START_TIME                          0x2AAA     /**< CGM Service, session start time characteristic UUID*/
#define BLE_UUID_CGM_SESSION_RUN_TIME                            0x2AAB     /**< CGM Service, session run time characteristic UUID*/
#define BLE_UUID_CGM_SPECIFIC_OPS_CTRLPT                         0x2AAC     /**< CGM Service, specific ops ctrlpt characteristic UUID*/
#define BLE_UUID_PLX_SPOT_CHECK_MEAS                             0x2A5E     /**< PLX Service, spot check measurement characteristic UUID*/
#define BLE_UUID_PLX_CONTINUOUS_MEAS                             0x2A5F     /**< PLX Service, continuous measurement characteristic UUID*/
#define BLE_UUID_PLX_FEATURES                                    0x2A60     /**< PLX Service, feature characteristic UUID*/
#define BLE_UUID_OTS_FEATURES                                    0x2ABD     /**< OTS Service, feature characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_NAME                                 0x2ABE     /**< OTS Service, Object Name characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_TYPE                                 0x2ABF     /**< OTS Service, Object Type characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_SIZE                                 0x2AC0     /**< OTS Service, Object Size characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_FIRST_CREATED                        0x2AC1     /**< OTS Service, Object First Created characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_LAST_MODIFIED                        0x2AC2     /**< OTS Service, Object Last Modified characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_ID                                   0x2AC3     /**< OTS Service, Object ID characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_PROPERTIES                           0x2AC4     /**< OTS Service, Object Properties characteristic UUID*/
#define BLE_UUID_OTS_OACP                                        0x2AC5     /**< OTS Service, Object Action Control Point characteristic UUID*/
#define BLE_UUID_OTS_OLCP                                        0x2AC6     /**< OTS Service, Object List Control Point characteristic UUID*/
#define BLE_UUID_OTS_LF                                          0x2AC7     /**< OTS Service, Object List Filter characteristic UUID*/
#define BLE_UUID_OTS_OBJECT_CHANGED                              0x2AC8     /**< OTS Service, Object Changed characteristic UUID*/


nrf52832 学习笔记(七)蓝牙协议层级理解_第11张图片
温度计 attribute 数据表,为了方便阅读,表中的UUID参数已经替换成了 带《》 的字符。
例如表中的《Primary Service》 其实就是

#define BLE_UUID_SERVICE_PRIMARY                      0x2800 /**< Primary Service. */

例如表中的《GAP》 其实就是

#define BLE_UUID_GATT                                 0x1801 /**< Generic Attribute Profile. */

GATT(Generic Attrubute Profile)

有了att协议,其实蓝牙之间就已经可以互相通信了,但是会有一些问题。

例如一个蓝牙设备内有一条 Attrubute 其中 Attribute Value 为 0x11,那么这个0x11代表什么呢? 长度?大小?次数?还是别的。

有人会说了 不是有 Attribute Type确定类型吗?类型确定了 Attribute Value 不就可以确定了。的确如此,如果是公共UUID的确可以由 Attribute Type 来确定 Attribute Value的内容是何含义。

但是公共UUID仅定义了一些常见的,如果这个 Attribute 不是公共UUID,该如何?很简单,再用一条Attribute 当作描述符,介绍 0x11 的含义。但是如何将这两条 Attribute 联系起来?

GATT 将 Attribute 进行抽象分类,形成一定逻辑结构和含义的数据集,也就是将多条 Attribute 组成 characteristic,将多个 characteristic 组成 service
nrf52832 学习笔记(七)蓝牙协议层级理解_第12张图片

Characteristic

一条characteristic由多条attribute组成。一般一个characteristic包含三种类型的数据条目(attribute):

  • characteristic声明条目(declaration attribute)

    由于一个service可以包含多个characteristic,characteristic declaration就是每个characteristic的分界符,解析时一旦遇到characteristic declaration,就可以认为接下来又是一个新的characteristic了,同时characteristic declaration还将包含value attribute的读写属性等。

  • characteristic值条目(value attribute)

    Characteristic value就是数据的值了,它也是一个单独的attribute

  • characteristic描述符条目(descriptor attribute)

    Characteristic descriptor就是数据的额外信息,比如温度的单位是什么,数据是用小数表示还是百分比表示等之类的数据描述信息。Descriptor属于可选条目,也就是说,一个characteristic可以不包含任何一条descriptor 也可以包含多条descriptor 。

nrf52832 学习笔记(七)蓝牙协议层级理解_第13张图片

这里着重提一种特殊的descriptor:CCCD。一般而言,都是client端来访问server端的characteristic,即通过ATT读或者写PDU访问相关数据。如果server端想直接把自己的characteristic的值告诉client端,就需要通过notify或者indicate PDU,跟其他PDU相比,这2个PDU是由server端自己决定什么时候开始传送,而不是被动接受client端的命令请求。

但client端毕竟是客户端啊,它得有自主权,所以引入了一个CCCD来帮助client端控制server端的行为。client端可以通过禁止CCCD以不接收 notify或者indicate命令,client端也可以通过使能CCCD以允许notify或者indicate命令。

重新总结一下,当CCCD使能的情况下,server端可以随时notify或者indicate数据给client端;当CCCD禁止的时候,哪怕server端有数据,它也不能notify或者indicate给client端。这里强调一下,当characteristic具有notify或者indicate操作功能时,蓝牙规范要求必须为其添加CCCD attribute。

service

一个service 是由一个或多个 Characteristic组成的,这里需要注意 service 不要和 client/server (C/S) 架构中的 server 端搞混淆。

最后简单点说,ATT就是由ATT命令组成的,client/server 之间通过ATT命令 操作attribute。GATT其实就是基于ATT协议并进一步封装从GATT命令,将attribute 组成有实际意义的 Characteristic / service, 使用GATT命令 操作 Characteristic / service(发现和使用服务、特性与描述符)
nrf52832 学习笔记(七)蓝牙协议层级理解_第14张图片

GAP(Generic Access Proflie)

nrf52832 学习笔记(七)蓝牙协议层级理解_第15张图片

GAP是什么?

一个协议,规定如何发现设备、建立连接、管理连接和协商安全级别等过程。

举个例子:广播包格式(Length-Type-Value结构),广播包字段含义就是GAP定义的。

//广播包格式结构体伪代码
typedef struct
{
	uint8_t len;
	uint8_t type;
	uint8_t data[len];
}adv_t;

/**@defgroup BLE_GAP_AD_TYPE_DEFINITIONS GAP Advertising and Scan Response Data format
 * @note Found at https://www.bluetooth.org/Technical/AssignedNumbers/generic_access_profile.htm
 * @{ */
#define BLE_GAP_AD_TYPE_FLAGS                               0x01 /**< Flags for discoverability. */
#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE   0x02 /**< Partial list of 16 bit service UUIDs. */
#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE         0x03 /**< Complete list of 16 bit service UUIDs. */
#define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE   0x04 /**< Partial list of 32 bit service UUIDs. */
#define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE         0x05 /**< Complete list of 32 bit service UUIDs. */
#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE  0x06 /**< Partial list of 128 bit service UUIDs. */
#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE        0x07 /**< Complete list of 128 bit service UUIDs. */
#define BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME                    0x08 /**< Short local device name. */
#define BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME                 0x09 /**< Complete local device name. */
#define BLE_GAP_AD_TYPE_TX_POWER_LEVEL                      0x0A /**< Transmit power level. */
#define BLE_GAP_AD_TYPE_CLASS_OF_DEVICE                     0x0D /**< Class of device. */
#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C               0x0E /**< Simple Pairing Hash C. */
#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R         0x0F /**< Simple Pairing Randomizer R. */
#define BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE           0x10 /**< Security Manager TK Value. */
#define BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS          0x11 /**< Security Manager Out Of Band Flags. */
#define BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE     0x12 /**< Slave Connection Interval Range. */
#define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT       0x14 /**< List of 16-bit Service Solicitation UUIDs. */
#define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT      0x15 /**< List of 128-bit Service Solicitation UUIDs. */
#define BLE_GAP_AD_TYPE_SERVICE_DATA                        0x16 /**< Service Data - 16-bit UUID. */
#define BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS               0x17 /**< Public Target Address. */
#define BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS               0x18 /**< Random Target Address. */
#define BLE_GAP_AD_TYPE_APPEARANCE                          0x19 /**< Appearance. */
#define BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL                0x1A /**< Advertising Interval. */
#define BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS         0x1B /**< LE Bluetooth Device Address. */
#define BLE_GAP_AD_TYPE_LE_ROLE                             0x1C /**< LE Role. */
#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256            0x1D /**< Simple Pairing Hash C-256. */
#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256      0x1E /**< Simple Pairing Randomizer R-256. */
#define BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID             0x20 /**< Service Data - 32-bit UUID. */
#define BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID            0x21 /**< Service Data - 128-bit UUID. */
#define BLE_GAP_AD_TYPE_LESC_CONFIRMATION_VALUE             0x22 /**< LE Secure Connections Confirmation Value */
#define BLE_GAP_AD_TYPE_LESC_RANDOM_VALUE                   0x23 /**< LE Secure Connections Random Value */
#define BLE_GAP_AD_TYPE_URI                                 0x24 /**< URI */
#define BLE_GAP_AD_TYPE_3D_INFORMATION_DATA                 0x3D /**< 3D Information Data. */
#define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA          0xFF /**< Manufacturer Specific Data. */
/**@} */

连接请求/回应 命令,参数更新请求/回应 命令等,都是GAP规定的。

SM

管配对和绑定的,具体可以参考
蓝牙4.2 中文协议文档 安全管理章节
链接:https://pan.baidu.com/s/1ho8dQ5pY5gJ35dApAU4gpw
提取码:hvz2
或者博客低功耗蓝牙配对绑定解读和实践

应用程序(Application)

所谓开发蓝牙应用程序,其实就是开发service和characteristic。通过API,添加自己需要的characteristic和service,你自己的蓝牙设备就诞生了。只要characteristic和service是符合GATT规范的,你可以随意添加任何characteristic和service,并将他们组合成一个专门的蓝牙设备。由于这个蓝牙设备是按照规范来定义的,所以它可以与任何其他蓝牙设备,比如手机,互联互通,并完成所要求的的交互动作。

这里的蓝牙设备,我们还可以进一步细分为蓝牙profile设备和非profile蓝牙设备。profile就是一个子规范,蓝牙profile设备包含的所有实际service和characteristic都是按照profile规格来添加和定义的,比如说心率计profile,就是一个蓝牙联盟定义的蓝牙设备,蓝牙联盟有一份专门的spec来定义心率计profile,在这份spec中规定了心率计profile除了包含心率service,还包含电池service,设备信息service等。

从这可以看出,心率profile和心率service是包含关系,前者包含后者。Nordic开发了很多蓝牙profile应用,如果你的应用就是一个profile,那么可以完全使用这些现成的例子而不做任何修改,这样做还有一个好处:BQB认证流程将大为简化,你只需做一些文档声明工作,不再需要出具测试报告,就可以获得BQB认证证书。

这里强调一下,如果你的设备没有完全按照心率profile来,但也包含心率service,这个时候你就不能说你的设备是一个心率profile设备,虽然你的设备不是心率profile设备,但是它提供的功能基本上跟心率profile设备差不多,因为它包含心率service。这种情况下,你的设备还是一个标准的蓝牙设备,还是可以去过BQB认证的,而且过BQB认证的流程也不复杂,就是比profile设备多了一个自测报告而已。这种设备跟手机通信,是没有任何兼容性或者互联互通问题的。最后强调一下,目前大部分人开发的蓝牙应用程序,其实都是非profile蓝牙应用程序(也叫私有proflie设备),比如手环、共享单车、蓝牙透传等。

Profile

profile是一个大家经常见到的英文单词,但是总感觉领会不到这个词的内涵。Profile,英文本义就是脸的侧面轮廓,这里大家一定要注意,脸的轮廓不等于脸本身(脸本身是非常复杂和细致的)

但profile本身是对脸的一种抽象,描述和定义,蓝牙核心规范其实也是使用profile这个引申意义而已,换句话说,蓝牙的profile跟英文字典中的profile是同一个意思,意义基本接近。

要定义蓝牙,必须要有一个规范,这就是蓝牙核心规范V4.2/V5.0/V5.1……蓝牙规范非常复杂和庞大,大部分蓝牙设备只实现了蓝牙规范中很少一部分,那么没有实现的这些规范对这个蓝牙设备来说能不能称为规范?当然不能!所谓规范或者规格,就是强制的,就必须实现。

针对这种情况,profile可以很好地应对。我们把蓝牙某部分规范称为profile,这个profile如果设备要实现它,那么它就是强制的;如果设备不用它,也没关系,这就是profile。基于此,我们可以把profile翻译成子规范或者条件规范或者剖面规范。

“蓝牙规范包含很多子规范”,这句话用中文说问题不是很大,但是你把它翻译成英文,那就难了!这就是英文需要用profile的原因(而不是spec),以及为什么profile在规范中出现的如此频繁。

GAP Role/Security Profiles

不要和Host层中的 GAP(Generic Access Proflie)搞混,这里的 GAP Role/Security Profiles 是一个协议,规定了 GAP 服务有哪些 characteristic。
nrf52832 学习笔记(七)蓝牙协议层级理解_第16张图片
从截图上可以看到 GAP 服务包含 Name、Appearance、Connection Parameters、Central Address Resolution characteristic。

GATT Profiles

不要和Host层中的 GATT(Generic Attrubute Profile)搞混,这里的 GATT Profiles 是一个协议,规定了 GATT 服务有哪些characteristic。
nrf52832 学习笔记(七)蓝牙协议层级理解_第17张图片

如何通过无线发送一个数据包?

假设有设备A和设备B,设备A要把自己目前的电量状态83%(十六进制表示为0x53)发给设备B,该怎么做呢?

作为一个开发者,他希望越简单越好,对他而言,他希望调用一个简单的API就能完成这件事,比如send(0x53),实际上我们的BLE协议栈就是这样设计的,开发者只需调用send(0x53)就可以把数据发送出去了,其余的事情BLE协议栈帮你搞定。很多人会想,BLE协议栈是不是直接在物理层就把0x53发出去,就如下图所示:

nrf52832 学习笔记(七)蓝牙协议层级理解_第18张图片

这种方式初看起来挺美的,但由于很多细节没有考虑到,实际是不可行的。

首先,它没有考虑用哪一个射频信道来进行传输,在不更改API的情况下,我们只能对协议栈进行分层,为此引入LL层,开发者还是调用send(0x53),send(0x53)再调用send_LL(0x53,2402M)(注:2402M为信道频率)。

这里还有一个问题,设备B怎么知道这个数据包是发给自己的还是其他人的,为此BLE引入access address概念,用来指明接收者身份,其中,0x8E89BED6这个access address比较特殊,它表示要发给周边所有设备,即广播。

如果你要一对一的进行通信(BLE协议将其称为连接),即设备A的数据包只能设备B接收,同样设备B的数据包只能设备A接收,那么就必须生成一个独特的随机access address以标识设备A和设备B两者之间的连接。

广播方式

我们先来看一下简单的广播情况,这种情况下,我们把设备A叫advertiser(广播者),设备B叫scanner或者observer(扫描者)。

广播状态下设备A的LL层API将变成send_LL(0x53,2402M, 0x8E89BED6)。
由于设备B可以同时接收到很多设备的广播,因此数据包还必须包含设备A的device address(0xE1022AAB753B)以确认该广播包来自设备A,为此send_LL参数需要变成(0x53,2402M, 0x8E89BED6, 0xE1022AAB753B)。

LL层还要检查数据的完整性,即数据在传输过程中有没有发生窜改,为此引入CRC24对数据包进行检验 (假设为0xB2C78E) 。同时为了调制解调电路工作更高效,每一个数据包的最前面会加上1个字节的preamble(前导帧),preamble一般为0x55或者0xAA。这样,整个空中包就变成(注:空中包用小端模式表示!):

nrf52832 学习笔记(七)蓝牙协议层级理解_第19张图片

上面这个数据包还有如下问题:

  1. 没有对数据包进行分类组织,设备B无法找到自己想要的数据0x53。
    为此我们需要在access address之后加入两个字段:LL header和长度字节。LL header用来表示数据包的LL类型,长度字节用来指明payload的长度

  2. 设备B什么时候开启射频窗口以接收空中数据包?
    如上图case1所示,当设备A的数据包在空中传输的时候,设备B把接收窗口关闭,此时通信将失败;同样对case2来说,当设备A没有在空中发送数据包时,设备B把接收窗口打开,此时通信也将失败。只有case3的情况,通信才能成功,即设备A的数据包在空中传输时,设备B正好打开射频接收窗口,此时通信才能成功,换句话说,LL层还必须定义通信时序。

  3. 当设备B拿到数据0x53后,该如何解析这个数据呢?它到底表示湿度还是电量,还是别的意思?
    这个就是GAP层要做的工作,GAP层引入了LTV(Length-Type-Value)结构来定义数据,比如020105,02-长度,01-类型(强制字段,表示广播flag,广播包必须包含该字段),05-值。

    由于广播包最大只能为31个字节,它能定义的数据类型极其有限,像这里说的电量,GAP就没有定义,因此要通过广播方式把电量数据发出去,只能使用供应商自定义数据类型0xFF,即04FF590053,其中04表示长度,FF表示数据类型(自定义数据),0x0059是供应商ID(自定义数据中的强制字段),0x53就是我们的数据(设备双方约定0x53就是表示电量,而不是其他意思)。

最终空中传输的数据包将变成:

AAD6BE898E600E3B75AB2A02E102010504FF5900538EC7B2

  • AA – 前导帧(preamble)
  • D6BE898E – 访问地址(access address)
  • 60 – LL帧头字段(LL header)
  • 0E – 有效数据包长度(payload length)
  • 3B75AB2A02E1 – 广播者设备地址(advertiser address)
  • 02010504FF590053 – 广播数据
  • 8EC7B2 – CRC24值

在这里插入图片描述

有了PHY,LL和GAP,就可以发送广播包了,但广播包携带的信息极其有限,而且还有如下几大限制:

  • 无法进行一对一双向通信 (广播是一对多通信,而且是单方向的通信)
  • 由于不支持组包和拆包,因此无法传输大数据
  • 通信不可靠及效率低下。广播信道不能太多,否则将导致扫描端效率低下。为此,BLE只使用37(2402MHz) /38(2426MHz) /39(2480MHz)三个信道进行广播和扫描,因此广播不支持跳频。由于广播是一对多的,所以广播也无法支持ACK。这些都使广播通信变得不可靠。
  • 扫描端功耗高。由于扫描端不知道设备端何时广播,也不知道设备端选用哪个频道进行广播,扫描端只能拉长扫描窗口时间,并同时对37/38/39三个通道进行扫描,这样功耗就会比较高。

而连接则可以很好解决上述问题,下面我们就来看看连接是如何将0x53发送出去的。

连接方式

到底什么叫连接(connection)?

像有线UART,很容易理解,就是用线(Rx和Tx等)把设备A和设备B相连,即为连接。用“线”把两个设备相连,实际是让2个设备有共同的通信媒介,并让两者时钟同步起来。蓝牙连接有何尝不是这个道理,所谓设备A和设备B建立蓝牙连接,就是指设备A和设备B两者一对一“同步”成功,其具体包含以下几方面:

  • 设备A和设备B对接下来要使用的物理信道达成一致
  • 设备A和设备B双方建立一个共同的时间锚点,也就是说,把双方的时间原点变成同一个点
  • 设备A和设备B两者时钟同步成功,即双方都知道对方什么时候发送数据包什么时候接收数据包

连接成功后,设备A和设备B通信流程如下所示:
nrf52832 学习笔记(七)蓝牙协议层级理解_第20张图片

如上图所示,一旦设备A和设备B连接成功(此种情况下,我们把设备A称为Master或者Central,把设备B称为Slave或者Peripheral),设备A将周期性以CI(connection interval)为间隔向设备B发送数据包,而设备B也周期性地以CI为间隔打开射频接收窗口以接收设备A的数据包。

同时按照蓝牙spec要求,设备B收到设备A数据包150us后,设备B切换到发送状态,把自己的数据发给设备A;设备A则切换到接收状态,接收设备B发过来的数据。由此可见,连接状态下,设备A和设备B的射频发送和接收窗口都是周期性地有计划地开和关,而且开的时间非常短,从而大大降低系统功耗并大大提高系统效率。

现在我们看看连接状态下是如何把数据0x53发送出去的,从中大家可以体会到蓝牙协议栈分层的妙处。

对开发者来说,很简单,他只需要调用send(0x53)

GATT层定义数据的类型和分组,方便起见,我们用0x0013表示电量这种数据类型,这样GATT层把数据打包成130053(小端模式!)

ATT层用来选择具体的通信命令,比如读/写/notify/indicate等,这里选择notify命令0x1B,这样数据包变成了:1B130053

L2CAP用来指定connection interval(连接间隔),比如每10ms同步一次(CI不体现在数据包中),同时指定逻辑通道编号0004(表示ATT命令),最后把ATT数据长度0x0004加在包头,这样数据就变为:040004001B130053

LL层要做的工作很多,首先LL层需要指定用哪个物理信道进行传输(物理信道不体现在数据包中),然后再给此连接分配一个Access address(0x50655DAB)以标识此连接只为设备A和设备B直连服务,然后加上LL header和payload length字段,LL header标识此packet为数据packet,而不是control packet等,payload length为整个L2CAP字段的长度,最后加上CRC24字段,以保证整个packet的数据完整性,所以数据包最后变成:
AAAB5D65501E08040004001B130053D550F6

  • AA – 前导帧(preamble)
  • 0x50655DAB – 访问地址(access address)
  • 1E – LL帧头字段(LL header)
  • 08 – 有效数据包长度(payload length)
  • 04000400 – ATT数据长度,以及L2CAP通道编号
  • 1B – notify command
  • 0x0013 – 电量数据handle
  • 0x53 – 真正要发送的电量数据
  • 0xF650D5 – CRC24值
    在这里插入图片描述

虽然开发者只调用了 send(0x53),但由于低功耗蓝牙协议栈层层打包,最后空中实际传输的数据将变成下图所示的模样,这就既满足了低功耗蓝牙通信的需求,又让用户API变得简单,可谓一箭双雕!

你可能感兴趣的:(蓝牙,BLE,nrf52832,nordic,蓝牙,BLE,嵌入式)