美文网首页物联网loT从业者物联网相关技术研究
CC2640R2F学习笔记(14)——GATT客户端读写特征值

CC2640R2F学习笔记(14)——GATT客户端读写特征值

作者: Leung_ManWah | 来源:发表于2019-04-09 08:58 被阅读18次

    一、背景

    1.1 GATT协议

    GATT(Generic Attributes Profile)的缩写,中文是通用属性协议,是已连接的低功耗蓝牙设备之间进行通信的协议。

    一旦两个设备建立起了连接,GATT 就开始起作用了,这也意味着,你必需完成前面的GAP协议。

    GATT使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service,Characteristic 对应的数据保存在一个查找表中,查找表使用 16bit ID 作为每一项的索引。

    GATT定义的多层数据结构简要概括起来就是 服务(Service) 可以包含多个 特征(Characteristic),每个特征包含 属性(Properties)值(Value),还可以包含多个 描述(Descriptor)

    1.2 属性协议(ATT)

    属性协议层 负责数据检索,允许一个设备暴露一些数据块给其他设备,其他设备称之为“属性”。

    在ATT环境中,展示属性的设备称之为服务器,与它配对的设备称之为客户端。链路层的主机从机和这里的服务器、客服端是两种概念,主设备既可以是服务器,也可以是客户端。从设备毅然。

    1.3 GATT通信中角色

    从GATT的角度来看,处于连接状态时的两个设备,它们各自充当两种角色中的一种:
    服务端(Server)
    包含被GATT客户端读取或写入的特征数据的设备。
    客户端(Client)
    从GATT服务器中读取数据或向GATT服务器写入数据的设备。

    外围设备(从机)作为 GATT 服务端(Server),它维持了 ATT 的查找表以及 service 和 characteristic 的定义;

    客户端和服务器的GATT角色独立于外围设备和中央设备的GAP角色。外围设备可以是GATT客户端或GATT服务器,中心可以是GATT客户端或GATT服务器

    二、配置读写特征值参数

    2.1 发现服务和特征相关结构体

    // GATT发现服务和特征时用
    // discovery information
    typedef struct
    {
      discState_t discState;            // discovery state
      uint16_t svcStartHdl;             // service start handle
      uint16_t svcEndHdl;               // service end handle
      uint16_t charHdl;                 // characteristic handle
    } discInfo_t;
    
    // 连接句柄表
    // entry to map index to connection handle and store address string for menu module
    typedef struct
    {
      uint16_t connHandle;              // connection handle of an active connection
      uint8_t strAddr[B_STR_ADDR_LEN];  // memory location for menu module to store address string
    } connHandleMapEntry_t;
    
    // pointer to allocate the connection handle map
    static connHandleMapEntry_t *connHandleMap;
    

    2.2 读写特征值相关变量

    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Value to write
    static uint8_t charVal = 0;
    

    2.3 初始化GATT客户端

    以SDK2.4 multi_role工程为例,在 multi_role_init() 初始化多角色应用程序函数中,

    /*==================================== 客户端 ====================================*/
    // 初始化GATT客户端
    VOID GATT_InitClient();
    
    // 注册GATT 本地事件和ATT响应等待传输
    GATT_RegisterForMsgs(selfEntity);
    
    // 注册当前任务为GATT的notify和indicate的接收端
    // 如果不注册,无法接收从机通过GATT_Notification发来的数据
    GATT_RegisterForInd(selfEntity);
    

    三、读取特征值

    3.1 流程

    建立连接,产生建立连接完成事件 GAP_LINK_ESTABLISHED_EVENT

    multi_role_startDiscovery() 开始发现

    multi_role_processGATTMsg() 处理GATT消息和事件,响应GATT发现

    multi_role_processGATTDiscEvent() 处理GATT发现事件,陆续更改发现状态

    发现服务/特征

    读取特征值

    3.2 读取特征值函数

    GATT发现服务和特征查看CC2640R2F学习笔记(13)——GATT客户端发现服务和特征

    在SDK2.4 multi_role工程中:
    根据mr_doGattRw函数重新定义一个读取特征值函数 mr_doGattRead()

    /**
     @brief 执行GATT读函数
     @param index 索引
     @return TRUE - 成功;FALSE - 失败
    */
    bool mr_doGattRead(uint8_t index)
    {
        bStatus_t status = FAILURE;
    
        // Create read request...place in CSTACK
        attReadReq_t req;
    
        req.handle = discInfo[index].charHdl;                       // 填充读请求包
    
        // Send read request. no need to free if unsuccessful
        // since the request is only placed in CSTACK; not allocated
        status = GATT_ReadCharValue(connHandleMap[index].connHandle, &req, selfEntity);
    
        return status;
    }
    

    3.3 执行读取特征值

    以SDK2.4 multi_role工程为例,在 multi_role_processGATTDiscEvent() GATT发现事件处理函数中,发现服务和特征完成后,加入读取特征值函数 mr_doGattRead()

    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    {
        ······
        ······
        /*--------------------------- 发现特征 ---------------------------*/
        else if(discInfo[connIndex].discState == BLE_DISC_STATE_CHAR)
        {
            if((pMsg->method == ATT_READ_BY_TYPE_RSP) &&                // 发现服务,存储句柄
                (pMsg->msg.readByTypeRsp.numPairs > 0))
            {
                discInfo[connIndex].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[3],
                                                            pMsg->msg.readByTypeRsp.pDataList[4]);
    
                    /* Display_print0(dispHandle, MR_ROW_STATUS1, 0, "Simple Svc Found"); */
            }
    
            mr_doGattRead(connIndex);                                     // 读取特征值
        }
    }
    

    四、写入特征值

    4.1 流程

    建立连接,产生建立连接完成事件 GAP_LINK_ESTABLISHED_EVENT

    multi_role_startDiscovery() 开始发现

    multi_role_processGATTMsg() 处理GATT消息和事件,响应GATT发现

    multi_role_processGATTDiscEvent() 处理GATT发现事件,陆续更改发现状态

    发现服务/特征

    写入特征值

    3.2 写入特征值函数

    在SDK2.4 multi_role工程中:
    根据mr_doGattRw函数重新定义一个读取特征值函数 mr_doGattWrite()

    /**
     @brief 执行GATT写函数
     @param index 索引
     @return TRUE - 成功;FALSE - 失败
    */
    bool mr_doGattWrite(uint8_t index)
    {
        bStatus_t status = FAILURE;
    
        if(discInfo[index].charHdl != 0)                                    // 如果已经发现特征
        {
            attWriteReq_t req;
    
            req.pValue = GATT_bm_alloc(connHandleMap[index].connHandle,     // 为写请求分配空间
                                        ATT_WRITE_REQ, 1, NULL);
    
            if(req.pValue != NULL)
            {
                req.handle = discInfo[index].charHdl;                       // 填充写请求包
                req.len = 2;
                req.pValue[0] = charVal;  // 写入的特征值(1字节),如果多字节则添加多个req.pValue
                req.sig = 0;
                req.cmd = 0;
                                                                            // 发送GATT写请求给控制器
                status = GATT_WriteCharValue(connHandleMap[index].connHandle, &req, selfEntity);
    
                if(status != SUCCESS)                                       // 发送失败
                {
                    GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);         // 释放空间
                    return FALSE;
                }
            }
        }
        return TRUE;
    }
    

    4.3 执行写入特征值

    以SDK2.4 multi_role工程为例,在 multi_role_processGATTDiscEvent() GATT发现事件处理函数中,发现服务和特征完成后,加入写入特征值函数 mr_doGattWrite()

    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    {
        ······
        ······
        /*--------------------------- 发现特征 ---------------------------*/
        else if(discInfo[connIndex].discState == BLE_DISC_STATE_CHAR)
        {
            if((pMsg->method == ATT_READ_BY_TYPE_RSP) &&                // 发现服务,存储句柄
                (pMsg->msg.readByTypeRsp.numPairs > 0))
            {
                discInfo[connIndex].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[3],
                                                            pMsg->msg.readByTypeRsp.pDataList[4]);
    
                    /* Display_print0(dispHandle, MR_ROW_STATUS1, 0, "Simple Svc Found"); */
            }
    
            mr_doGattWrite(connIndex);                                     // 写入特征值
        }
    }
    

    • 由 Leung 写于 2019 年 4 月 9 日

    • 参考:simplelink_cc2640r2_sdk_2_40_00_32 [提取码:3pg6]

    相关文章

      网友评论

        本文标题:CC2640R2F学习笔记(14)——GATT客户端读写特征值

        本文链接:https://www.haomeiwen.com/subject/whspiqtx.html