【BLE】CC2541之indicate

本篇博文最后修改时间:2017年08月31日,17:00。


一、简介

本文以SimpleBLEPeripheral为例,讲述如何通过按五项按键的“center”键发送indicate数据给btool,用PC的串口工具打印出“indicate”的“发数据”和“应答”的过程。


二、实验平台

协议栈版本:BLE-CC254x-1.4.0

编译软件:IAR 8.20.2

硬件平台:Smart RF开发板(主芯片CC2541)


版权声明

博主:甜甜的大香瓜

声明:喝水不忘挖井人,转载请注明出处。

原文地址:http://blog.csdn.NET/feilusia

联系方式:897503845@qq.com

香瓜BLE之CC2541群:127442605

香瓜BLE之CC2640群:557278427

香瓜BLE之Android群:541462902

香瓜嵌入式之STM8/STM32群:164311667
香瓜嵌入式之Linux:512598061
香瓜嵌入式之职场交流:450154342
香瓜嵌入式之英语尬聊:169024332
opengua官方旗舰店:https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

四、 实验前提
1、在进行本文步骤前,请先 阅读 以下博文:
暂无

2、在进行本文步骤前,请先 实现以下博文:
1)《CC2541之按键》:http://blog.csdn .NET /feilusia/article/details/47336473


五、基础知识

1、什么是indicate?

答:indicate译为“指示”,它是服务器给客户端发送数据的方式。


2、它与notify有什么不同?

答:它在使用上比notify多一个应答的步骤,如下图:


注:有应答的通信方式“indicate”,更适用于可靠的通信方式。


3、indicate的指示开关如何打开、关闭?

答:


也就是0x0002写到属性为“indicate”的特征值的CCC位,则开指示;如果写0x0000,则关指示。

注:如果写0x0001到属性为“indicate”的特征值的CCC位,无用。


六、实验步骤

1、添加属性为indicate的特征值char7(simpleGATTprofile.c中

1)修改simpleGATTprofile.h 的宏定义



2)添加char7的UUID

// Characteristic 7 UUID: 0xFFF7
CONST uint8 simpleProfilechar7UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(SIMPLEPROFILE_CHAR7_UUID), HI_UINT16(SIMPLEPROFILE_CHAR7_UUID)
};

3)添加char7的设置属性

// Simple Profile Characteristic 7 Properties  
static uint8 simpleProfileChar7Props = GATT_PROP_INDICATE;  
  
// Characteristic 7 Value  
static uint8 simpleProfileChar7[SIMPLEPROFILE_CHAR7_LEN] = {0};  
  
// Simple Profile Characteristic 7 Configuration Each client has its own  
// instantiation of the Client Characteristic Configuration. Reads of the  
// Client Characteristic Configuration only shows the configuration for  
// that client and writes only affect the configuration of that client.  
static gattCharCfg_t simpleProfileChar7Config[GATT_MAX_NUM_CONN];  
  
// Simple Profile Characteristic 7 User Description  
static uint8 simpleProfileChar7UserDesp[17] = "Characteristic 7\0";

4)属性表修改

①修改属性表的大小

#define SERVAPP_NUM_ATTR_SUPPORTED        25
具体要看属性表有多少个属性。


②修改属性表

      // Characteristic 7 Declaration  
      {   
        { ATT_BT_UUID_SIZE, characterUUID },  
        GATT_PERMIT_READ,   
        0,  
        &simpleProfileChar7Props   
      },  
  
  
      // Characteristic Value 7  
      {   
        { ATT_BT_UUID_SIZE, simpleProfilechar7UUID },  
        0,  
        0,   
        simpleProfileChar7   
      },  
  
  
      // Characteristic 7 configuration  
      {   
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },  
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,   
        0,   
        (uint8 *)simpleProfileChar7Config   
      },  
        
      // Characteristic 7 User Description  
      {   
        { ATT_BT_UUID_SIZE, charUserDescUUID },  
        GATT_PERMIT_READ,   
        0,   
        simpleProfileChar7UserDesp   
      }, 

5)修改参数函数

①在SimpleProfile_SetParameter中添加

    case SIMPLEPROFILE_CHAR7:    
      if ( len == SIMPLEPROFILE_CHAR7_LEN )     
      {    
        VOID osal_memcpy( simpleProfileChar7, value, SIMPLEPROFILE_CHAR7_LEN );    
      }    
      else    
      {    
        ret = bleInvalidRange;    
      }    
      break;

②在SimpleProfile_GetParameter中添加

    case SIMPLEPROFILE_CHAR7:  
      VOID osal_memcpy( value, simpleProfileChar7, SIMPLEPROFILE_CHAR7_LEN );  
      break;  


6)修改读写特征值函数

①在simpleProfile_ReadAttrCB中添加

      case SIMPLEPROFILE_CHAR7_UUID:  
        *pLen = SIMPLEPROFILE_CHAR7_LEN;  
        VOID osal_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR7_LEN );  
        break;
以上的添加实际上没什么用,但是跟char4一样统一。

②在simpleProfile_WriteAttrCB中修改

      case GATT_CLIENT_CHAR_CFG_UUID:
        if ( pAttr->handle == simpleProfileAttrTbl[ATTRTBL_CHAR4_CCC_IDX].handle )//CHAR4 NOTIFY
        {
          // BloodPressure Notifications
          status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                   offset, GATT_CLIENT_CFG_NOTIFY );
        }
        else if ( pAttr->handle == simpleProfileAttrTbl[ATTRTBL_CHAR6_CCC_IDX].handle )//CHAR6 NOTIFY 
        {
          // BloodPressure Notifications
          status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                   offset, GATT_CLIENT_CFG_NOTIFY );
        }
        else if ( pAttr->handle == simpleProfileAttrTbl[ATTRTBL_CHAR7_CCC_IDX].handle )//CHAR7 INDICATE
        {
          // BloodPressure Indications
          status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                   offset, GATT_CLIENT_CFG_INDICATE );
        }        
        else
        {
          status = ATT_ERR_INVALID_HANDLE;
        }        

        break;
注意,这里每个具备notify和indicate属性的都需要添加一个对应的代码,否则CCC开关开不起来。


③添加char7的CCC在属性表中偏移值的宏

#define ATTRTBL_CHAR7_CCC_IDX               23
注:char4和char6等其他notify、indicate属性的自行添加。这里只添加char7的。


7)修改SimpleProfile_AddService

bStatus_t SimpleProfile_AddService( uint32 services )
{
  uint8 status = SUCCESS;

  // Initialize Client Characteristic Configuration attributes
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar6Config ); 
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar7Config ); 

  // Register with Link DB to receive link status change callback
  VOID linkDB_Register( simpleProfile_HandleConnStatusCB );  
  
  if ( services & SIMPLEPROFILE_SERVICE )
  {
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( simpleProfileAttrTbl, 
                                          GATT_NUM_ATTRS( simpleProfileAttrTbl ),
                                          &simpleProfileCBs );
  }

  return ( status );
}


8)修改simpleProfile_HandleConnStatusCB

static void simpleProfile_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{ 
  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    // Reset Client Char Config if connection has dropped
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
           ( !linkDB_Up( connHandle ) ) ) )
    { 
      GATTServApp_InitCharCfg( connHandle, simpleProfileChar4Config );
      GATTServApp_InitCharCfg( connHandle, simpleProfileChar6Config );
      GATTServApp_InitCharCfg( connHandle, simpleProfileChar7Config );      
    }
  }
}

9)修改应用层时初始化特征值的部分(simpleBLEPeripheral.c的SimpleBLEPeripheral_Init中)

{  
  uint8 charValue1 = 1;  
  uint8 charValue2 = 2;  
  uint8 charValue3 = 3;  
  uint8 charValue4 = 4;  
  uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };  
  uint8 charValue6[SIMPLEPROFILE_CHAR6_LEN] = { 1, 2, 3, 4, 5 };  
  uint8 charValue7[SIMPLEPROFILE_CHAR7_LEN] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, }; 
  
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, sizeof ( uint8 ), &charValue1 );  
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, sizeof ( uint8 ), &charValue2 );  
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, sizeof ( uint8 ), &charValue3 );  
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof ( uint8 ), &charValue4 );  
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );  
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6 );      
  SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR7, SIMPLEPROFILE_CHAR7_LEN, charValue7 );
}

2、添加indicate函数

1)添加char7的value在属性表中的偏移值的宏(simpleGATTprofile.c)

#define ATTRTBL_CHAR7_VALUE_IDX               22 


2)添加indicate指示函数(simpleGATTprofile.c)

//******************************************************************************  
//name:         SimpleGATTprofile_Char7_Indicate  
//introduce:    指示len长度的数据 
//parameter:    connHandle:连接句柄  
//              pValue:要通知的数据,范围为0~SIMPLEPROFILE_CHAR7_LEN,最多20个字节  
//              len:要通知的数据的长度 
//              taskId:应答时要返回到的那个任务的id
//return:       none  
//****************************************************************************** 
bStatus_t SimpleGATTprofile_Char7_Indicate( uint16 connHandle, uint8 *pValue, uint8 len, uint8 taskId)
{
  attHandleValueInd_t  indi;
  uint16 value;

  value  = GATTServApp_ReadCharCfg( connHandle, simpleProfileChar7Config );//读出CCC的值

  if ( value & GATT_CLIENT_CFG_INDICATE ) //判断是否打开通知开关,打开了则发送数据
  {
    indi.handle = simpleProfileAttrTbl[ATTRTBL_CHAR7_VALUE_IDX].handle;
    indi.len = len;
    osal_memcpy( indi.value, pValue, len);       //数据
    return(GATT_Indication( connHandle, &indi, FALSE, taskId ));
  }
  
  return(FAILURE);
}

3)声明函数(simpleGATTprofile.h)

//******************************************************************************  
//name:         SimpleGATTprofile_Char7_Indicate  
//introduce:    指示len长度的数据 
//parameter:    connHandle:连接句柄  
//              pValue:要通知的数据,范围为0~SIMPLEPROFILE_CHAR7_LEN,最多20个字节  
//              len:要通知的数据的长度 
//              taskId:应答时要返回到的那个任务的id
//return:       none  
//****************************************************************************** 
bStatus_t SimpleGATTprofile_Char7_Indicate( uint16 connHandle, uint8 *pValue, uint8 len, uint8 taskId);

3、在应用层使用indicate发送数据(simpleBLEPeripheral.c中)

1)按键处理处simpleBLEPeripheral_HandleKeys添加

  if ( keys & HAL_KEY_SW_5 )
  {   
    uint16 notify_Handle; 
    uint8 *p = buf[20]; 
    uint8 status;
    
    GAPRole_GetParameter( GAPROLE_CONNHANDLE, &notify_Handle);                //获取Connection Handle 
    
    for(uint8 i = 0; i < 20; i++)       //写一个20字节的测试缓冲区的数据
    {
      *(p+i) = i;
    }

    status = SimpleGATTprofile_Char7_Indicate(notify_Handle, p, 20, simpleBLEPeripheral_TaskID);  
    
    if(status == SUCCESS)
    {
       NPI_PrintString("indicate is seccess to send!\r\n");
    }
    else
    {
       NPI_PrintString("indicate is fail to send!\r\n");    
    }
  }
status为SUCCESS时,说明主机已经写了0x0002到char7的CCC中,打开了指示开关。没连接、没打开指示开关等原因,则status不为SUCCESS。

调用SimpleGATTprofile_Char7_Indicate函数时,最后一个参数simpleBLEPeripheral_TaskID就是indicate应答会返回的任务ID处,所以indicate应答才会返回应用层。

GATT_Indication函数说明有以下一段注释:

 *          If the return status from this function is SUCCESS, the calling
 *          application task will receive an OSAL GATT_MSG_EVENT message.
 *          The type of the message will be ATT_HANDLE_VALUE_CFM.
说明indicate的事件是GATT_MSG_EVENT(下面会用到),消息类型是ATT_HANDLE_VALUE_CFM。

2)应用层事件处理函数添加GATT_MSG_EVENT事件

/*********************************************************************
 * @fn      simpleBLEPeripheral_ProcessOSALMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  switch ( pMsg->event )
  {
  //#if defined( CC2540_MINIDK )
    case KEY_CHANGE:
      simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
      break;
  //#endif // #if defined( CC2540_MINIDK )

  case GATT_MSG_EVENT:
      Indication_ProcessGattMsg( (gattMsgEvent_t *) pMsg );
      break;
      
  default:
    // do nothing
    break;
  }
}
由于这个事件是底层传上来的,所以会传到系统消息处理的地方。

因此在这里添加一个indicate的消息处理函数。


3)定义一个indicate的消息处理函数

/*********************************************************************
 * @fn      Indication_ProcessGattMsg
 *
 * @brief   Process GATT messages
 *
 * @return  none
 */
static void Indication_ProcessGattMsg( gattMsgEvent_t *pMsg )
{
  NPI_PrintString("Indication_ProcessGattMsg\r\n");
}

4)声明indicate的消息处理函数

static void Indication_ProcessGattMsg( gattMsgEvent_t *pMsg );


七、注意事项

暂无


八、实验结果

1、btool与Smart RF连接后,按五向按键的“center”键


显示错误是正常,因为此时指示开关还没有打开。


2、打开指示开关

方法一(主机端打开指示开关):


0x003A是char7的CCC的特征值句柄,往里写0x0002打开指示开关。


方法二(从机端打开指示开关):

GATTServApp_WriteCharCfg(connHandle, simpleProfileChar7Config, 0x0002);
不建议从机端自己打开此开关,应尽量保证主机来控制从机。


3、再按五向按键的“center”键发送indicate数据


看串口工具:

“indicate is seccess to send!”代表Smart RF发送indicate数据发送成功。

“Indication_ProcessGattMsg”则代表btool返回的应答消息到达了应用层。

看btool:

收到了Smart RF发出的20个字节的数据。


因此,实验成功。







评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值