CC2640学习笔记——自定义广播
上文将SDK广播例程导入到了工作区,并修改工程配置使其正常编译下载到CC2640R2F芯片中。接下来笔者通过修改例程实现广播自定义数据。
一、广播信息定义
广播名称 | ATCBroadcastTest |
---|---|
蓝牙类型 | LE only(低功耗蓝牙BLE) |
广播类型 | Scannable Undirected event |
广播数据 | ATCTest |
二、GAP概述
Generic Access Profile (GAP):低功耗蓝牙协议栈的 GAP 层负责连接功能。该层处理设备的访问模式和过程,包括设备发现、链路建立、链路终止、安全功能的启动和设备配置。
- GAP状态
基于设备配置的角色,上图 GAP 状态图显示了设备的各种状态。下面描述这些状态。
- 待机:复位后设备处于初始空闲状态。
- 广播:设备使用特定数据进行广告,让任何发起设备知道它是可连接设备(此广告包含设备地址,并且可以包含一些附加数据,例如设备名称)。
- 扫描:扫描设备收到广告后,向广告商发送扫描请求。广告商以扫描响应进行响应。此过程称为设备发现。扫描设备知道广告设备并且可以发起与其的连接。
- 发起:发起时,发起者必须指定要连接的对端设备地址。如果接收到与对等设备的该地址匹配的通告,则发起设备会发出请求,以使用 GAP 连接状态中描述的连接参数与通告设备建立连接(链接)。
- Peripheral/Central:当连接形成时,如果设备是广告者,则充当 Peripheral;如果是发起者,则充当 Central。
蓝牙设备根据应用用例以一种或多种 GAP 角色运行。 GAP 角色利用一种或多种 GAP 状态。基于此配置,许多蓝牙低功耗协议栈事件直接由主应用程序任务处理,有时从 GAP Bond Manager 路由。应用程序可以向堆栈注册以获得某些事件的通知。
- GAP角色
根据设备的配置,GAP 层始终以规范定义的四 个角色之一进行操作:
- 广播者- 设备是不可连接的广告者。
- 观察者 - 设备扫描广告但无法发起连接。
- 外设 - 该设备是一个广告商,可连接并在单个链路层连接中作为外设运行。
- 中央设备 - 设备扫描广告并启动连接,并在单个或多个链路层连接中作为中央设备运行。
三、广播相关协议概述
1、广播事件
BLE广播事件有七种类型(见《Bluetooth Core Specification》V5.2 Vol6-PartB-4.4.2)。
关于各种类型的广播事件详情读者可自行研究《蓝牙核心规范》,笔者简述下接下来要使用的不定向可扫描的广播事件(Scannable Undirected event)。
2、不定向可扫描广播事件(见《Bluetooth Core Specification》V5.2 Vol6-PartB-4.4.2.5)
可扫描无向广告事件使用 ADV_SCAN_ID 作为指示标志 (ADV_EXT_IND PDU 暂不讨论)。读者有兴趣可以在实验过程中使用抓包工具查看PDU格式及内容,是否跟协议规范一致。
可扫描无向事件类型允许扫描者使用扫描请求(SCAN_REQ PDU)来响应,以请求广播者返回扫描附加信息。
如果广播者从广播过滤策略允许的扫描者接收到包含其设备地址的SCAN_REQ PDU,则其应在同一广播频道索引上使用SCAN_RSP PDU进行回复。发送SCAN_RSP PDU后,或者如果广播过滤策略不允许处理SCAN_REQ PDU,则广播者应移动到下一个使用的主要广播频道索引以发送另一个ADV_SCAN_IND PDU,或者关闭广播事件。
广播事件中两个连续ADV_SCAN_IND PDU开始之间的时间应小于或等于10ms。广播事件应在广告间隔内关闭。
未接收到SCAN_REQ PDU的广播事件的结构如下图所示。
接收到SCAN_REQ PDU并返回SCAN_RSP PDU的广播事件的结构如下图所示。
对于不定向可扫描广播事件不会相应来自广播通道上的CONNECT_IND PDU。
广播时间间隔在编写程序时讨论。
3、消息序列图
(1)低功耗蓝牙协议栈
低功耗蓝牙协议栈模型如图所示。
关于蓝牙的OSI模型和Host/Controller模型笔者理解有限,不在此献丑,有兴趣可自行研究相关文档。
实现蓝牙的不定向可扫描广播只需要跟 GAP这个对象打交道就行了。GAP上面已经进行了简要叙述。
要理解应用蓝牙广播的配置,需要了解通过应用层代码实现对协议栈 HCI和LL 的控制。
HCI描述参考TI官方协议栈对HCI的介绍。
LL描述参考TI官方协议栈对LL的介绍
(2)不定向广播消息序列
下图描述了设备B向设备A发送广播,设备B的Host层和LL层之间的信息交互过程。
四、代码实现
接下来基于上一篇 广播例程工程 实现不定向可扫描广播,周期广播自定义数据。
1、修改工程名称
将 ble5_simple_broadcaster_cc2640r2lp_app 重命名为 AtcBroadcaster_app。
将 ble5_simple_broadcaster_cc2640r2lp_stack_library 重命名为为 AtcBroadcaster_stack_library。
修改 AtcBroadcaster_app项目配置,选定 AtcBroadcaster_app 打开路径“Project->Properties->Build->Dependencies”,将ble5_simple_broadcaster_cc2640r2lp_stack_library 条目删除,添加AtcBroadcaster_stack_library。点击“Apply and Close”按钮生效更改。
重新编译两个文件夹,AtcBroadcaster_app 编译完会提示警告“Referenced project ‘ble5_simple_broadcaster_cc2640r2lp_stack_library’ does not exist in the workspace. ”,在文件“AtcBroadcaster_app/.project”中找到该警告位置删除该条目。
重新编译不再发出警告。
Ps:这个问题是笔者强迫症使然,不修改工程名称,或者忽略掉最后这个警告都不影响后续操作。
2、main 函数
打开路径“AtcBroadcaster_app ->Startup->main.c”,可以看到例程main函数如下所示
/*******************************************************************************
* @fn Main
*
* @brief Application Main
*
* input parameters
*
* @param None.
*
* output parameters
*
* @param None.
*
* @return None.
*/
int main()
{
/* Register Application callback to trap asserts raised in the Stack */
RegisterAssertCback(AssertHandler);
Board_initGeneral();
#ifndef POWER_SAVING
/* Set constraints for Standby, powerdown and idle mode */
Power_setConstraint(PowerCC26XX_SB_DISALLOW);
Power_setConstraint(PowerCC26XX_IDLE_PD_DISALLOW);
#endif // POWER_SAVING
#ifdef ICALL_JT
user0Cfg.appServiceInfo->timerTickPeriod = Clock_tickPeriod;
user0Cfg.appServiceInfo->timerMaxMillisecond = ICall_getMaxMSecs();
#endif /* ICALL_JT */
/* Initialize ICall module */
ICall_init();
/* Start tasks of external images - Priority 5 */
ICall_createRemoteTasks();
/* Kick off application - Priority 1 */
SimpleBroadcaster_createTask();
BIOS_start(); /* enable interrupts and start SYS/BIOS */
return 0;
}
现在简要分析一下主函数。
前两句分别是注册断言回调函数和开发板必要的初始化,可以不必理会。
接下来两个条件条件编译嵌套,查看"AtcBroadcaster_app ->TOOLS->defines->ble5_simple_broadcaster_cc2640r2lp_app_FlashROM_StackLibrary.opt”文件可以看到,声明了"POWER_SAVING"和“ICALL_JT”。
因此前一个嵌套可以忽略掉,后一个前套内设置了用户参数 user0Cfg,这是初始化蓝牙协议栈的必要参数。
ICall_init(),初始化了ICall模块, ICall 允许应用程序和协议栈在统一的 TI-RTOS 环境中高效运行、通信和共享资源。
ICall_createRemoteTasks(), 创建蓝牙协议栈任务但并未启动, 该任务优先级为 5,即任务最高优先级。前面的用户参数 user0Cfg,在这个函数里作为配置参数。
SimpleBroadcaster_createTask(), 创建用户自定义蓝牙任务,蓝牙行为和事件都在这个任务里。
BIOS_start(),开始 TI-RTOS 任务调度。
例程主函数逻辑清晰,叙述简要。在此框架下适当修改即可。
3、自定义蓝牙任务
(1) 修改文件名称
项目浏览器中打开路径 “AtcBroadcaster_app->Application”,将 “simple_broadcaster.c” 和 “simple_broadcaster.h” 两个文件名称改为 “AtcBroadcaster.c”、“AtcBroadcaster.h”。
更改 “AtcBroadcaster.h” 条件编译语句。
······
#ifndef ATCBROADCASTER_H/* SIMPLEBROADCASTER_H */
#define ATCBROADCASTER_H/* SIMPLEBROADCASTER_H */
#ifdef __cplusplus
extern "C"
{
#endif
······
(2) 修改 AtcBroadcaster.c
首先使用替换工具将“Simple”字符串替换为“Atc”。
(3) 广播任务
例程广播任务代码如下所示
/*********************************************************************
* @fn AtcBroadcaster_processEvent
*
* @brief Application task entry point for the Atc Broadcaster.
*
* @param none
*
* @return none
*/
static void AtcBroadcaster_taskFxn(UArg a0, UArg a1)
{
// Initialize application
AtcBroadcaster_init();
// Application main loop
for (;;)
{
// Get the ticks since startup
uint32_t tickStart = Clock_getTicks();
uint32_t events;
events = Event_pend(syncEvent, Event_Id_NONE, SB_ALL_EVENTS,
ICALL_TIMEOUT_FOREVER);
if (events)
{
ICall_EntityID dest;
ICall_ServiceEnum src;
ICall_HciExtEvt *pMsg = NULL;
if (ICall_fetchServiceMsg(&src, &dest,
(void **)&pMsg) == ICALL_ERRNO_SUCCESS)
{
if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
{
// Process inter-task message
AtcBroadcaster_processStackMsg((ICall_Hdr *)pMsg);
}
if (pMsg)
{
ICall_freeMsg(pMsg);
}
}
// If RTOS queue is not empty, process app message.
if (events & SB_QUEUE_EVT)
{
while (!Queue_empty(appMsgQueueHandle))
{
sbEvt_t *pMsg = (sbEvt_t *)Util_dequeueMsg(appMsgQueueHandle);
if (pMsg)
{
// Process message.
AtcBroadcaster_processAppMsg(pMsg);
// Free the space from the message.
ICall_free(pMsg);
}
}
}
}
}
}
AtcBroadcaster_init(), 调用这个函数,对使用蓝牙应用进行必要的配置。
在主循环中调用 Event_pend() 函数等待 ICall 事件和用户自定义事件发生,并在后面的代码处理他们。
AtcBroadcaster_processStackMsg(), 处理协议栈对应用任务发送的事件标志。
AtcBroadcaster_processAppMsg(), 处理用户自定义的时间标志。
状态机是个很好的工具,能帮助编程人员理清编程对象的行为特征,初学者建议可以多了解一下。
(4) 广播任务初始化
初始化代码如下,
/*********************************************************************
* @fn AtcBroadcaster_init
*
* @brief Initialization function for the Atc Broadcaster App
* Task. This is called during initialization and should contain
* any application specific initialization (ie. hardware
* initialization/setup, table initialization, power up
* notification ...).
*
* @param none
*
* @return none
*/
static void AtcBroadcaster_init(void)
{
// ******************************************************************
// N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
// ******************************************************************
// Register the current thread as an ICall dispatcher application
// so that the application can send and receive messages.
ICall_registerApp(&selfEntity, &syncEvent);
#ifdef USE_RCOSC
RCOSC_enableCalibration();
#endif // USE_RCOSC
// Create an RTOS queue for message from profile to be sent to app.
appMsgQueueHandle = Util_constructQueue(&appMsgQueue);
// Open LCD
dispHandle = Display_open(Display_Type_ANY, NULL);
// Register with GAP for HCI/Host messages. This is needed to receive HCI
// events. For more information, see the HCI section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
GAP_RegisterForMsgs(selfEntity);
//Initialize GAP layer for Peripheral role and register to receive GAP events
GAP_DeviceInit(GAP_PROFILE_BROADCASTER, selfEntity, addrMode, NULL);
Display_printf(dispHandle, 0, 0, "BLE Broadcaster");
}
ICall_registerApp(), 这是要使用协议栈必须调用的函数,它注册当前任务为ICall 调度应用程序,使其能和协议栈收发数据通信。
Util_constructQueue(), 创建一个应用消息队列,用于接收协议栈发给应用任务的消息。
GAP_RegisterForMsgs(), HCI 传输协议栈和蓝牙控制单元间的命令和消息,这个函数设置用户任务可接收 HCI 消息。
GAP_DeviceInit(), GAP 设备初始化,将当前设备 GAP 角色设置为广告者。
(5) 协议栈事件处理
协议栈事件处理代码如下
/*********************************************************************
* @fn AtcBroadcaster_processStackMsg
*
* @brief Process an incoming stack message.
*
* @param pMsg - message to process
*
* @return none
*/
static void AtcBroadcaster_processStackMsg(ICall_Hdr *pMsg)
{
switch (pMsg->event)
{
case GAP_MSG_EVENT:
AtcBroadcaster_processGapMessage((gapEventHdr_t*) pMsg);
break;
default:
// do nothing
break;
}
}
可以看到例程中仅实现了一个 GAP_MSG_EVENT 事件,这事件处理在协议栈 GAP 这个对象的相关事务,蓝牙广播就是 GAP 的业务范围。因此需要着重学习 GAP 消息处理函数,在例程的基础上稍加修改就能达到效果了。
协议栈事件有很多,GAP_MSG_EVENT 只是其中很重要中的一个。协议栈定义的事件标志可以找到定义如下。
/*********************************************************************
* BLE OSAL GAP GLOBAL Events
*/
#define GAP_EVENT_SIGN_COUNTER_CHANGED 0x4000 //!< The device level sign counter changed
// GAP - Messages IDs (0xD0 - 0xDF)
#define GAP_MSG_EVENT 0xD0 //!< Incoming GAP message
// SM - Messages IDs (0xC1 - 0xCF)
#define SM_NEW_RAND_KEY_EVENT 0xC1 //!< New Rand Key Event message
#define SM_MSG_EVENT 0xC2 //!< Incoming SM message
// GATT - Messages IDs (0xB0 - 0xBF)
#define GATT_MSG_EVENT 0xB0 //!< Incoming GATT message
#define GATT_SERV_MSG_EVENT 0xB1 //!< Incoming GATT Serv App message
// L2CAP - Messages IDs (0xA0 - 0xAF)
#define L2CAP_DATA_EVENT 0xA0 //!< Incoming data on a channel
#define L2CAP_SIGNAL_EVENT 0xA2 //!< Incoming Signaling message
// HCI - Messages IDs (0x90 - 0x9F)
#define HCI_DATA_EVENT 0x90 //!< HCI Data Event message
#define HCI_GAP_EVENT_EVENT 0x91 //!< GAP Event message
#define HCI_SMP_EVENT_EVENT 0x92 //!< SMP Event message
#define HCI_EXT_CMD_EVENT 0x93 //!< HCI Extended Command Event message
#define HCI_SMP_META_EVENT_EVENT 0x94 //!< SMP Meta Event message
#define HCI_GAP_META_EVENT_EVENT 0x95 //!< GAP Meta Event message
// ICall and Dispatch - Messages IDs (0x80 - 0x8F)
#define ICALL_EVENT_EVENT 0x80 //!< ICall Event message
#define ICALL_CMD_EVENT 0x81 //!< ICall Command Event message
#define DISPATCH_CMD_EVENT 0x82 //!< Dispatch Command Event message
(6) GAP 消息处理
GAP 消息处理函数如下所示,可以看出也仅实现了GAP_DEVICE_INIT_DONE_EVENT 这个事件。这个事件是在函数 AtcBroadcaster_init() 中调用了 GAP_DeviceInit() 函数初始化当前设备为 广播者后,协议栈向应用任务出的事件标志,表示设备初始化完成。
/*********************************************************************
* @fn AtcBroadcaster_processGapMessage
*
* @brief Process an incoming GAP event.
*
* @param pMsg - message to process
*/
static void AtcBroadcaster_processGapMessage(gapEventHdr_t *pMsg)
{
switch(pMsg->opcode)
{
case GAP_DEVICE_INIT_DONE_EVENT:
{
bStatus_t status = FAILURE;
gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
if(pPkt->hdr.status == SUCCESS)
{
// Store the system ID
uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
// use 6 bytes of device address for 8 bytes of system ID value
systemId[0] = pPkt->devAddr[0];
systemId[1] = pPkt->devAddr[1];
systemId[2] = pPkt->devAddr[2];
// set middle bytes to zero
systemId[4] = 0x00;
systemId[3] = 0x00;
// shift three bytes up
systemId[7] = pPkt->devAddr[5];
systemId[6] = pPkt->devAddr[4];
systemId[5] = pPkt->devAddr[3];
// Set Device Info Service Parameter
DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
// Display device address
Display_printf(dispHandle, 1, 0, "%s Addr: %s",
(addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
Util_convertBdAddr2Str(pPkt->devAddr));
Display_printf(dispHandle, 2, 0, "Initialized");
// Setup and start Advertising
// For more information, see the GAP section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
// Temporary memory for advertising parameters. These will be copied
// by the GapAdv module
GapAdv_params_t advParamLegacy = GAPADV_PARAMS_LEGACY_SCANN_CONN;
#ifndef BEACON_FEATURE
advParamLegacy.eventProps = GAP_ADV_PROP_SCANNABLE | GAP_ADV_PROP_LEGACY;
#else
advParamLegacy.eventProps = GAP_ADV_PROP_LEGACY;
#endif // !BEACON_FEATURE
// Create Advertisement set
status = GapAdv_create(&AtcBroadcaster_advCallback, &advParamLegacy,
&advHandleLegacy);
AtcBROADCASTER_ASSERT(status == SUCCESS);
// Load advertising data
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
sizeof(advertData), advertData);
AtcBROADCASTER_ASSERT(status == SUCCESS);
// Load scan response data
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
sizeof(scanRspData), scanRspData);
AtcBROADCASTER_ASSERT(status == SUCCESS);
// Set event mask
status = GapAdv_setEventMask(advHandleLegacy,
GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
GAP_ADV_EVT_MASK_SET_TERMINATED);
AtcBROADCASTER_ASSERT(status == SUCCESS);
// Enable legacy advertising
status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
AtcBROADCASTER_ASSERT(status == SUCCESS);
}
break;
}
default:
Display_clearLine(dispHandle, 2);
break;
}
}
现在修改这个函数完成自定义数据的广播。
4、自定义信息广播实现
(1) 广播 PDU 类型
advParamLegacy.eventProps = GAP_ADV_PROP_SCANNABLE | GAP_ADV_PROP_LEGACY;
当前设置为不定向可扫描的蓝牙广告。
(2) 广播间隔
例程默认参数,
/// Default parameters for legacy, scannable, connectable advertising
#define GAPADV_PARAMS_LEGACY_SCANN_CONN { \
.eventProps = GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_SCANNABLE | \
GAP_ADV_PROP_LEGACY, \
.primIntMin = 160, \
.primIntMax = 160, \
.primChanMap = GAP_ADV_CHAN_ALL, \
.peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID, \
.peerAddr = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, \
.filterPolicy = GAP_ADV_WL_POLICY_ANY_REQ, \
.txPower = GAP_ADV_TX_POWER_NO_PREFERENCE, \
.primPhy = GAP_ADV_PRIM_PHY_1_MBPS, \
.secPhy = GAP_ADV_SEC_PHY_1_MBPS, \
.sid = 0 \
}
其中 primIntMin , primIntMax , 表示广播间隔 数值 1 代表 0.625ms,160 * 0.625ms = 100ms。这个参数保持默认不做修改。
参数其中还有, 广播通道,过滤,Phy 等相关设置,这里不做讨论。
(3) 修改广播内容
例程广播内容:
// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8 advertData[] =
{
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
#ifndef BEACON_FEATURE
// three-byte broadcast of the data "1 2 3"
0x04, // length of this data including the data type byte
GAP_ADTYPE_MANUFACTURER_SPECIFIC, // manufacturer specific adv data type
1,
2,
3
#else
// 25 byte beacon advertisement data
// Preamble: Company ID - 0x000D for TI, refer to https://www.bluetooth.org/en-us/specification/assigned-numbers/company-identifiers
// Data type: Beacon (0x02)
// Data length: 0x15
// UUID: 00000000-0000-0000-0000-000000000000 (null beacon)
// Major: 1 (0x0001)
// Minor: 1 (0x0001)
// Measured Power: -59 (0xc5)
0x1A, // length of this data including the data type byte
GAP_ADTYPE_MANUFACTURER_SPECIFIC, // manufacturer specific adv data type
0x0D, // Company ID - Fixed
0x00, // Company ID - Fixed
0x02, // Data Type - Fixed
0x15, // Data Length - Fixed
0x00, // UUID - Variable based on different use cases/applications
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // UUID
0x00, // Major
0x01, // Major
0x00, // Minor
0x01, // Minor
0xc5 // Power - The 2's complement of the calibrated Tx Power
#endif // !BEACON_FEATURE
};
修改为
// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8 advertData[] =
{
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
};
广播数据最长为 31 字节,一组有效广播数据第一个字节为当前数据的长度(长度包括数据类型 1 字节 和 荷载数据 n 字节),第二个字节为数据标志, 后面的数据为荷载数据。一组有效广播数据长度为 1 + 1 + n 字节。
当前广播数据共三字节, 0x02 表示这组数据的长度,GAP_ADTYPE_FLAGS 为广播标志,GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED表示不支持 BR/EDR, 即当前设备为 LE 蓝牙设备。
(4) 修改扫描回复数据
例程扫描恢复数据为:
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[] =
{
// complete name
0x15, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'S',
'i',
'm',
'p',
'l',
'e',
'B',
'L',
'E',
'B',
'r',
'o',
'a',
'd',
'c',
'a',
's',
't',
'e',
'r',
// Tx power level
0x02, // length of this data
GAP_ADTYPE_POWER_LEVEL,
0 // 0dBm
};
修改为:
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[] =
{
// complete name
0x0F, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'A', 't', 'c', 'B', 'r', 'o', 'a', 'd', 'c', 'a', 's', 't', 'e', 'r',
// User define String
0x08, // length of this data
GAP_ADTYPE_MANUFACTURER_SPECIFIC,
'A', 'T', 'C', 'T', 'e', 's', 't'
};
和广播数据一样,扫描回复数据最大也只能到 31 字节。第一组数据定义了蓝牙全名为“AtcBroadcaster”,第二组数据定义了自定义数据为“ATCTest”。
5、结果调试
编译程序并下载到 CC2640R2F平台。
使用手机APP可以看到,收到的广播数据。
可看到全名为 “AtcBroadcaster”,在Comoany一栏数据为 “41 54 43 54 65 73 74”
对照ASCII码表可译出为"ATCTest"。
五、总结
当前项目可自行下载。
蓝牙广播者,是蓝牙调试里最简单的角色,可以很快地的调试出可观测结果。下一篇试着使用定时器计数,测量芯片温度等数据来使用更新广播数据。