从机有定义2个特征值(FF70,FF71)
主机端代码中,应该怎么处理。
实现同时读取到从机的2个特征值呢。
从机有定义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; }
新的问题~
代码中请求读取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之类的数据,
但是蓝牙的协议规范在一个读没完成之前, 不允许发另外一个读.
所以,你可以做个标志位, gatt read request 的时候,把要read的handle记录下来,这样在gatt read rsp 事件来的时候,就对应的handle 过来的数据.
嗯嗯,我在特征值数据中做了区分。可以正常区分数据来源了。
还有问题需要请教
我的项目中将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; } }
应该是第二个没使能
@xhh,谢谢大佬,道理我懂的,第2个的centralCCDHdl可以在这里拿,但是他这里提交了一个start_cccd_evt之后,必须要等返回了成功写入了之后才能进行第2个的操作,这里我就不清楚在哪个回调函数里面提交第二个服务的cccd了。
我尝试了在你上面截图这里再拿一个cccdHdl1,然后在start_write_cccd_evt里面调用,不成功。
我在这里复制了一遍,改成了CCCDHdl1,也是不成功的
@TECH_Lpc
大佬能不能帮忙看下代码怎么写
你好,例程是获取一个cccd对应的handle值,如果从机拥有多个noti,则主机可以依次获取对应的cccd并进行使能。针对该功能提供了一份可以获取到所有属性的代码参考。已发送至邮箱,请查收。
收到您的邮件了,按照例程代码做了修改后还是只能开启一个notify,已经将log和代码回复到您邮寄了。
已回复。
我尝试在notify的回调函数中开启第2个服务的notify,而后发现log里面出现Param Update...,查看代码得知这是GAP_LINK_PARAM_UPDATE_EVENT事件引起的。同时第一个已经开启的notify关闭了,第2个服务的notify开启了。
看这个事件的名字是连接参数更改?
请问这个信息能否帮忙定位一下问题,谢谢大佬。