主机如何同时监听多个特征值

从机有定义2个特征值(FF70,FF71)

主机端代码中,应该怎么处理。

实现同时读取到从机的2个特征值呢。


首先,要区分 "监听"跟 "读取" 的差异,

一般来说ble 外设设备或者说从机(通常作为server)的数据传给主机(通常为有两种方式:

1, 从机主动上报包括notify或indicate;

2, 主机端去读,这时候主动权在主机侧,而不是从机侧.


这里我们先来讨论下,notify

实现notify的前提是, 主机端先对从机使能cccd, 然后从机端才可以notify 数据出来,这里我们就要解决使能notify的问题:

实际上cccd 对应的uuid 是 0x2902

我们根据primary service 查到的handle区间, 再去通过GATT_ReadUsingCharUUID查到cccd对应的handle即可:

attReadByTypeReq_t req;
req.startHandle = centralSvcStartHdl;
req.endHandle = centralSvcEndHdl;
req.type.len = ATT_BT_UUID_SIZE;
req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
result = GATT_ReadUsingCharUUID( centralConnHandle, &req, centralTaskId );


然后在gatt_discivery的事件里面 去把发现的所有的具有cccd的handle 记录下来:

     case BLE_DISC_STATE_CCCD:
         if ( pMsg->method == ATT_READ_BY_TYPE_RSP) {
            if(pMsg->msg.readByTypeRsp.numPairs > 0 ) {                 
                 centralCCCDHdl = BUILD_UINT16( pMsg->msg.readByTypeRsp.pDataList[0],
                                             pMsg->msg.readByTypeRsp.pDataList[1] );
                    for(unsigned char i = 0; i < pMsg->msg.readByTypeRsp.numPairs ; i++){                         
                            char_value_handle_list[cccd_handle_index++] = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[pMsg->msg.readByTypeRsp.len * i], \
                                                            pMsg->msg.readByTypeRsp.pDataList[pMsg->msg.readByTypeRsp.len * i+1]);   
                    }             
            }
            ble_db_dis_state = BLE_DISC_STATE_IDLE;
        }


然后再把char_value_handle_list[cccd_handle_index++] 里面存储的cccd handle 依次使能:

   

      uint8_t data[2] = {0x01,0x00};
    return central_gatt_write_char_value(connection_handle,notify_handle,data,2);

其中:

uint8_t central_gatt_write_char_value(uint16_t connection_handle,uint16_t char_handle,uint8_t *data,uint16_t length){
    uint8_t result;
    attWriteReq_t req;
 
    req.cmd = FALSE;
    req.sig = FALSE;
    req.handle = char_handle;
    req.len = length;
    req.pValue = GATT_bm_alloc(connection_handle,ATT_WRITE_REQ,req.len,NULL,0);
    if ( req.pValue != NULL ){
        tmos_memcpy(req.pValue,data,length); 
        result = GATT_WriteCharValue(centralConnHandle,&req,centralTaskId);
        if(SUCCESS == result){
            return SUCCESS;
        }else{
            GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
            write_data_when_failed.connection_handle = connection_handle;
            write_data_when_failed.char_handle = char_handle;
            write_data_when_failed.data_length = length;
            tmos_memcpy(write_data_when_failed.data,data,write_data_when_failed.data_length);
            PRINT("data0 data1=%02x %02x \r\n",write_data_when_failed.data[0],write_data_when_failed.data[1]);
            tmos_start_task(centralTaskId,START_WRITE_EVT,80);
            return result;
        }
    }
    return 0xff;
}

经过使能cccd 后,就可以在

GATT的event 里面下面类似的处理函数内,接收到 notify数据了

static void centralProcessGATTMsg( gattMsgEvent_t *pMsg ){
    if ( centralState != BLE_STATE_CONNECTED ){
        // In case a GATT message came after a connection has dropped,
        // ignore the message
        return;
    }
    
    switch(pMsg->method){
        case ATT_HANDLE_VALUE_NOTI:
            PRINT("ATT_HANDLE_VALUE_NOTI->");
            for(uint16_t i=0;imsg.handleValueNoti.len;i++){
                PRINT("%02X ",pMsg->msg.handleValueNoti.pValue[i]);
            }
            PRINT("\r\n");


至于read

这里是类似上面的查询cccd的handle,需要依次,把char value 的handle 查到,,然后根据对应的handle,调用

extern bStatus_t GATT_ReadCharValue( uint16 connHandle, attReadReq_t *pReq, uint8 taskId );

然后在 GATT的event 里面下面类似的处理函数内,

接收到ATT_READ_RSP的值:

static void centralProcessGATTMsg( gattMsgEvent_t *pMsg ){
    if ( centralState != BLE_STATE_CONNECTED ){
        // In case a GATT message came after a connection has dropped,
        // ignore the message
        return;
    }
    
    switch(pMsg->method){
        case ATT_READ_RSP:
            PRINT("接收到ATT_READ_RSP的值->");
            for(uint16_t i=0;imsg.readRsp.len;i++){
                PRINT("%02X ",pMsg->msg.readRsp.pValue[i]);
            }
            PRINT("\r\n");



非常感谢  

已经解决了


        attReadReq_t req;  
        
        req.handle = centralCharHdl;///读取不同的特征值数据        
        if( GATT_ReadCharValue( centralConnHandle, &req, centralTaskId ) == SUCCESS )
        {
          centralProcedureInProgress = TRUE;
          centralDoWrite = !centralDoWrite;
        }
    if ( ( pMsg->method == ATT_READ_RSP ) ||
       ( ( pMsg->method == ATT_ERROR_RSP ) &&
         ( pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ ) ) )
  {
    if ( pMsg->method == ATT_ERROR_RSP )
    {
      uint8 status = pMsg->msg.errorRsp.errCode;
      
      PRINT( "Read Error: %x\n", status );
    }
    else
    {
      // After a successful read, display the read value
      PRINT("Read rsp: %x\n",*pMsg->msg.readRsp.pValue);
      ////这里如何判断数据来源呢?是由哪个特征值返回的数据呢。
    }
    centralProcedureInProgress = FALSE;
  }


新的问题~j_0035.gif

代码中请求读取3个不同特征值的数据

在event(ATT_READ_RSP)中  ,如何区分返回数据的来源呢。



wch的协议栈中, read rsp 并没有带除了数据和长度以外的数据

/**
 * Read Response format.
 */
typedef struct
{
  uint16 len;    //!< Length of value
  uint8 *pValue; //!< Value of the attribute with the handle given (0 to ATT_MTU_SIZE-1)
} attReadRsp_t;

而在规范里,亦没有带,

在Read Response 中并没有携带 handle之类的数据,

image.png


但是蓝牙的协议规范在一个读没完成之前, 不允许发另外一个读.


所以,你可以做个标志位, gatt read request 的时候,把要read的handle记录下来,这样在gatt read rsp 事件来的时候,就对应的handle 过来的数据.


嗯嗯,我在特征值数据中做了区分。可以正常区分数据来源了。


还有问题需要请教j_0070.gif

我的项目中将CH573作为主机,需要比较频繁读取从机的特征值数据(3个),还要能随时写入一个特征值。经常会出现读取失败或者写失败的情况。

请问有什么解决方案和优化的方法吗。

3个特征值的读取间隔 分别是300ms  1000ms  3000ms ,使用主机循环读取特征值还是从机发送notify的方式会更好一点呢?



建议使用notify, notify 的效率更高且带了gatt handle.只是需要在连接时候依次写cccd, 使能notify.

而read request 需要在至少两个连接事件才能完成一个过程,

而notify 可以做到一个连接事件notify 多个数据. (config.h 中的 BLE_TX_NUM_EVENT 大于1 即可.)



你好,TECH46。您解答的完整的例子能发一份吗?


请问一下,使用开启两个特征值的notify功能代码应该怎么写,我目前是在开启了CCCD之后,又仿照例子增加了查找特征值2,开启CCCD的顺序,但是收到的notify值依旧只是第一个特征值的notify。

static void centralGATTDiscoveryEvent(gattMsgEvent_t *pMsg) {
    attReadByTypeReq_t req;
    if (centralDiscState == BLE_DISC_STATE_SVC) {
        // Service found, store handles
        if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP
                && pMsg->msg.findByTypeValueRsp.numInfo > 0) {
            centralSvcStartHdl = ATT_ATTR_HANDLE(
                    pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
            centralSvcEndHdl = ATT_GRP_END_HANDLE(
                    pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);

            // Display Profile Service handle range
            PRINT("Found Profile Service handle : %x ~ %x \n",
                    centralSvcStartHdl, centralSvcEndHdl);
        }
        // If procedure complete
        if ((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP
                && pMsg->hdr.status == bleProcedureComplete)
                || (pMsg->method == ATT_ERROR_RSP)) {
            if (centralSvcStartHdl != 0) {

                // Discover characteristic
                centralDiscState = BLE_DISC_STATE_CHAR1;
                req.startHandle = centralSvcStartHdl;
                req.endHandle = centralSvcEndHdl;
                req.type.len = ATT_BT_UUID_SIZE;
                req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
                req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
                printf("S-CHAR1\r\n");
//                GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
                GATT_DiscCharsByUUID(centralConnHandle, &req, centralTaskId);
            }
        }

    } else if (centralDiscState == BLE_DISC_STATE_CHAR1) {
        // Characteristic found, store handle
        if (pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->msg.readByTypeRsp.numPairs > 0) {

            centralCharHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
                    pMsg->msg.readByTypeRsp.pDataList[1]);

            // Start do read or write
            /*
             tmos_start_task(centralTaskId, START_READ_OR_WRITE_EVT,
             DEFAULT_READ_OR_WRITE_DELAY);
             */

            // Display Characteristic 1 handle
            PRINT("Found Characteristic 1 handle : %x \n", centralCharHdl);
        }
        if ((pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->hdr.status == bleProcedureComplete)
                || (pMsg->method == ATT_ERROR_RSP)) {
            // Discover characteristic
            centralDiscState = BLE_DISC_STATE_CCCD1;
            req.startHandle = centralSvcStartHdl;
            req.endHandle = centralSvcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
            req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
            printf("M-CHAR1\r\n");
            GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
        }
    } else if (centralDiscState == BLE_DISC_STATE_CCCD1) {
        printf("DDDD,%x\r\n", pMsg->msg.readByTypeRsp.numPairs);
        // Characteristic found, store handle
        if (pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->msg.readByTypeRsp.numPairs > 0) {
            centralCCCDHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
                    pMsg->msg.readByTypeRsp.pDataList[1]);


            // Start do write CCCD
            tmos_start_task(centralTaskId, START_WRITE_CCCD_EVT,
            DEFAULT_WRITE_CCCD_DELAY);
            printf("F-CHAR1\r\n");

            // Display Characteristic 1 handle
            PRINT("Found client characteristic configuration handle : %x \n",
                    centralCCCDHdl);
        }
        // If procedure complete
        if ((pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->hdr.status == bleProcedureComplete)
                || (pMsg->method == ATT_ERROR_RSP)) {
            // Discover characteristic
            centralDiscState = BLE_DISC_STATE_CHAR2;
            req.startHandle = centralSvcStartHdl;
            req.endHandle = centralSvcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR2_UUID);
            req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR2_UUID);
            printf("S-CHAR2\r\n");
//                GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
            GATT_DiscCharsByUUID(centralConnHandle, &req, centralTaskId);
        }
    } else if (centralDiscState == BLE_DISC_STATE_CHAR2) {
        // Characteristic found, store handle
        if (pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->msg.readByTypeRsp.numPairs > 0) {
            centralCharHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
                    pMsg->msg.readByTypeRsp.pDataList[1]);

            // Start do read or write
            /*
             tmos_start_task(centralTaskId, START_READ_OR_WRITE_EVT,
             DEFAULT_READ_OR_WRITE_DELAY);
             */

            // Display Characteristic 1 handle
            PRINT("Found Characteristic 1 handle : %x \n", centralCharHdl);
        }
        if ((pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->hdr.status == bleProcedureComplete)
                || (pMsg->method == ATT_ERROR_RSP)) {
            // Discover characteristic
            centralDiscState = BLE_DISC_STATE_CCCD2;
            req.startHandle = centralSvcStartHdl;
            req.endHandle = centralSvcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
            req.type.uuid[1] = HI_UINT16(GATT_CLIENT_CHAR_CFG_UUID);
            printf("M-CHAR2\r\n");
            GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
        }
    } else if (centralDiscState == BLE_DISC_STATE_CCCD2) {
        printf("DDDD,%x\r\n", pMsg->msg.readByTypeRsp.numPairs);
        // Characteristic found, store handle
        if (pMsg->method == ATT_READ_BY_TYPE_RSP
                && pMsg->msg.readByTypeRsp.numPairs > 0) {
            printf("IIII\r\n");
            centralCCCDHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
                    pMsg->msg.readByTypeRsp.pDataList[1]);
            centralProcedureInProgress = FALSE;

            // Start do write CCCD
            tmos_start_task(centralTaskId, START_WRITE_CCCD_EVT,
            DEFAULT_WRITE_CCCD_DELAY);
            printf("F-CHAR2\r\n");
            // Display Characteristic 1 handle
            PRINT("Found client characteristic configuration handle : %x \n",
                    centralCCCDHdl);
        }
        centralDiscState = BLE_DISC_STATE_IDLE;
    }
}



应该是第二个没使能


image.png


@xhh,谢谢大佬,道理我懂的,第2个的centralCCDHdl可以在这里拿,但是他这里提交了一个start_cccd_evt之后,必须要等返回了成功写入了之后才能进行第2个的操作,这里我就不清楚在哪个回调函数里面提交第二个服务的cccd了。

我尝试了在你上面截图这里再拿一个cccdHdl1,然后在start_write_cccd_evt里面调用,不成功。

image.png

我在这里复制了一遍,改成了CCCDHdl1,也是不成功的


@TECH_Lpc

大佬能不能帮忙看下代码怎么写


你好,例程是获取一个cccd对应的handle值,如果从机拥有多个noti,则主机可以依次获取对应的cccd并进行使能。针对该功能提供了一份可以获取到所有属性的代码参考。已发送至邮箱,请查收。


@TECH_Lpc

收到您的邮件了,按照例程代码做了修改后还是只能开启一个notify,已经将log和代码回复到您邮寄了。


已回复。


@TECH_Lpc

我尝试在notify的回调函数中开启第2个服务的notify,而后发现log里面出现Param Update...,查看代码得知这是GAP_LINK_PARAM_UPDATE_EVENT事件引起的。同时第一个已经开启的notify关闭了,第2个服务的notify开启了。

看这个事件的名字是连接参数更改?

请问这个信息能否帮忙定位一下问题,谢谢大佬。


只有登录才能回复,可以选择微信账号登录