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

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

低功耗蓝牙库

重要

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

概览

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

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

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

架构

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 over Bluetooth 低功耗服务的应用程序必须提供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. AddCustomServicesCb 在您的应用程序中创建 IotBle _:

      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 简介

关贸总协定服务详情

MQTT over BLE 使用数据传输 GATT 服务的实例在 FreeRTOS 设备和代理设备之间发送 MQTT 简明二进制对象表示 (CBOR) 消息。数据传输服务暴露了某些特征,这些特征有助于通过BLE GATT协议发送和接收原始数据。它还处理大于 BLE 最大传输单元 (MTU) 大小的有效负载的分段和组装。

服务 UID

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

服务实例

为与经纪商的每个 MQTT 会话创建一个 GATT 服务实例。每个服务都有一个唯一的 UUID(两个字节),用于标识其类型。每个实例都由实例 ID 区分。

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

特性

特色内容格式:CBOR

最大特征值大小:512 字节

特征 要求 mandatory 可选属性 安全权限 简要描述 UUID
控制 M Write None(无) 写入需要加密 用于启动和停止 MQTT 代理。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF01
tx 消息 M 已读、通知 None(无) 读取需要加密 用于通过代理向经纪人发送包含消息的通知。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF02
rx 消息 M 读取、写入但无响应 None(无) 读取、写入需要加密 用于通过代理接收来自经纪人的消息。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF03
TXLargeMessage M 已读、通知 None(无) 读取需要加密 用于通过代理向代理发送大型消息(消息 > BLE MTU 大小)。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF04
RXLargeMessage M 读取、写入但无响应 None(无) 读取、写入需要加密 用于通过代理接收来自代理的大型消息(消息 > BLE MTU 大小)。 A9D7-166A-D72E-40A9-A002-4804-4CC3-FF05
关贸总协定程序要求
读取特征值 必需
读取长特性值 必需
写入特征值 必需
写入长特征值 必需
阅读特征描述符 必需
编写特征描述符 必需
通知 必需
适应症 必需
消息类型

交换了以下消息类型。

消息类型 消息 使用这些键/值对进行映射
0x01 CONNECT
  • 密钥 = “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 整数,消息类型 (10)

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

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

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

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

  • 密钥 = “s”,值 = 类型 0,整数,状态码为 UnSubscription

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

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

0x0E 断开连接
  • 密钥 = “w”,值 = 类型 0 整数,消息类型 (14)

大型有效载荷传输特性
TXLargeMessage

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

  • 设备通过该特征将有效载荷的第一个 MTU 字节作为通知发送。

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

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

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

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

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

RXLargeMessage

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

  • 代理使用基于此特征的 write with response 逐个写入消息,最大可达 MTU 大小。

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

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

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