低功耗蓝牙库 - FreeRTOS
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

低功耗蓝牙库

重要

该库托管在已弃用的 Amazon-FreeRTOS 存储库中。当您创建新项目时,我们建议从此处开始。如果您已经有一个基于现已弃用的 Amazon-FreeRTOS 存储库的 FreeRTOS 项目,请参阅 Amazon-FreeRTOS Github 存储库迁移指南

概述

FreeRTOS 支持通过代理设备(例如手机)使用低功耗蓝牙功能发布和订阅消息队列遥测传输 (MQTT) 主题。利用 FreeRTOS 低功耗蓝牙 (BLE) 库,您的微控制器可以安全地与 Amazon IoT MQTT 代理进行通信。

通过适用于 FreeRTOS 蓝牙设备的移动开发工具包,您可以编写本机移动应用程序以通过 BLE 与微控制器上的嵌入式应用程序进行通信。有关移动开发工具包的更多信息,请参阅适用于 FreeRTOS 蓝牙设备的移动开发工具包

FreeRTOS BLE 库包含一些用于配置 Wi-Fi 网络,传输大量数据以及通过 BLE 提供网络抽象的服务。为了更直接地控制 BLE 堆栈,FreeRTOS BLE 库还包括中间件和一些低级别 API。

架构

FreeRTOS BLE 库包含三个层:服务、中间件和低级别包装程序。

服务

FreeRTOS BLE 服务层由四个利用中间件 API 的通用属性 (GATT) 服务组成:

  • 设备信息

  • Wi-Fi 预置

  • 网络抽象

  • 大型对象传输

设备信息

设备信息服务可收集有关微控制器的详细信息,其中包括:

  • 您的设备正在使用的 FreeRTOS 的版本。

  • 为其注册的账户的 Amazon IoT 终端节点。

  • 低功耗蓝牙最大传输单元 (MTU)。

Wi-Fi 预置

利用 Wi-Fi 预置服务,具有 Wi-Fi 功能的微控制器可执行以下操作:

  • 列出范围内的网络。

  • 将网络和网络凭证保存到闪存。

  • 设置网络优先级。

  • 从闪存中删除网络和网络凭证。

网络抽象

网络抽象服务将应用程序的网络连接类型抽象化。通用 API 与您的设备的 Wi-Fi、以太网和低功耗蓝牙硬件堆栈交互,使得应用程序兼容多种连接类型。

大型对象传输

大型对象传输服务向客户端发送数据并从客户端接收数据。Wi-Fi 预置和网络抽象等其他服务使用大型对象传输服务来发送和接收数据。您还可以使用大型对象传输 API 来直接与服务交互。

MQTT over BLE

MQTT over BLE 包含用于通过 BLE 创建 MQTT 代理服务的 GATT 配置文件。MQTT 代理服务允许 MQTT 客户端通过网关设备与 Amazon MQTT 代理进行通信。例如,使用代理服务,您可以通过智能手机应用程序将运行 FreeRTOS 的设备连接到 Amazon MQTT。BLE 设备是 GATT 服务器,并且公开了网关设备的服务和特性。使用这些公开的服务和特性,GATT 服务器通过云为设备执行 MQTT 操作。有关详细信息,请参考 附录 A:MQTT over BLE GATT 配置文件

中间件

FreeRTOS 低功耗蓝牙中间件是来自低级别 API 的抽象层。中间件 API 构成了一个面向低功耗蓝牙堆栈的更方便用户使用的界面。

利用中间件 API,您可以跨多个层将多个回调注册到单个事件。初始化低功耗蓝牙中间件还会初始化服务并启动通告。

灵活的回调订阅

假设您的低功耗蓝牙硬件断开连接,并且低功耗蓝牙 MQTT 服务需要检测断开连接事件。您编写的应用程序可能还需要检测相同的断开连接事件。低功耗蓝牙中间件可以将事件路由到已注册回调的代码的各个部分,而不会让较高的层争用低级别资源。

低级别包装程序

低级别 FreeRTOS 低功耗蓝牙包装器是来自制造商的低功耗蓝牙堆栈的抽象。低级别包装程序提供了一组可直接控制硬件的通用 API。低级别 API 优化了 RAM 的使用,但功能有限。

使用低功耗蓝牙服务 API 可与低功耗蓝牙服务交互。该服务 API 相比低级别 API 需要更多资源。

依赖项和要求

低功耗蓝牙库具有以下直接依赖项:

  • 线性容器

  • 一个平台层,直接与操作系统交互用于线程管理、计时器、时钟功能和网络访问。

只有 Wi-Fi 预配置服务具有 FreeRTOS 库依赖项:

GATT 服务 依赖关系
Wi-Fi 预置 Wi-Fi 库

要与 Amazon IoT MQTT 代理进行通信,您必须有一个 Amazon 账户,并且您必须将设备注册为 Amazon IoT 事物。有关设置的更多信息,请参阅 Amazon IoT 开发人员指南

在移动设备上,FreeRTOS 低功耗蓝牙使用 Amazon Cognito 进行用户身份验证。要使用 MQTT 代理服务,您必须创建一个 Amazon Cognito 身份和用户池。每个 Amazon Cognito 身份都必须附加适当的策略。有关更多信息,请参阅 Amazon Cognito 开发人员指南

库配置文件

使用 FreeRTOS 低功耗蓝牙 MQTT 服务的应用程序必须提供一个定义了配置参数的 iot_ble_config.h 标头文件。未定义的配置参数将采用 iot_ble_config_defaults.h 中指定的默认值。

一些重要的配置参数包括:

IOT_BLE_ADD_CUSTOM_SERVICES

允许用户创建其自己的服务。

IOT_BLE_SET_CUSTOM_ADVERTISEMENT_MSG

允许用户自定义通告和扫描响应消息。

有关更多信息,请参阅低功耗蓝牙 API 参考

优化

在优化主板性能时,请注意以下事项:

  • 低级别 API 占用的 RAM 更少,但它提供的功能有限。

  • 您可以将 iot_ble_config.h 标头文件中的 bleconfigMAX_NETWORK 参数设置为一个较小的值来减少使用的堆栈量。

  • 您可以将 MTU 大小增至其最大值来限制消息缓冲,并加快代码运行速度和减少占用的 RAM。

使用限制

默认情况下,FreeRTOS 低功耗蓝牙库将 eBTpropertySecureConnectionOnly 属性设置为 TRUE,这会将设备置于“仅安全连接”模式。按照蓝牙核心规范 5.0 版第 3 册 C 部分 10.2.4 中所述,当设备处于“仅安全连接”模式中时,需要最高 LE 安全模式 1 级别(级别 4)才能访问任何权限高于最低 LE 安全模式 1 级别(级别 1)的属性。在 LE 安全模式 1 级别 4,设备必须具有输入和输出功能才能进行数字比较。

此处列出了支持的模式及其关联属性:

模式 1、级别 1(不安全)
/* Disable numeric comparison */ #define IOT_BLE_ENABLE_NUMERIC_COMPARISON ( 0 ) #define IOT_BLE_ENABLE_SECURE_CONNECTION ( 0 ) #define IOT_BLE_INPUT_OUTPUT ( eBTIONone ) #define IOT_BLE_ENCRYPTION_REQUIRED ( 0 )
模式 1、级别 2(无身份验证配对,有加密)
#define IOT_BLE_ENABLE_NUMERIC_COMPARISON ( 0 ) #define IOT_BLE_ENABLE_SECURE_CONNECTION ( 0 ) #define IOT_BLE_INPUT_OUTPUT ( eBTIONone )
模式 1、级别 3(有身份验证配对,有加密)

不支持此模式。

模式 1、级别 2(有身份验证 LE 安全连接配对,有加密)

默认支持此模式。

有关 LE 安全模式的信息,请参阅蓝牙核心规范 5.0 版第 3 册 C 部分 10.2.1。

初始化

如果您的应用程序通过中间件与低功耗蓝牙堆栈交互,则您只需初始化中间件即可。中间件负责初始化堆栈的较低层。

中间件

初始化中间件

  1. 在调用低功耗蓝牙中间件 API 之前初始化任意低功耗蓝牙硬件驱动程序。

  2. 启用低功耗蓝牙。

  3. 使用 IotBLE_Init() 初始化中间件

    注意

    如果您运行 Amazon 演示,则不需要执行初始化步骤。演示初始化由网络管理器处理,该管理器位于 freertos/demos/network_manager

低级别 API

如果您不想使用 FreeRTOS 低功耗蓝牙 GATT 服务,则可绕过中间件,并直接与低级别 API 交互以节省资源。

初始化低级别 API

  1. 在调用 API 之前初始化所有低功耗蓝牙硬件驱动程序。驱动程序初始化不是低功耗蓝牙低级别 API 的一部分。

  2. 低功耗蓝牙低级别 API 提供了对低功耗蓝牙堆栈的启用/禁用调用以优化功率和资源。在调用 API 之前,您必须启用低功耗蓝牙。

    const BTInterface_t * pxIface = BTGetBluetoothInterface(); xStatus = pxIface->pxEnable( 0 );
  3. 蓝牙管理器包含低功耗蓝牙的和蓝牙经典功能的通用 API。常见管理器的回调必须是第二个初始化的。

    xStatus = xBTInterface.pxBTInterface->pxBtManagerInit( &xBTManagerCb );
  4. 低功耗蓝牙适配器位于常见 API 的上方。您必须初始化其回调,就像您初始化常见 API 一样。

    xBTInterface.pxBTLeAdapterInterface = ( BTBleAdapter_t * ) xBTInterface.pxBTInterface->pxGetLeAdapter(); xStatus = xBTInterface.pxBTLeAdapterInterface->pxBleAdapterInit( &xBTBleAdapterCb );
  5. 注册新的用户应用程序。

    xBTInterface.pxBTLeAdapterInterface->pxRegisterBleApp( pxAppUuid );
  6. 初始化对 GATT 服务器的回调。

    xBTInterface.pxGattServerInterface = ( BTGattServerInterface_t * ) xBTInterface.pxBTLeAdapterInterface->ppvGetGattServerInterface(); xBTInterface.pxGattServerInterface->pxGattServerInit( &xBTGattServerCb );

    在初始化低功耗蓝牙适配器后,您可以添加 GATT 服务器。一次只能注册一个 GATT 服务器。

    xStatus = xBTInterface.pxGattServerInterface->pxRegisterServer( pxAppUuid );
  7. 设置应用程序属性,如仅安全连接和 MTU 大小。

    xStatus = xBTInterface.pxBTInterface->pxSetDeviceProperty( &pxProperty[ usIndex ] );

API 参考

有关完整 API 参考,请参阅低功耗蓝牙 API 参考

示例用法

以下示例说明了如何将低功耗蓝牙库用于通告和创建新服务。有关所有 FreeRTOS 低功耗蓝牙演示应用程序,请参阅低功耗蓝牙演示应用程序

广告

  1. 在您的应用程序中,设置通告 UUID:

    static const BTUuid_t _advUUID = { .uu.uu128 = IOT_BLE_ADVERTISING_UUID, .ucType = eBTuuidType128 };
  2. 然后,定义 IotBle_SetCustomAdvCb 回调函数:

    void IotBle_SetCustomAdvCb( IotBleAdvertisementParams_t * pAdvParams, IotBleAdvertisementParams_t * pScanParams) { memset(pAdvParams, 0, sizeof(IotBleAdvertisementParams_t)); memset(pScanParams, 0, sizeof(IotBleAdvertisementParams_t)); /* Set advertisement message */ pAdvParams->pUUID1 = &_advUUID; pAdvParams->nameType = BTGattAdvNameNone; /* This is the scan response, set it back to true. */ pScanParams->setScanRsp = true; pScanParams->nameType = BTGattAdvNameComplete; }

    此回调在通告消息中发送 UUID,在扫描响应中发送全名。

  3. 打开 vendors/vendor/boards/board/aws_demos/config_files/iot_ble_config.h 并将 IOT_BLE_SET_CUSTOM_ADVERTISEMENT_MSG 设置为 1。这将触发 IotBle_SetCustomAdvCb 回调。

添加新服务

有关服务的完整示例,请参阅 freertos/.../ble/services

  1. 为服务的特性和描述符创建 UUID:

    #define xServiceUUID_TYPE \ {\ .uu.uu128 = gattDemoSVC_UUID, \ .ucType = eBTuuidType128 \ } #define xCharCounterUUID_TYPE \ {\ .uu.uu128 = gattDemoCHAR_COUNTER_UUID,\ .ucType = eBTuuidType128\ } #define xCharControlUUID_TYPE \ {\ .uu.uu128 = gattDemoCHAR_CONTROL_UUID,\ .ucType = eBTuuidType128\ } #define xClientCharCfgUUID_TYPE \ {\ .uu.uu16 = gattDemoCLIENT_CHAR_CFG_UUID,\ .ucType = eBTuuidType16\ }
  2. 创建缓冲区以注册特性和描述符的处理:

    static uint16_t usHandlesBuffer[egattDemoNbAttributes];
  3. 创建属性表。为节省一些 RAM,请将表定义为 const

    重要

    始终按顺序创建属性,将服务作为第一个属性。

    static const BTAttribute_t pxAttributeTable[] = { { .xServiceUUID = xServiceUUID_TYPE }, { .xAttributeType = eBTDbCharacteristic, .xCharacteristic = { .xUuid = xCharCounterUUID_TYPE, .xPermissions = ( IOT_BLE_CHAR_READ_PERM ), .xProperties = ( eBTPropRead | eBTPropNotify ) } }, { .xAttributeType = eBTDbDescriptor, .xCharacteristicDescr = { .xUuid = xClientCharCfgUUID_TYPE, .xPermissions = ( IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM ) } }, { .xAttributeType = eBTDbCharacteristic, .xCharacteristic = { .xUuid = xCharControlUUID_TYPE, .xPermissions = ( IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM ), .xProperties = ( eBTPropRead | eBTPropWrite ) } } };
  4. 创建回调阵列。此回调阵列必须与以上定义的表阵列采用相同的顺序。

    例如,如果在访问 xCharCounterUUID_TYPE 时触发 vReadCounter,并在访问 xCharControlUUID_TYPE 时触发 vWriteCommand,则定义阵列如下:

    static const IotBleAttributeEventCallback_t pxCallBackArray[egattDemoNbAttributes] = { NULL, vReadCounter, vEnableNotification, vWriteCommand };
  5. 创建服务:

    static const BTService_t xGattDemoService = { .xNumberOfAttributes = egattDemoNbAttributes, .ucInstId = 0, .xType = eBTServiceTypePrimary, .pusHandlesBuffer = usHandlesBuffer, .pxBLEAttributes = (BTAttribute_t *)pxAttributeTable };
  6. 使用您在上一步中创建的结构调用 API IotBle_CreateService。中间件同步所有服务的创建,因此在触发 IotBle_AddCustomServicesCb 回调时必须已经定义好了所有新服务。

    1. vendors/vendor/boards/board/aws_demos/config_files/iot_ble_config.h 中将 IOT_BLE_ADD_CUSTOM_SERVICES 设置为 1

    2. 在应用程序中创建 IotBle_AddCustomServicesCb:

      void IotBle_AddCustomServicesCb(void) { BTStatus_t xStatus; /* Select the handle buffer. */ xStatus = IotBle_CreateService( (BTService_t *)&xGattDemoService, (IotBleAttributeEventCallback_t *)pxCallBackArray ); }

移植

用户输入和输出外围设备

安全连接需要输入和输出以进行数字比较。可使用事件管理器注册 eBLENumericComparisonCallback 事件:

xEventCb.pxNumericComparisonCb = &prvNumericComparisonCb; xStatus = BLE_RegisterEventCb( eBLENumericComparisonCallback, xEventCb );

外围设备必须显示数字密钥,并将比较结果作为输入。

移植 API 实施

要将 FreeRTOS 移植到新目标,您必须为 Wi-Fi 预配置服务和低功耗蓝牙的功能实施一些 API。

低功耗蓝牙 API

要使用 FreeRTOS 低功耗蓝牙中间件,您必须实施一些 API。

GAP for Bluetooth Classic 和 GAP for Bluetooth Low Energy 的通用 API
  • pxBtManagerInit

  • pxEnable

  • pxDisable

  • pxGetDeviceProperty

  • pxSetDeviceProperty(所有选项都是强制性的,eBTpropertyRemoteRssieBTpropertyRemoteVersionInfo 除外)

  • pxPair

  • pxRemoveBond

  • pxGetConnectionState

  • pxPinReply

  • pxSspReply

  • pxGetTxpower

  • pxGetLeAdapter

  • pxDeviceStateChangedCb

  • pxAdapterPropertiesCb

  • pxSspRequestCb

  • pxPairingStateChangedCb

  • pxTxPowerCb

特定于低功耗蓝牙 的 GAP 的 API
  • pxRegisterBleApp

  • pxUnregisterBleApp

  • pxBleAdapterInit

  • pxStartAdv

  • pxStopAdv

  • pxSetAdvData

  • pxConnParameterUpdateRequest

  • pxRegisterBleAdapterCb

  • pxAdvStartCb

  • pxSetAdvDataCb

  • pxConnParameterUpdateRequestCb

  • pxCongestionCb

GATT 服务器
  • pxRegisterServer

  • pxUnregisterServer

  • pxGattServerInit

  • pxAddService

  • pxAddIncludedService

  • pxAddCharacteristic

  • pxSetVal

  • pxAddDescriptor

  • pxStartService

  • pxStopService

  • pxDeleteService

  • pxSendIndication

  • pxSendResponse

  • pxMtuChangedCb

  • pxCongestionCb

  • pxIndicationSentCb

  • pxRequestExecWriteCb

  • pxRequestWriteCb

  • pxRequestReadCb

  • pxServiceDeletedCb

  • pxServiceStoppedCb

  • pxServiceStartedCb

  • pxDescriptorAddedCb

  • pxSetValCallbackCb

  • pxCharacteristicAddedCb

  • pxIncludedServiceAddedCb

  • pxServiceAddedCb

  • pxConnectionCb

  • pxUnregisterServerCb

  • pxRegisterServerCb

有关将 FreeRTOS 低功耗蓝牙库移植到您的平台的更多信息,请参阅《FreeRTOS 移植指南》中的移植低功耗蓝牙库

附录 A:MQTT over BLE GATT 配置文件

GATT 服务详细信息

MQTT over BLE 使用数据传输 GATT 服务的实例在 FreeRTOS 设备和代理设备之间发送 MQTT 简洁二进制对象表示法 (CBOR) 格式的消息。数据传输服务会公开某些特征,这些特征有助于通过 BLE GATT 协议发送和接收原始数据。它还可以处理有效负载大于 BLE 最大传输单元 (MTU) 大小的分段和程序集。

服务 UUID

A9D7-166A-D72E-40A9-A002-4804-4CC3-FF00

服务实例

每次与代理进行的 MQTT 会话时都会创建一个 GATT 服务实例。每项服务都有一个用于标识类型的唯一 UUID(两个字节)。每个实例都根据实例 ID 进行区分。

每项服务都作为每个 BLE 服务器设备上的主要服务进行实例化。您可以在指定设备上创建该服务的多个实例。MQTT 代理服务类型具有唯一的 UUID。

特性

特征内容格式:CBOR

最大特征值大小:512 字节

特征 要求 必需属性 可选属性 安全权限 简要描述 UUID
控制 M Write 写入需要加密 用于启动和停止 MQTT 代理。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF01
TXMessage M 读取、通知 读取需要加密 用于通过透传代理 (Proxy) 向中介代理 (Broker) 发送包含消息的通知。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF02
RXMessage M 读写无响应 读写需要加密 用于通过透传代理 (Proxy) 接收来自中介代理 (Broker) 的消息。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF03
TXLargeMessage M 读取、通知 读取需要加密 用于通过透传代理 (Proxy) 向中介代理 (Broker) 发送大消息(消息 > BLE MTU 大小)。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF04
RXLargeMessage M 读写无响应 读写需要加密 用于通过透传代理 (Proxy) 从中介代理 (Broker) 接收大消息(消息 > BLE MTU 大小)。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF05
GATT 过程要求
读取特征值 必需
读取长特征值 必需
写入特征值 必需
写入长特征值 必需
读取特征描述符 必需
写入特征描述符 必需
通知 必需
指示 必需
消息类型

交换以下消息类型。

消息类型 消息 使用这些键/值对进行映射
0x01 连接
  • 键 =“w”,值 = 类型 0 整数,消息类型 (1)

  • 键 =“d”,值 = 类型 3,文本字符串,会话的客户端标识符

  • 键 =“a”,值 = 类型 3,文本字符串,会话的代理端点

  • 键 =“c”,值 = 简单值类型 True/False

0x02 CONNACK
  • 键 =“w”,值 = 类型 0 整数,消息类型 (2)

  • 键 =“s”,值 = 类型 0 整数,状态代码

0x03 发布
  • 键 =“w”,值 = 类型 0 整数,消息类型 (3)

  • 键 =“u”,值 = 类型 3,文本字符串,发布的主题

  • 键 = n”,值 = 类型 0,整数,发布的 QoS

  • 键 =“i”,值 = 类型 0,整数,消息标识符,仅适用于 QoS 1 发布

  • 键 =“k”,值 = 类型 2,字节字符串,发布的有效负载

0x04 PUBACK
  • 仅适用于 QoS 1 消息发送。

  • 键 =“w”,值 = 类型 0 整数,消息类型 (4)

  • 键 =“i”,值 = 类型 0,整数,消息标识符

0x08 订阅
  • 键 =“w”,值 = 类型 0 整数,消息类型 (8)

  • 键 =“v”,值 = 类型 4,文本字符串数组,订阅的主题

  • 键 =“o”,值 = 类型 4,整数数组,订阅的 QoS

  • 键 =“i”,值 = 类型 0,整数,消息标识符

0x09 SUBACK
  • 键 =“w”,值 = 类型 0 整数,消息类型 (9)

  • 键 =“i”,值 = 类型 0,整数,消息标识符

  • 键 =“s”,值 = 类型 0 整数,订阅的状态代码

0X0A UNSUBSCRIBE
  • 键 =“w”,值 = 类型 0 整数,消息类型 (9)

  • 键 =“v”,值 = 类型 4,文本字符串数组,取消订阅的主题

  • 键 =“i”,值 = 类型 0,整数,消息标识符

0x0B UNSUBACK
  • 键 =“w”,值 = 类型 0 整数,消息类型 (11)

  • 键 =“i”,值 = 类型 0,整数,消息标识符

  • 键 =“s”,值 = 类型 0,整数,取消订阅的状态代码

0X0C PINGREQ
  • 键 =“w”,值 = 类型 0 整数,消息类型 (12)

0x0D PINGRESP
  • 键 =“w”,值 = 类型 0 整数,消息类型 (13)

0x0E DISCONNNECT
  • 键 =“w”,值 = 类型 0 整数,消息类型 (14)

大有效负载传输特征
TXLargeMessage

设备使用 txLargeMessage 发送大于 BLE 连接协商的 MTU 大小的大型有效负载。

  • 设备通过特征将有效负载前面的 MTU 字节作为通知发送。

  • 代理根据此特征发送有关剩余字节的读取请求。

  • 设备最多发送 MTU 大小或有效负载的剩余字节,以较小者为准。它每次都会根据发送的有效负载大小来增加读取的偏移量。

  • 代理将继续读取特征,直到获得长度为零的有效负载或小于 MTU 大小的有效负载。

  • 如果设备在指定的超时时间内没有收到读取请求,则传输将失败,代理和网关会释放缓冲区。

  • 如果代理在指定的超时时间内没有收到读取响应,则传输将失败,代理会释放缓冲区。

RXLargeMessage

设备使用 RXLargeMessage 来接收大于 BLE 连接协商的 MTU 大小的大型有效负载。

  • 代理使用响应此特征的写入来逐一写入不超过 MTU 大小的消息。

  • 设备会缓存消息,直到收到长度为零或长度小于 MTU 大小的写入请求。

  • 如果设备在指定的超时时间内没有收到写入请求,则传输将失败,设备会释放缓冲区。

  • 如果代理在指定的超时时间内没有收到写入响应,则传输将失败,代理会释放缓冲区。