CC2642的GGS使用笔记

编程入门 行业动态 更新时间:2024-10-11 09:23:44

CC2642的GGS使用<a href=https://www.elefans.com/category/jswz/34/1770047.html style=笔记"/>

CC2642的GGS使用笔记

一、前言
我们了解BLE的GATT之前需要了解一些基本的概念:
(1)Profile,字面意思简介、概述、形象印象、轮廓、配置文件,在BLE中,我们可能把它理解成配置文件较好,Profile有一些是BLE SIG规定的,有些可以是我们自定义的。
(2)Service,字面意思服务,在GATT中可以有多个服务,同样地,服务有些是BLE SIG定义的,有些是我们自定义的,习惯性叫这些服务为GATT服务。
(3)Characteristic, 字面意思特征,它存在于Service里面,就好比房间是一个服务,里面的装饰配置之类的是特征,而这些特征决定了房间的用处,也就是服务的用处。
(4)Attribute,字面意思是属性,可以这么理解,它描述的是特征的细节,比如描述特征的读写权限,特征的内容(值),特征的简介描述,还有特征的配置。
二、GGS相关的API
  下面列举几个我们GGS常用的协议栈API:

  • bStatus_t GGS_SetParameter( uint8 param, uint8 len, void *value )
    描述:设置GAP GATT服务参数
    参数
      param: Profile参数ID, 取的值可以参考gapgattserver.h的宏定义,其实就是下面表格列出的值:
GGS_DEVICE_NAME_ATT
GGS_APPEARANCE_ATT
GGS_DEVICE_NAME_ATT
GGS_APPEARANCE_ATT
GGS_PERI_CONN_PARAM_ATT
GGS_CENT_ADDR_RES_ATT
GGS_RESOLVABLE_PRIVATE_ADDRESS_ONLY_ATT

  len: 要写入的字节个数
  value: 指向要写的数据
  比如我们在APP的初始化处初始蓝牙设备的名称,可以这么写:

    uint8_t Ble_deviceName[] = "BoBo";GGS_SetParameter(GGS_DEVICE_NAME_ATT, strlen(Ble_deviceName), Ble_deviceName);
  • bStatus_t GAP_SetParamValue(uint16_t paramID, uint16_t paramValue)
    描述:设置GAP参数值
    参数
      paramID, 这个参数可以参考Gap_ParamIDs_t枚举
      paramValue, 值
    bStatus_t GGS_AddService( uint32 services )
    描述:添加一个功能到GGS上
    参数:services,该参数是32位,每个位代表一个功能服务,较常用的是bit1,也就是GAP_SERVICE。
  • bStatus_t GATTServApp_AddService( uint32 services )
    描述:添加一个功能到GATT服务上,我们将一个服务注册到GATT服务上之后,在蓝牙建立连接过程中客户端就能发现该服务,如果注册的回调函数功能正常的话,就能够正常的使用该服务。
    参数
      services,该参数是32位,每个位代表一个功能服务,大多数情况下推荐0xFFFFFFFF,即GATT_ALL_SERVICES
  • bStatus_t GATTServApp_RegisterService( gattAttribute_t *pAttrs, uint16 numAttrs, uint8 encKeySize, CONST gattServiceCBs_t *pServiceCBs )
    描述:向GATT服务应用注册服务的属性列表和回调函数
    参数
      pAttrs, 指向属性列表的指针
      numAttrs, 属性单元个数
      encKeySize, 服务所需的最小加密钥匙字节大小(7~16个字节)
      pServiceCBs, 指向服务回调函数的指针

这个API很重要,我们有必要看看结构体gattAttribute_t和gattServiceCBs_t的原型:

typedef struct attAttribute_t
{/// @brief GATT Attribute Type format.struct{uint8 len;         //!< UUID的长度const uint8 *uuid; //!< 指向UUID} type; //!< 属性类型 (2或者16字节UUIDs)uint8 permissions;    //!< 属性的权限uint16 handle;       //!< 属性的句柄 - 由属性服务器内部分配uint8* const pValue; //!< 属性值 - 字节个数不超过512
} gattAttribute_t;typedef struct
{pfnGATTReadAttrCB_t pfnReadAttrCB;           //!< 指向读ATT函数pfnGATTWriteAttrCB_t pfnWriteAttrCB;         //!< 指向写ATT函数pfnGATTAuthorizeAttrCB_t pfnAuthorizeAttrCB; //!< 指向认证函数
} gattServiceCBs_t;

上面三个函数类型如下:

typedef bStatus_t (*pfnGATTReadAttrCB_t)( uint16 connHandle, gattAttribute_t *pAttr,uint8 *pValue, uint16 *pLen, uint16 offset,uint16 maxLen, uint8 method );
typedef bStatus_t (*pfnGATTWriteAttrCB_t)( uint16 connHandle, gattAttribute_t *pAttr,uint8 *pValue, uint16 len, uint16 offset,uint8 method );
typedef bStatus_t (*pfnGATTAuthorizeAttrCB_t)( uint16 connHandle, gattAttribute_t *pAttr,uint8 opcode );
  • bStatus_t GATTServApp_ProcessCharCfg( gattCharCfg_t *charCfgTbl, uint8 *pValue, uint8 authenticated, gattAttribute_t *attrTbl, uint16 numAttrs, uint8 taskId, pfnGATTReadAttrCB_t pfnReadAttrCB )
    描述:处理客户端特征配置的改变
    参数
      charCfgTbl,特征配置表
      pValue, 指向属性内容
       authenticated, 是否需要认证
      attrTbl, 服务属性列表
      numAttrs, 服务属性列表单元个数
      taskId, 确认通知的任务ID
      pfnReadAttrCB, Att读回调函数,GATTServApp_ProcessCharCfg最后会通过该函数给客户端发送数据
  • bStatus_t GATTServApp_ProcessCCCWriteReq( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 len, uint16 offset, uint16 validCfg )
    描述:处理客户端特征配置写请求,这个函数配合UUID 0x2902使用
    参数
      connHandle, 客户端的连接句柄
      pAttr, 指向属性表
      pValue, 指向写入的数据
      Len, 待写的字节数
      offset,偏移字节写入
      validCfg, 配置选项,值可以是GATT_CLIENT_CFG_NOTIFY或者GATT_CLIENT_CFG_INDICATE,或者两者都选(或逻辑)。

三、实现一个数据透传服务
  下面写一个服务,两个特征,一个支持写,一个支持读和notify

直接上代码:

  • icce_service.c
/** icce_service.c**  Created on: 2023年4月15日*      Author: 28596*/
/********************************************************************** INCLUDES*/
#include <string.h>//#include <xdc/runtime/Log.h> // Comment this in to use xdc.runtime.Log
#include <ti/common/cc26xx/uartlog/UartLog.h>  // Comment out if using xdc Log#include <icall.h>/* This Header file contains all BLE API and icall structure definition */
#include "icall_ble_api.h"#include "icce_service.h"
#define DataServiceUUID 0xFFF5
#define DataWrite_UUID 0x0001
#define DataRead_UUID  0x0002
#define ATT_BT_UUID_SIZE 2
#define u8DATALEN     247
#define DATA_SERVICE_SERV_UUID_BASE16(uuid) LO_UINT16(uuid), HI_UINT16(uuid)#define DS_UUID_BASE16(uuid)  LO_UINT16(uuid), HI_UINT16(uuid)static uint8_t wCharact_Props = GATT_PROP_WRITE;
static uint8_t rnCharact_Props = GATT_PROP_NOTIFY;static uint8_t ds_icall_rsp_task_id = INVALID_TASK_ID;
// Write Characteristic Value
static uint8_t LOC_InputVal[u8DATALEN] = {0};
static uint8_t LOC_InputValLen = 0;
static uint8_t LOC_OutputVal[u8DATALEN] = {0};
static uint8_t LOC_OutputValLen = 0;
static ICCEDataServiceCBs_t *pAppCBs = NULL;
// Notify Characteristic 客户端特征配置描述符
static gattCharCfg_t *NotifyCharactConfig;//占用字节大小由可连接设备个数决定
// 数据服务的服务UUID Service UUID
CONST uint8_t icce_DataServiceUUID[ATT_BT_UUID_SIZE] =
{DATA_SERVICE_SERV_UUID_BASE16(DataServiceUUID)
};
// 数据服务之下的写UUID
CONST uint8_t wUserCharact_UUID[ATT_BT_UUID_SIZE] =
{DS_UUID_BASE16(DataWrite_UUID)
};
// 数据服务之下的读&Notify UUID
CONST uint8_t rnUserCharact_UUID[ATT_BT_UUID_SIZE] =
{DS_UUID_BASE16(DataRead_UUID)
};/* 属性类型的定义(UUID的字节长度,UUID)*/
static CONST gattAttrType_t LOC_DataServiceDecl = { ATT_BT_UUID_SIZE, icce_DataServiceUUID };
/* 服务列表 */
static gattAttribute_t ICCE_Data_ServiceAttrTbl[] =
{// 数据服务服务什么{{ ATT_BT_UUID_SIZE, primaryServiceUUID },//2800GATT_PERMIT_READ,0,(uint8_t *)&LOC_DataServiceDecl},// 写特征的申明{{ ATT_BT_UUID_SIZE, characterUUID },//2803GATT_PERMIT_READ,0,&wCharact_Props},// 写特征的内容{{ ATT_BT_UUID_SIZE, wUserCharact_UUID },GATT_PERMIT_WRITE,0,LOC_InputVal},// 读/通知特征的声明{{ ATT_BT_UUID_SIZE, characterUUID },GATT_PERMIT_READ,0,&rnCharact_Props},// 读/通知特征的内容{{ ATT_BT_UUID_SIZE, rnUserCharact_UUID },GATT_PERMIT_WRITE,//写权限允许,数组可以更改0,LOC_OutputVal},// 通知特征的 CCCD{{ ATT_BT_UUID_SIZE, clientCharCfgUUID },//0x2902GATT_PERMIT_READ | GATT_PERMIT_WRITE,0,(uint8_t *)&NotifyCharactConfig},
};/********************************************************************** 静态函数声明*/
static bStatus_t ICCE_Data_Service_ReadAttrCB(uint16_t connHandle,gattAttribute_t *pAttr,uint8_t *pValue,uint16_t *pLen,uint16_t offset,uint16_t maxLen,uint8_t method);
static bStatus_t ICCE_Data_Service_WriteAttrCB(uint16_t connHandle,gattAttribute_t *pAttr,uint8_t *pValue,uint16_t len,uint16_t offset,uint8_t method);/********************************************************************** 配置文件的回调*/
CONST gattServiceCBs_t LOC_ICCE_Data_ServiceCBs =
{ICCE_Data_Service_ReadAttrCB,ICCE_Data_Service_WriteAttrCB,NULL
};/** ICCE数据服务添加函数 - 向Gatt服务器里注册Gatt属性* 传参:rspTaskId - ICALL任务ID*/
extern bStatus_t ICCE_DataService_AddService(uint8_t rspTaskId)
{uint8_t status;// 初始化,为客户端特征配置表动态分配一块内存NotifyCharactConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns);if(NotifyCharactConfig == NULL){return(bleMemAllocError);}// 初始化客户端特征配置属性,默认设置为无效GATTServApp_InitCharCfg(LINKDB_CONNHANDLE_INVALID, NotifyCharactConfig);// 向GATT服务器添加我们写好的服务status = GATTServApp_RegisterService(ICCE_Data_ServiceAttrTbl,GATT_NUM_ATTRS(ICCE_Data_ServiceAttrTbl),GATT_MAX_ENCRYPT_KEY_SIZE,&LOC_ICCE_Data_ServiceCBs);Log_info1("Registered service, %d attributes",GATT_NUM_ATTRS(ICCE_Data_ServiceAttrTbl));ds_icall_rsp_task_id = rspTaskId;return(status);
}
/** 数据服务设置参数 - 设置服务里面的属性内容.* 传参:param -配置文件参数ID,目前我们ICCE数据服务里面有两个参数,我们把ID的范围取0:1*      len   - 带写入的数据长度*      value - 指向带写入的数据.*/
bStatus_t ICCEDataService_SetParameter(uint8_t param, uint16_t len, void *value)
{bStatus_t ret = SUCCESS;uint8_t  *pAttrVal;uint8_t  *pValLen;uint16_t valMinLen;uint16_t valMaxLen;uint8_t sendNotiInd = FALSE;gattCharCfg_t *attrConfig;uint8_t needAuth;switch(param){case wCharact_PARAM_ID:pAttrVal = LOC_InputVal;pValLen = &LOC_InputValLen;valMinLen = 0;valMaxLen = sizeof(LOC_InputVal);break;case rnCharact_PARAM_ID:pAttrVal = LOC_OutputVal;pValLen = &LOC_OutputValLen;valMinLen = 0;valMaxLen = sizeof(LOC_OutputVal);sendNotiInd = TRUE;attrConfig = NotifyCharactConfig;needAuth = FALSE;  // 这里发送设置成不需要认证break;default:Log_error1("SetParameter: Parameter #%d not valid.", param);return(INVALIDPARAMETER);}// 更新数据和发送notification或者indicationif(len <= valMaxLen && len >= valMinLen){memcpy(pAttrVal, value, len);*pValLen = len;if(sendNotiInd){Log_info2("Trying to send noti/ind: connHandle %x, %s",attrConfig[0].connHandle,(uintptr_t)((attrConfig[0].value ==0) ? "\x1b[33mNoti/ind disabled\x1b[0m" :(attrConfig[0].value ==1) ? "Notification enabled" :"Indication enabled"));// 尝试发送NotificationGATTServApp_ProcessCharCfg(attrConfig, pAttrVal, needAuth,ICCE_Data_ServiceAttrTbl,GATT_NUM_ATTRS(ICCE_Data_ServiceAttrTbl),ds_icall_rsp_task_id,ICCE_Data_Service_ReadAttrCB);}}else{Log_error3("Length outside bounds: Len: %d MinLen: %d MaxLen: %d.", len,valMinLen,valMaxLen);ret = bleInvalidRange;}return(ret);
}static uint8_t ICCEData_Service_findCharParamId(gattAttribute_t *pAttr)
{// Is this a Client Characteristic Configuration Descriptor?if(ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID ==*(uint16_t *)pAttr->type.uuid){return(ICCEData_Service_findCharParamId(pAttr - 1));}// 判断是否是wCharact的UUIDelse if(ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, wUserCharact_UUID, pAttr->type.len)){return(wCharact_PARAM_ID);}// 判断是否是rnCharact的UUIDelse if(ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, rnUserCharact_UUID, pAttr->type.len)){return(rnCharact_PARAM_ID);}else{return(0xFF);}
}/********************************************************************** @fn          ICCE_Data_Service_ReadAttrCB* @brief       读取一个属性* @param       connHandle - 连接句柄,客户端和服务器建立连接之后的句柄* @param       pAttr - 指向属性的指针* @param       pValue - 指向待读的数据* @param       pLen - 指向读取的字节个数* @param       offset - 偏移字节读取* @param       maxLen - 读取的最大长度* @param       method - 读取的方式* @return      SUCCESS, blePending or Failure*/
static bStatus_t ICCE_Data_Service_ReadAttrCB(uint16_t connHandle,gattAttribute_t *pAttr,uint8_t *pValue, uint16_t *pLen,uint16_t offset,uint16_t maxLen,uint8_t method)
{bStatus_t status = SUCCESS;uint16_t valueLen;uint8_t paramID = 0xFF;paramID = ICCEData_Service_findCharParamId(pAttr);switch(paramID){case rnCharact_PARAM_ID:valueLen = LOC_OutputValLen;break;default:Log_error0("Attribute was not found.");return(ATT_ERR_ATTR_NOT_FOUND);}if(offset > valueLen){Log_error0("An invalid offset was requested.");status = ATT_ERR_INVALID_OFFSET;}else{*pLen = MIN(maxLen, valueLen - offset);memcpy(pValue, pAttr->pValue + offset, *pLen);}return(status);
}/********************************************************************** @fn      ICCE_Data_Service_WriteAttrCB* @brief   在写操作之前验证属性的数据* @param   connHandle - 连接句柄* @param   pAttr - 指向属性的指针* @param   pValue - 指向待写的数据* @param   len - 待写入的数据长度* @param   offset - 偏移字节写入* @param   method - 写类型* @return  SUCCESS, blePending or Failure*/
static bStatus_t ICCE_Data_Service_WriteAttrCB(uint16_t connHandle,gattAttribute_t *pAttr,uint8_t *pValue, uint16_t len,uint16_t offset,uint8_t method)
{bStatus_t status = SUCCESS;uint8_t paramID = 0xFF;uint8_t changeParamID = 0xFF;uint16_t writeLenMin;uint16_t writeLenMax;uint8_t *pValueLenVar;if(ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid){// Allow notification and indication, but do not check if really allowed per CCCD.status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len,offset,GATT_CLIENT_CFG_NOTIFY | GATT_CLIENT_CFG_INDICATE);if(SUCCESS == status && pAppCBs && pAppCBs->pfnCfgChangeCb){pAppCBs->pfnCfgChangeCb(connHandle,ICCEData_Service_findCharParamId(pAttr), len, pValue);}return(status);}paramID = ICCEData_Service_findCharParamId(pAttr);switch(paramID){case wCharact_PARAM_ID:writeLenMin = 0;writeLenMax = sizeof(LOC_InputVal);pValueLenVar = &LOC_InputValLen;Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",(uintptr_t)"String",connHandle,len,offset,method);break;default:Log_error0("Attribute was not found.");return(ATT_ERR_ATTR_NOT_FOUND);}if(offset >= writeLenMax){Log_error0("An invalid offset was requested.");status = ATT_ERR_INVALID_OFFSET;}else if(offset + len > writeLenMax){Log_error0("Invalid value length was received.");status = ATT_ERR_INVALID_VALUE_SIZE;}else if(offset + len < writeLenMin &&(method == ATT_EXECUTE_WRITE_REQ || method == ATT_WRITE_REQ)){Log_error0("Invalid value length was received.");status = ATT_ERR_INVALID_VALUE_SIZE;}else{memcpy(pAttr->pValue + offset, pValue, len);if(offset + len >= writeLenMin){changeParamID = paramID;*pValueLenVar = offset + len; // Update data length.}}
#if 0if(changeParamID != 0xFF){if(pAppCBs && pAppCBs->pfnChangeCb){pAppCBs->pfnChangeCb(connHandle, paramID, len + offset, pValue); // Call app function from stack task context.}}
#endifreturn(status);
}extern void ICCE_test(void)
{uint8_t value[10] = {0};static uint64_t vl = 0;vl++;memcpy(value, (uint8_t *)&vl, sizeof(vl));ICCEDataService_SetParameter(rnCharact_PARAM_ID, 10, value);
}
  • icce_service.h
/** icce_service.h**  Created on: 2023年4月15日*      Author: 28596xx*/#ifndef PROFILES_ICCE_SERVICE_H_
#define PROFILES_ICCE_SERVICE_H_#ifdef __cplusplus
extern "C"
{
#endif/********************************************************************** INCLUDES*/
#include <bcomdef.h>#define wCharact_PARAM_ID 0x00
#define rnCharact_PARAM_ID 0x01extern bStatus_t ICCE_DataService_AddService(uint8_t rspTaskId);
extern bStatus_t ICCEDataService_SetParameter(uint8_t param, uint16_t len, void *value);extern void ICCE_test(void);//用于测试notify是否正常
#endif /* PROFILES_ICCE_SERVICE_H_ */

将上面这两个文件添加到工程,ICCE_test() 用一个定时器每5s执行一次。编译下载到CC2642,运行之后用手机就能发现我们这个服务了。

color=gray

更多推荐

CC2642的GGS使用笔记

本文发布于:2024-03-23 01:48:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1739027.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:笔记   GGS

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!