GATTServApp模块

GATT服务器应用程序(GATTServApp)存储和管理应用程序范围的属性表。各种配置文件使用此模块将其特性添加到属性表。蓝牙低功耗协议栈使用此模块来响应GATT客户端的发现请求。例如,GATT客户端可以发送Discover all Primary Characteristics消息。GATT服务器端的蓝牙低功耗协议栈接收到该消息,并使用GATTServApp查找并发送存储在属性表中的所有主要特性。这种类型的功能超出了本文档的范围,它们是在库代码中实现。可以从gattservapp_util.c中定义配置文件以及BLE Stack API Reference(GATTServApp部分)中描述的API来查看GATTServApp函数。这些功能包括查找特定属性和阅读或修改客户端特征配置。有关GATTServApp在应用程序中的功能的示例,请参见图46 .

下图示意了属性表的初始化,这幅图其实完全对应了GATT Services和Profile中的属性表。该流程图是程序上下文的具体实现过程。图中可以看出在程序中分别使用前面涉及的GGS_Addservice();GATTServApp_AddService();DevInfo_AddService();SimpleProfile_AddService();就这样一步一步在GATT Server中构建了属性表。

构建属性表

上电或重置时,应用程序使用GATTServApp来创建GATT表来添加服务。每个服务都包含具有UUID,值,权限以及读取和写入回调的属性列表。正如图46所示,所有的这些信息是通过GATTServApp传递到GATT和存储在堆栈中。
属性表初始化必须在应用程序初始化函数中出现,也就是simple_peripheral_init()。

 //初始化GATT属性GGS_AddService (GATT_ALL_SERVICES );          // GAP GATTServApp_AddService (GATT_ALL_SERVICES );  // GATT属性

在属性表中实现配置文件

属性表的定义

GATT属性的每个服务或组必须定义一个固定大小的属性表,该表被传递到GATT中。simple_gatt_profile.c中的此表定义如下。

 static  gattAttribute_t  simpleProfileAttrTbl [ SERVAPP_NUM_ATTR_SUPPORTED ]

此表中的每个属性都是以下类型。

 typedef struct attAttribute_t{gattAttrType_t type; //!< 属性类型(2或16个八位字节UUID)uint8 permissions;   //!< 属性权限uint16 handle;        //!< 属性句柄-由属性服务器内部分配uint8* const pValue; //!< 属性值 -属性值的最大长度为512 octets。} gattAttribute_t;
  • gattAttrType_t类型
    type是与放置在表中的属性相关联的UUID。gattAttrType_t本身定义为:
 typedef  struct { uint8  len ;          //!<UUID的长度(2或16)const  uint8  * uuid ;  //!<指向UUID的指针}  gattAttrType_t ;

其中长度可以是ATT_BT_UUID_SIZE(2字节)或 ATT_UUID_SIZE(16字节)。*uuid是指向蓝牙SIG(定义在gatt_uuid.c中)或在配置文件中定义的自定义UUID保留的数字的指针。

  • uint8权限
    强制GATT客户端设备如何以及如何访问该属性的值。可能的权限在gatt.h中定义如下:

    宏定义 16进制数 权限
    #define GATT_PERMIT_READ 0x01 可读
    #define GATT_PERMIT_WRITE 0x02 可写
    #define GATT_PERMIT_AUTHEN_READ 0x04 认证后可读
    #define GATT_PERMIT_AUTHEN_WRITE 0x08 认证后可写
    #define GATT_PERMIT_AUTHOR_READ 0x10 授权后可读
    #define GATT_PERMIT_AUTHOR_WRITE 0x20 授权后可写
    #define GATT_PERMIT_ENCRYPT_READ 0x40 读需要加密
    #define GATT_PERMIT_ENCRYPT_WRITE 0x80 写需要加密

    这里的认证、授权、加密后面GATT内存分配部分还会详细说明。

  • uint16 handle
    handle不可修改,由属性服务器内部分配(顺序分配)。

  • uint8 * const pValue
    pValue是指向属性值的指针。初始化后大小无法更改。最大大小为512个字节。

Service声明

参考下面simple_gatt_profile service声明

 // Simple Profile Service { {  ATT_BT_UUID_SIZE , primaryServiceUUID  },    // type GATT_PERMIT_READ ,                               // permissions 0 ,                                              // handle (uint8  * )&simpleProfileService                // pValue }

其中type设置的是蓝牙SIG规范定义的主服务UUID(0x2800)。权限为可读(GATT_PERMIT_READ),允许客户端读取该服务。pValue是指向服务的UUID的指针,自定义为0xFFF0。

 // Simple Profile Service属性static  CONST  gattAttrType_t  simpleProfileService  = { ATT_BT_UUID_SIZE ,simpleProfileServUUID };

characteristic Declaration

参考下面simple_gatt_profile Characteristic1 Declaration。

 //特征1声明{ {  ATT_BT_UUID_SIZE , characterUUID  }, // type GATT_PERMIT_READ ,                       // permissions 0 ,                                      // handle &simpleProfileChar1Props                 // pValue },

type设置为蓝牙SIG定义的characteristic UUID(0x2803)。权限为可读(GATT_PERMIT_READ),允许客户端读取该服务。对于characteristic Declaration中的pValue值,传递给GATTServApp,GATTServApp只需要characteristic Value的权限,所以在characteristic Declaration的pValue中值传递了characteristic Value的权限,可读可写(0x0A)。GATTServApp会自动构建characteristic value的handle和UUID。放入characteristic Declaration的pValue的2-4个字节中。

注意:characteristic Declaration中的pValue和characteristic Value中的权限是有区别的,前者是GATT客户端可以读取看见的属性,后者是characteristic Value的真实权限,这意味着他们必须要一致,不然会混淆GATT客户端的开发者。

客户端Characteristic Configuration

这部分首先要明白一点,前面说的characteristic value 1,2,3,5都是客户端发送读写命令,然后服务端响应处理然后返回,但是characteristic4 value不具有读写权限,他是通知属性,意思就是characteristic4 value只能是服务端发送给客户端。下面代码设置了characteristic config 的可读可写属性,所以客户端可以配置characteristic4 value,比如当前就是禁用通知。客户端可以设置characteristic config 的值来控制通知属性。

参考simple_gatt_profile中Characteristic4配置。

      // Characteristic Value 4{ { ATT_BT_UUID_SIZE, simpleProfilechar4UUID },0, 0, &simpleProfileChar4 },//Characteristic 4 configuration{ {  ATT_BT_UUID_SIZE , clientCharCfgUuID  },GATT_PERMIT_READ  |  GATT_PERMIT_WRITE ,0 ,(uint8  * )&simpleProfileChar4Config },

添加服务功能

如GATTServApp 模块所述,应用启动时,需要添加支持的GATT服务。每个配置文件都需要一个全局AddService函数,用于被应用程序调用。其中一些服务在协议栈中定义,如GAP GATT服务和GATT服务。用户定义的服务必须公开自己的AddService函数,该应用程序可以调用配置文件初始化。以SimpleProfile_AddService()为例,这些功能应该如下。

  • 为客户端特征配置(CCC)阵列分配空间。作为示例,指向这些数组之一的指针在配置文件中初始化,如client_characteristic_configuration中所述。
    在AddService函数中,声明支持的连接,并为每个数组分配内存。只有一个CCC在simple_gatt_profile中定义,但可以有多个CCC。当有多个设备连接服务端,服务端的simpleProfileChar4Config必须为每个连接了的客户端分配一个内存,用于为每个设备保存通知配置。
     //分配客户端特征配置表simpleProfileChar4Config  =  (gattCharCfg_t  * )ICall_malloc (sizeof (gattCharCfg_t ) *  linkDBNumConns  );if  ( simpleProfileChar4Config  ==  NULL  ){ return  ( bleMemAllocError  ); }
  • 初始化CCC数组。CCC值在掉电之后也能够保存下来,使用下面函数初始化CCC值。可以在连接设备时尝试使用之前保存的值,如果连接设备是新的设备则使用默认值。
 `GATTServApp_InitCharCfg ( INVALID_CONHANDLE , simpleProfileChar4Config  );`
  • 使用GATTServApp_RegisterService注册该配置文件。该函数功能是将配置文件的属性表传递给GATTServApp,以便将配置文件的属性添加到由协议栈管理的应用程序范围的属性表中,并为每个属性分配句柄。这也将配置文件的回调指针传递给堆栈,以启动GATTServApp和配置文件之间的通信。
 //Register GATT attribute list and CBs with GATT Server Appstatus =  GATTServApp_RegisterService ( simpleProfileAttrTbl ,GATT_NUM_ATTRS ( simpleProfileAttrTbl  ),GATT_MAX_ENCRYPT_KEY_SIZE ,&simpleProfileCBs  );

注册应用程序回调函数

配置文件可以使用回调将邮件中继到应用程序。在simple_peripheral项目中,只要GATT客户端写入特征值,simple_gatt_profile将调用应用程序回调。对于要使用的这些应用程序回调,配置文件必须定义一个注册应用程序回调函数,该应用程序在初始化期间用于设置回调。simple_gatt_profile的注册应用程序回调函数如下:

     bStatus_t  SimpleProfile_RegisterAppCBs ( simpleProfileCBs_t  * appCallbacks  ){ if  ( appCallbacks  ){ simpleProfile_AppCBs  =  appCallbacks ; 返回 ( SUCCESS  ); }else { return  ( bleAlreadyInRequestedMode  ); } }

其中回调typedef被定义如下。

     typedef  struct { simpleProfileChange_t  pfnSimpleProfileChange ;  //当特征值更改时调用}  simpleProfileCBs_t ;

然后,应用程序必须定义此类型的回调,并将其传递给具有SimpleProfile_RegisterAppCBs()函数的simple_gatt_profile。这发生在simple_peripheral.c中,如下所示。

 //简单的GATT配置文件回调#ifndef FEATURE_OAD_ONCHIP static  simpleProfileCBs_t  SimpleBLEPeripheral_simpleProfileCBs  = { SimpleBLEPeripheral_charValueChangeCB  //特征值更改回调}; #endif //!FEATURE_OAD_ONCHIP// ...//使用SimpleGATTprofile注册回调SimpleProfile_RegisterAppCB (&SimpleBLEPeripheral_simpleProfileCBs );

上述操作完成之后,一旦characteristic中value改变,SBP_CHAR_CHANGE_EVT事件被更新,应用程序就可以收到并处理改变的值。

客户端读请求

如图所示当接收到来自GATT客户端的读取请求时,协议栈检查属性的权限,如果属性可读,则调用Profile读回调函数。Profile复制该值,并在profile层执行特定处理,由ATT_ReadRsp指令发送给协议栈,协议栈使用ATT_READ_RSP发送给客户端。

客户端写请求

当收到来自GATT客户端的给定属性的写请求时,协议栈会检查属性的权限,如果该属性是允许写的,则调用该配置文件的回调。配置文件存储要写入的值,执行profile层响应的处理,并在需要时通知应用程序。下图显示了simple_gatt_profile中simpleprofileChar3的写入过程。

注意:协议栈程序最小化处理非常重要,在该示例中,应用程序通过消息队列的方式,在应用程序上下文中处理value改变之后额外的处理过程。

获取和设置函数

包含characteristic的配置文件应为应用程序提供set和get抽象功能,以读取和写入配置文件的characteristic。设置参数功能还包括如果相关characteristic有通知或指示属性,则检查并实现通知和指示的逻辑。下面的显示了在simple_gatt_profile中设置simpleProfileChacteristic4的这个例子。

例如,应用程序通过以下方法在simple_peripheral.c中将simpleProfileCharacteristic4初始化为0。

 uint8_t charValue4 = 0;SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t), &charValue4);

此函数的代码显示在以下代码片段中(来自simple_gatt_profile.c)。除了设置静态simpleProfileChar4的值之外,该函数还调用 GATTServApp_ProcessCharCfg(),因为它具有通知属性。此操作会强制GATTServApp检查GATT客户端是否已启用通知。如果是这样,GATTServApp会向GATT客户端发送此属性的通知。

 bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value ){bStatus_t ret = SUCCESS switch ( param ){case SIMPLEPROFILE_CHAR4:if ( len == sizeof ( uint8 ) ){simpleProfileChar4 = *((uint8*)value);// See if Notification has been enabledGATTServApp_ProcessCharCfg( simpleProfileChar4Config, &simpleProfileChar4, FALSE,simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ),INVALID_TASK_ID, simpleProfile_ReadAttrCB );}

队列写

当接收到多个写指令是,GATT服务端通过排队方式处理更多的有效数据。默认队列大小为5.默认MTU为23,有效载荷为18字节,最多可以接收90个字节的有效载荷。有关排队写入的更多信息,请参阅蓝牙核心规范版本5.0的排队写入部分([Vol 3],F部分,第3.4.6节)。

使用GATTServApp_SetParameter()与参数调整队列大小GATT_PARAM_NUM_PREPARE_WRITES。没有指定的限制,但它由可用的HEAPMGR空间限定。

为GATT程序分配内存

GATT和ATT有效载荷结构必须动态分配内存。例如,发送GATT_Notification时必须分配缓冲区。这里有两种方法,一种是上面讲过的首选方式调用SimpleProfile_SetParameter();一种是直接调用如果使用发送GATT通知或指示的首选方法,一种是直接使用GATT_Notification()或GATT_Indication();其实本质上SimpleProfile_SetParameter()也是调用的GATT_Notification()或GATT_Indication()。

如果直接使用GATT_Notification()或GATT_Indication(),则必须如下添加内存管理(gattServApp_SendNotiInd中)。

  1. 尝试使用GATT_bm_alloc()为通知或指示有效载荷分配内存。
  2. 如果分配成功,则使用GATT_Notification()或 GATT_Indication()发送通知或指示。

注意:如果通知或指示的返回值为SUCCESS (0x00),则堆栈释放内存。

  1. 如果返回值不是SUCCESS,则使用GATT_bm_free()来释放内存。以下是GATTServApp_SendNotiInd gattservapp_util.c文件中函数的一个示例。
     noti.pValue = (uint8 *)GATT_bm_alloc( connHandle, ATT_HANDLE_VALUE_NOTI, GATT_MAX_MTU, &len );if ( noti.pValue != NULL ){status = (*pfnReadAttrCB)( connHandle, pAttr, noti.pValue, &noti.len, 0, len, GATT_LOCAL_READ );if ( status == SUCCESS ){noti.handle = pAttr->handle;if ( cccValue & GATT_CLIENT_CFG_NOTIFY ){status = GATT_Notification( connHandle, &noti, authenticated );}else // GATT_CLIENT_CFG_INDICATE{status = GATT_Indication( connHandle, (attHandleValueInd_t *)&noti, authenticated, taskId );}}if ( status != SUCCESS ){GATT_bm_free( (gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI );}}else{status = bleNoResources;}

注册接收应用程序中的其他GATT事件

使用GATT_RegisterForMsgs(),可以接收额外的GATT消息来处理某些角落情况。这种情况可以在SimpleBLEPeripheral_processGATTMsg()中看到。目前处理以下三种情况。

  • 堆栈中的GATT服务器无法发送ATT响应(由于缺少可用的HCI缓冲区):尝试在下一个连接间隔发送。另外,如果在蓝牙核心规范版本5.0中指定的ATT事务在30秒内未完成,则会发送bleTimeout状态。
     // See if GATT server was unable to transmit an ATT responseif (pMsg->hdr.status == blePending){//No HCI buffer was available. Let's try to retransmit the response//on the next connection event.if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity, SBP_CONN_EVT_END_EVT) == SUCCESS){//First free any pending responseSimpleBLEPeripheral_freeAttRsp(FAILURE);//Hold on to the response message for retransmissionpAttRsp = pMsg;//Don't free the response message yetreturn (FALSE);}}
  • ATT流量控制违规:通知应用程序连接的设备违反ATT流量控制规范,例如发送指示确认之前发送读取请求。在连接过程中,无需更多的ATT请求或指示。应用程序可能希望由于此违规而终止连接。作为simple_peripheral的一个例子,display被更新。
     else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT){//ATT request-response or indication-confirmation flow control is//violated. All subsequent ATT requests or indications will be dropped.//The app is informed in case it wants to drop the connection.//Display the opcode of the message that caused the violation.DISPLAY_WRITE_STRING_VALUE("FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode, LCD_PAGE5);}
  • 更新ATT MTU大小:通知应用程序,以防任何方式影响其处理。有关MTU 的更多信息,请参见最大传输单元(MTU)。作为simple_peripheral的一个例子,display被更新。
     else if (pMsg->method == ATT_MTU_UPDATED_EVENT){// MTU size updatedDISPLAY_WRITE_STRING_VALUE("MTU Size: $d", pMsg->msg.mtuEvt.MTU, LCD_PAGE5);}

© Copyright 2017, 成都乐控畅联科技有限公司.

CC2640R2F BLE5.0 蓝牙协议栈GATTServApp模块相关推荐

  1. CC2640R2F BLE5.0 蓝牙协议栈GAP Bond管理和LE安全连接

    GAP Bond管理和LE安全连接 GAP Bond Manager是一个可配置的模块,使用Bond manager后应用程序可以减少大部分安全机制.下表列出了术语. 术语 描述 配对(Pairing ...

  2. CC2640R2F BLE5.0 蓝牙协议栈Off-Chip OAD功能

    Off-Chip OAD 本节描述了分离镜像的片外OAD(Split Image Off-Chip OAD)和片外OAD库(Library off-chip OAD)之间的差异.片外OAD利用一个外部 ...

  3. CC2640R2F BLE5.0 蓝牙协议栈GAP GATT 服务(GGS)

    GAP GATT 服务(GGS) 在前面GATT服务和简介中说过,GGS服务包含设备和访问信息,例如设备名称,Appearance,外围首选连接参数.GGS的目的是在设备发现和连接启动过程中进行辅助. ...

  4. CC2640R2F BLE5.0 蓝牙协议栈信道选择算法#2(CSA#2)

    概要 信道选择算法#2(CSA#2)是更复杂和更难跟踪用于获得下一个连接事件的信道索引的算法.特别是在高通量使用情况下,避免干扰和多路径衰落效应比通道选择算法#1更有效. 有关算法的详细说明,请参见蓝 ...

  5. CC2640R2F BLE5.0 蓝牙协议栈OAD功能概述

    OAD 概述 本章节旨在从更高层次解释OAD主要概念,这些概念将在下一章节进一步扩展.一些概念,例如Boot镜像管理(BIM)在具体实现细节上可能有所不同.但是本章尽可能的覆盖这些概念,下一章节解释它 ...

  6. 低功耗蓝牙onscanresult 出来名字为空_浅析BLE5.0蓝牙模块在智能家居的运用

    随着物联网渗透到各个行业领域,也使得各个领域对无线模块BLE蓝牙模块的需求激增.BLE蓝牙模块作为无线通信模块的一种,目前在智能家居.智能穿戴.智慧医疗.蓝牙室内定位等领域已经得到广泛应用.今天,小编 ...

  7. CC2640R2F BLE5.0 CC2640R2F软件架构

    软件架构 开发模型 下图展示CC2640R2F支持的两种开发模型,本文讲解都集中在图一,也就是整个ble协议栈和应用都工作在一个CC2640R2F的单SOC解决方案. 单一设备:Controller. ...

  8. CC2640R2F BLE5.0 CC2640R2F UART驱动

    UART驱动 这一节我们讲一下UART驱动的分层实现,UART APIs以及如何调用UART APIs来实现基本的串口打印. 概述 UART用于芯片和串行端口之间的数据传输,UART驱动程序经过多层的 ...

  9. CC2640R2F BLE5.0 CC2640R2BLE5.0开发文档

    CC2640R2&BLE5.0开发 关于我们 关于我们 开发板介绍 文章所有代码.工具.文档开源.加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5. ...

最新文章

  1. CUDA C++编程接口:编译
  2. 芯讯通1月28号晚上八点直播-C-V2X产业链生态思考,关注易贸智慧互联公众号免费收听...
  3. Java虚拟机学习(4):JDK可视化监控工具
  4. abp 打包部署到ubuntu_如何在Ubuntu中安装Docker和运行 Docker容器
  5. java 固定长度队列_如何彻底搞懂 Java 数据结构?|CSDN 博文精选
  6. 「每天一道面试题」对象和GC Roots引用链没连接时一定会被回收吗
  7. 移动端前端开发技术概况
  8. Linux学习笔记——~/.bash_profile文件
  9. 粘胶活化剂市场现状及未来发展趋势
  10. 炒鸡福利:买云服务送智能摄像头
  11. Zhong__PyCharm配置豆瓣源提升插件、依赖安装速度
  12. netcat 使用方法
  13. macOS - Cocoa开发之沙盒机制及访问Sandbox之外的文件
  14. 系统提示内部服务器错误是怎么回事,XP系统提示“HTTP500内部服务器错误”是怎么回事...
  15. VMware安装Redhat虚拟机步骤
  16. 刚子扯谈:源于Chanel的图片描述
  17. 【detectron2】detectron2在ubuntu16.04系统下安装报错问题
  18. 用户界面设计黄金三原则
  19. 安装Tecplot360版
  20. Java API(十五):Queue队列、Deque栈、Map集合

热门文章

  1. ubuntu 配置登录失败次数限制
  2. CodeBlocks 主题美化(编辑器修改主题)
  3. 云呐资产|令人满意的固定资产盘点系统方法
  4. 去中心化和中心化哪个才是未来,Dex.top教你熊市生存指南
  5. 测试ip 或者 ip+端口 是否畅通
  6. javaSE探赜索隐四<基本排序算法>
  7. c语言运行excel中vba程序,VBA代码在WPS上可运行,在EXCEL中报错
  8. 10000marker_为什么跑全基因组dna时为什么用10000bp的marker
  9. Excel怎么将表格中的所有空白行删除
  10. 微信是一个计算机信息系统么,电脑微信不登录也能收到信息怎么办啊