沁恒微BLE蓝牙主机寻找从机服务及特征值的记录

1、今天,22年10月10号,两年多的嵌软工作生涯,突然想记录一下工作中软件开发的事。

2、嗯~,先说一下开发需求吧。刚入公司转正不久,导师告诉我要做一个蓝牙主机,能够根据用户输入的设备名称寻找指定的BLE蓝牙从机,并对指定的特征值进行读写。

3、开始干吧,打开沁恒微官方例程,文件路径:CH583EVT\EVT\EXAM\BLE。当你打开后开始阅读代码。发现问题了,这个例程是通过一个固定的从机地址去连接了从机:

static void centralEventCB(gapRoleEvent_t *pEvent)
{
    switch(pEvent->gap.opcode)
    {
        case GAP_DEVICE_INIT_DONE_EVENT:
        {
            PRINT("Discovering...\n");
            GAPRole_CentralStartDiscovery(DEFAULT_DISCOVERY_MODE,
                                          DEFAULT_DISCOVERY_ACTIVE_SCAN,
                                          DEFAULT_DISCOVERY_WHITE_LIST);
        }
        break;

        case GAP_DEVICE_INFO_EVENT:
        {
            // Add device to list
            centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
        }
        break;

        case GAP_DEVICE_DISCOVERY_EVENT:
        {
            uint8_t i;

            // See if peer device has been discovered
            for(i = 0; i < centralScanRes; i++)
            {
                if(tmos_memcmp(PeerAddrDef, centralDevList[i].addr, B_ADDR_LEN))
                    break;
            }

看到这一句:if(tmos_memcmp(PeerAddrDef, centralDevList[i].addr, B_ADDR_LEN))
例程的主机是把找到的地址放到一个叫centralDevList的数组中,然后在该数组中寻找从机地址PeerAddrDef。从这里开始,跟需求就完全不一样了;顺便说一下,她的寻找服务和特征值也并不是我的需求想要的。

4、那么在这里寻找从机的时候,我需要去匹配用户输入的从机名称(不是从机地址),我们需要将用户输入的名称和地址做一个匹配或绑定,将就使用他的centralDevList数组来存储从机地址。在这个case GAP_DEVICE_INFO_EVENT:语句中修改一下:

case GAP_DEVICE_INFO_EVENT:       //当发现设备时发送tmos事件类型gapDeviceInfoEvent_t,发现设备后取消设备发现,将不会收到GAP_DEVICE_INFO_EVENT
        {
			uint8_t cmystata;
			At *p_Atcmd;
			p_Atcmd = GetAtRecStruct();
          uint8_t UserNamelen,len;
          uint8_t EvtData[31];

          sprintf(EvtData,"%s",pEvent->deviceInfo.pEvtData);    //将扫描的数据格式化成字符串进行子字符串匹配
          UserNamelen = p_Atcmd->Auxlen-(strlen(AT_CONNECT)+1);				//用户输入的名字长度,不要末尾两字符
//          PRINT("格式化的字符串:%s,要匹配的字符串:%s,长度:%d\r\n",EvtData,p_Atcmd->p_dat,UserNamelen);

          //以下为字符串匹配
          for (uint8_t i = 0; i < pEvent->deviceInfo.dataLen;)
		  {
        	  if(pEvent->deviceInfo.pEvtData[i]>=2)
			  {
        		  len = pEvent->deviceInfo.pEvtData[i];
        		  if(pEvent->deviceInfo.pEvtData[i+1] == 0x09)
				  {
        			  PRINT("名称长度:%d",len);
        			  PRINT("找到的名称");
        			  hex_dump(&pEvent->deviceInfo.pEvtData[i+2], len-1);
        			  PRINT("用户输入的名称:");
        			  hex_dump(p_Atcmd->p_dat, len-1);
        			  cmystata = memcmp(&pEvent->deviceInfo.pEvtData[i+2],p_Atcmd->p_dat,len-1);
        			  PRINT("匹配状态:%d\r\n",cmystata);
        			  if(cmystata == 0)	//	找到了
					  {
						StrMatchingFlag = TRUE;
						GAPRole_CentralCancelDiscovery();			//取消设备扫描发现
						centralAddDeviceInfo(pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType);
					  }
        			  PRINT("\r\n");
					  return;
				  }
        		  else {
						i += len+1;
        		  }

			  }
        	  else {
				i++;
			}
		  }
        }
        break;

这样,我们找到了要去连接的从机,在case GAP_DEVICE_DISCOVERY_EVENT:下面屏蔽掉在数组在寻找固定地址的代码:

/*******************************************************
            uint8_t i;
            // See if peer device has been discovered
            for(i = 0; i < centralScanRes; i++)
            {
                if(tmos_memcmp(PeerAddrDef, centralDevList[i].addr, B_ADDR_LEN))
                break;
            }

            // Peer device not found
            if(i == centralScanRes)
*****************************************************/

设备连接上了,我们开始去找服务和特征值吧,并且要把服务和特征值的UUID保存下来,以便用户在读写某个特征值的时候可以找到对应的特征值去读写。
5、在看例程的这个函数static void centralGATTDiscoveryEvent(gattMsgEvent_t *pMsg),这个函数的内部处理是去找serve和char值,例程中直接去找了服务和固定的char,如下所示;

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_CHAR;
                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);

                GATT_ReadUsingCharUUID(centralConnHandle, &req, centralTaskId);
            }
        }
    }

这里找服务的时候是单个响应:

centralSvcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
            centralSvcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);

charyeshi 固定的UUID:

req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
                req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);

注意这里的if判断条件ATT_FIND_BY_TYPE_VALUE_RSP,当需要找从机的多个服务和char时,不能用这个条件作为判断,否则永远也进不去if。找多个serve和char时使用的判断条件是:ATT_READ_BY_GRP_TYPE_RSP,即按分组类型获取从机响应,找多个服务和char也不是:GATT_ReadUsingCharUUID函数,需要使用:GATT_DiscAllChars,接下来是把句柄和serve的UUID拿到:

if(pMsg->method == ATT_READ_BY_GRP_TYPE_RSP &&
           pMsg->msg.findByTypeValueRsp.numInfo > 0)
{
    for (uint8_t i = 0; i < pMsg->msg.readByGrpTypeRsp.numGrps; i++)
	{
            	// Attribute LEN
		PRINT("current att len:%04d\r\n",pMsg->msg.readByGrpTypeRsp.len);
				// Attribute start Handle
		PRINT("current att start handle:%04x\r\n",BUILD_UINT16( pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i], \
							pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 1]));

				// Attribute End Group Handle
		PRINT("current att end  handle:%04x\r\n",BUILD_UINT16(   pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i+2], \
							pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 3]));

				//Primary Service UUID Length
		PRINT("current service uuid len:%04d\r\n",pMsg->msg.readByGrpTypeRsp.len - 4);

				// uuid
		PRINT("uuid:");
				uint8_t *p_uuid = &(pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 4]);
				uint8_t uuid_length = pMsg->msg.readByGrpTypeRsp.len - 4;
		hex_dump(p_uuid,uuid_length);
	}
}
if((pMsg->method == ATT_READ_BY_GRP_TYPE_RSP &&
            pMsg->hdr.status == bleProcedureComplete) ||
           (pMsg->method == ATT_ERROR_RSP))
   {
   		PRINT("开始获取所有服务和特征值GATT-DisAllChars,centralConnHandle = %02x\r\n",centralConnHandle);
//                result = GATT_DiscAllChars(centralConnHandle,centralSvcStartHdl,centralSvcEndHdl,centralTaskId);         //获取服务下的所有特征值
  		Mychar.num = 0;
   		result = GATT_DiscAllChars(centralConnHandle,1,0xFFFF,centralTaskId);
                PRINT("GATT_DiscAllChars返回值:%02x\r\n",result);
   }

上面通过GATT_DiscAllChars(centralConnHandle,1,0xFFFF,centralTaskId);读取从机的char,那么接下来获取一下char的响应:

else if(centralDiscState == BLE_DISC_STATE_CHAR)
    {
        // Characteristic found, store handle
        if(pMsg->method == ATT_READ_BY_TYPE_RSP &&
           pMsg->msg.readByGrpTypeRsp.numGrps > 0)
        {
        for(uint8_t i=0;i<pMsg->msg.readByGrpTypeRsp.numGrps;i++)
            {
            	//characteristic properties
            	Mychar.char_Grop[Mychar.num].char_properties = pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 2];

            	Mychar.char_Grop[Mychar.num].char_handle = BUILD_UINT16(pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i+3],\
											 pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 4]);
				//characteristic uuid length
            	Mychar.char_Grop[Mychar.num].char_uuidlen = pMsg->msg.readByGrpTypeRsp.len - 5;

				//uuid
            	Mychar.char_Grop[Mychar.num].uuid = BUILD_UINT16(pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 5],
										 pMsg->msg.readByGrpTypeRsp.pDataList[pMsg->msg.readByGrpTypeRsp.len * i + 6]);
//				PRINT("属性与 或的结果;%x\r\n",attribute);
				if(Mychar.char_Grop[Mychar.num].char_properties & (GATT_PROP_WRITE | GATT_PROP_READ))
				{
					centralCharHdl = Mychar.char_Grop[Mychar.num].char_handle;
					PRINT("有读写属性的charhandle:%04x\r\n",centralCharHdl);
				}

				if(Mychar.char_Grop[Mychar.num].char_properties & GATT_PROP_NOTIFY)
				{
					centralCCCDHdl = Mychar.char_Grop[Mychar.num].char_handle;
					PRINT("notify handle:%04x\r\n",Mychar.char_Grop[Mychar.num].char_handle);
				}
				Mychar.num++;
				PRINT("\r\n");
            }
   }

Mychar是自己定义的一个结构体,用来存储char的一些参数。在适当的位置增加打印信息就可以在串口助手看到每个servechar的UUID和句柄了。嗯~这里还要说一下,虽然UUID拿到了,但是BLE协议栈在主从机交互的时候是不使用UUID的,读写某个UUID,是通过该UUID对应的句柄来进行交互。
6、好了,不知道把这个经验放在哪里,又怕自己忘掉,就在这个平台简单记录一下吧,自己摸索了一两天,也希望对初入BLE蓝牙协议栈的小伙伴有一点帮助。

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值