一、通过SimpleBLECentral工程分析CC2541作为主机时是如何发现从机的服务和特征值的,以及读取特征值的具体过程

二、服务和特征值

1、一个工程可以有多个服务,比如按键服务、心率计服务、温度计服务。(服务都是自己定义的,如新增的服务simpleprofile)

2、一个服务可以有多个特征值,特征值是主从机传输数据的媒介,像运人渡河的小船

3、如果主机要想获得按键服务的特征值,必须先发现按键服务、再获得按键服务的特征值句柄、再根据特征值句柄获取特征值

三、主机程序代码解析

发现服务特征值部分:

1、串口工具中输入AT+CON1,主从机连接后,主机会判断是否之前获取过特征值句柄。如果没有,则产生“START_DISCOVERY_EVT事件”开始发现服务

    case GAP_LINK_ESTABLISHED_EVENT:{if ( pEvent->gap.hdr.status == SUCCESS ){          simpleBLEState = BLE_STATE_CONNECTED;simpleBLEConnHandle = pEvent->linkCmpl.connectionHandle;simpleBLEProcedureInProgress = TRUE;    // If service discovery not performed initiate service discoveryif ( simpleBLECharHdl == 0 )  //是否之前获得过特征值句柄{osal_start_timerEx( simpleBLETaskId, START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );}LCD_WRITE_STRING( "Connected", HAL_LCD_LINE_1 );SerialPrintString("Connected: ");LCD_WRITE_STRING( bdAddr2Str( pEvent->linkCmpl.devAddr ), HAL_LCD_LINE_2 );   SerialPrintString((uint8*) bdAddr2Str( pEvent->linkCmpl.devAddr ));SerialPrintString("\r\n");}else{simpleBLEState = BLE_STATE_IDLE;simpleBLEConnHandle = GAP_CONNHANDLE_INIT;simpleBLERssi = FALSE;simpleBLEDiscState = BLE_DISC_STATE_IDLE;LCD_WRITE_STRING( "Connect Failed", HAL_LCD_LINE_1 );SerialPrintString("Connect Failed: ");LCD_WRITE_STRING_VALUE( "Reason:", pEvent->gap.hdr.status, 10, HAL_LCD_LINE_2 );SerialPrintValue("Reason:",  pEvent->gap.hdr.status,10);}}break;

①产生的START_DISCOVERY_EVT事件交给下面事件处理函数执行

  if ( events & START_DISCOVERY_EVT ){simpleBLECentralStartDiscovery( );return ( events ^ START_DISCOVERY_EVT );}

②simpleBLECentralStartDiscovery函数具体如下:

static void simpleBLECentralStartDiscovery( void )
{//uint8 uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),//                                 HI_UINT16(SIMPLEPROFILE_SERV_UUID) };uint8 uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SimpleKeysService),HI_UINT16(SimpleKeysService) };// Initialize cached handlessimpleBLESvcStartHdl = simpleBLESvcEndHdl = simpleBLECharHdl = 0;simpleBLEDiscState = BLE_DISC_STATE_SVC;// Discovery simple BLE serviceGATT_DiscPrimaryServiceByUUID( simpleBLEConnHandle,uuid,ATT_BT_UUID_SIZE,simpleBLETaskId );
}

默认发现的是UUID为FFF0的服务,因为SIMPLEPROFILE_SERV_UUID被赋值为FFF0(这一步决定发现什么样的服务。若将UUID改为FFE0,则发现key press state服务)

3、发现服务后,进入发现服务的回调函数

static void simpleBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )
{attReadByTypeReq_t req;if ( simpleBLEDiscState == BLE_DISC_STATE_SVC ){// Service found, store handlesif ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&pMsg->msg.findByTypeValueRsp.numInfo > 0 ){simpleBLESvcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;simpleBLESvcEndHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle;}// If procedure completeif ( ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP  && pMsg->hdr.status == bleProcedureComplete ) ||( pMsg->method == ATT_ERROR_RSP ) ){if ( simpleBLESvcStartHdl != 0 ){// Discover characteristicsimpleBLEDiscState = BLE_DISC_STATE_CHAR;req.startHandle = simpleBLESvcStartHdl;req.endHandle = simpleBLESvcEndHdl;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);req.type.uuid[0] = LO_UINT16(Keypressstate);req.type.uuid[1] = HI_UINT16(Keypressstate);GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId );}}}else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR )   //发现特征值1{// Characteristic found, store handleif ( pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 ){simpleBLECharHdl = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],pMsg->msg.readByTypeRsp.dataList[1] );LCD_WRITE_STRING( "Simple Svc Found", HAL_LCD_LINE_1 );SerialPrintString("Simple Svc Found\r\n");simpleBLEProcedureInProgress = FALSE;}simpleBLEDiscState = BLE_DISC_STATE_IDLE;//addosal_start_timerEx( simpleBLETaskId, READ_keypressstate_EVT, 1000 );//一定要延时一定时间,否则会读取特征值失败    }
}

①上个过程到这个过程,是怎么过来的?

第2步中GATT_DiscPrimaryServiceByUUID函数处理完后,会返回GATT_MSG_EVENT消息事件,此步函数的入口参数指针就指向了与GATT_MSG_EVENT相关的结构体。即上一步的返回的内容传给此步函数。

②存储被发现服务的起始句柄simpleBLESvcStartHdl和结束句柄simpleBLESvcEndHdl。接着进入特征值的发现,将特征值1的UUID(即0xFFF1,修改为对应服务中的UUID)赋值给结构体req中的元素,然后执行GATT_ReadUsingCharUUID( simpleBLEConnHandle, &req, simpleBLETaskId )

4、接着会再次进入上面的函数simpleBLEGATTDiscoveryEvent。由于已经发现了服务,直接跳到else if处,然后通过simpleBLECharHdl=BUILD_UNIT16()函数来获取特征值1的特征值句柄,并将特征值句柄保存下来,随时可用来操作特征值(先发现服务,再发现服务中的特征值),如下

  else if ( simpleBLEDiscState == BLE_DISC_STATE_CHAR )   //发现特征值1{// Characteristic found, store handleif ( pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 ){simpleBLECharHdl = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],pMsg->msg.readByTypeRsp.dataList[1] );LCD_WRITE_STRING( "Simple Svc Found", HAL_LCD_LINE_1 );SerialPrintString("Simple Svc Found\r\n");simpleBLEProcedureInProgress = FALSE;}simpleBLEDiscState = BLE_DISC_STATE_IDLE;//addosal_start_timerEx( simpleBLETaskId, READ_keypressstate_EVT, 1000 );//一定要延时一定时间,否则会读取特征值失败。通过这一步才会有后面的Read rsp:0    } 

①此处打印出Simple Svc Found,产生READ_keypressstate_EVT事件

②发现特征值的响应的数据在pMSG->msg.readByTypeRsp中,GO TO响应数据结构体readByTypeRsp,如下:

/*** Read By Type Response format.*/
typedef struct
{uint8 numPairs;                 //!< Number of attribute handle-UUID pairs founduint8 len;                      //!< Size of each attribute handle-value pairuint8 dataList[ATT_MTU_SIZE-2]; //!< List of 1 or more attribute handle-value pairs
} attReadByTypeRsp_t;

>numPairs表示返回特征值的个数

>Len表示dataList数据长度,固定为7B(7个字节)

>dataList[ATT_MTU_SIZE-2]表示特征值数据,dataList数组的第0,1字节是属性声明句柄,第二字节表示属性权限,第3,4字节是特征值句柄,最后5,6字节表示UUID

>ATT_MTU_SIZE-2表示dataList数组大小,默认为21B。一个特征值数据7B,如果发现的特征值在3个以内,则一个dataList足够,如果超过3个,则需要多个响应

读取特征值部分:

5、上一步产生的事件,在此步执行对应的事件处理函数。SimpleBLECentral.c中处理函数如下:

  //读特征值时添加  if ( events & READ_keypressstate_EVT )  {   // Do a read  attReadReq_t req;  uint8 status;  req.handle = simpleBLECharHdl;  status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );  if ( status == SUCCESS )  {  simpleBLEProcedureInProgress = TRUE;  }        return ( events ^ READ_keypressstate_EVT );  }  

①调用GATT_ReadCharValue读特征值函数,让主机读取从机特征值中存储值,从而产生SYS_EVENT_MSG事件

②具体过程:GATT应答了这个读特征值请求(这里应该只从机应答了读请求),从机返回值SUCCESS(0x00),向下告知BLE有生意了,该干活了。于是BLE协议栈在下次建立连接时,发送获取数据指令个service,于是就把特征值中存储值传给了client(主机),当client接收到数据时,BLE就会把数据打包成一个消息(OSAL message),通过出纳GATT返回给主机的应用程序。消息内包含GATT_MSG_EVENT和修改了的ATT_READ_RSP。应用程序接收到了从OSAL来的SYS_EVENT_MSG事件,就知道数据接收到了,应用程序接收消息,调用函数simpleBLECentral_ProcessOSALMsg,拆包检查,就可以取走消息里面的数据了,最后应用程序把包装好的消息给销毁,这就是整数据传送过程

6、产生的事件交给事件处理函数执行

  if ( events & SYS_EVENT_MSG ){uint8 *pMsg;if ( (pMsg = osal_msg_receive( simpleBLETaskId )) != NULL ){simpleBLECentral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );// Release the OSAL messageVOID osal_msg_deallocate( pMsg );}// return unprocessed eventsreturn (events ^ SYS_EVENT_MSG);}

①进入simpleBLECentral_ProcessOSALMsg函数就会执行下面一句

static void simpleBLECentral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{switch ( pMsg->event ){case KEY_CHANGE:simpleBLECentral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );break;case GATT_MSG_EVENT:simpleBLECentralProcessGATTMsg( (gattMsgEvent_t *) pMsg );break;}
}

②进入simpleBLECentralProcessGATTMsg函数进行拆包分析数据

static void simpleBLECentralProcessGATTMsg( gattMsgEvent_t *pMsg )
{if ( simpleBLEState != BLE_STATE_CONNECTED )  //以防GATT的消息来了之后的连接已断开,如果断开连接就忽略该消息{// In case a GATT message came after a connection has dropped,// ignore the messagereturn;}static int dataCount=0;if ( pMsg->method == ATT_HANDLE_VALUE_NOTI ||pMsg->method == ATT_HANDLE_VALUE_IND ){attHandleValueNoti_t noti;dataCount = dataCount+ 1;LCD_WRITE_STRING_VALUE( "Data Cnt: ", dataCount, 10, HAL_LCD_LINE_5 );noti.handle = pMsg->msg.handleValueNoti.handle;noti.len = pMsg->msg.handleValueNoti.len;osal_memcpy(¬i.value, &pMsg->msg.handleValueNoti.value,noti.len);// osal_memcpy(¬i.value, &pMsg->msg.handleValueNoti.value,pMsg->msg.handleValueNoti.len);sbpSerialAppWrite(noti.value,noti.len);}//重点关注此段程序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;LCD_WRITE_STRING_VALUE( "Read Error", status, 10, HAL_LCD_LINE_1 );SerialPrintValue("Read Error", status, 10);SerialPrintString("\r\n");}else{//取出消息包中从机发送过来的数据,并将值赋给valueRead,最终显示出来uint8 valueRead = pMsg->msg.readRsp.value[0];LCD_WRITE_STRING_VALUE( "Read rsp:", valueRead, 10, HAL_LCD_LINE_1 );SerialPrintValue("Read rsp:", valueRead, 10);SerialPrintString("\r\n");}simpleBLEProcedureInProgress = FALSE;}  //重点关注此段程序else if ( ( pMsg->method == ATT_WRITE_RSP ) ||( ( pMsg->method == ATT_ERROR_RSP ) &&( pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ ) ) ){if ( pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP ){uint8 status = pMsg->msg.errorRsp.errCode;LCD_WRITE_STRING_VALUE( "Write Error", status, 10, HAL_LCD_LINE_1 );SerialPrintValue( "Write Error", status, 10);SerialPrintString("\r\n");}else{// After a succesful write, display the value that was written and increment valueuint8 temp=simpleBLECharVal;//LCD_WRITE_STRING_VALUE( "Write sent:", simpleBLECharVal, 10, HAL_LCD_LINE_1 );     SerialPrintValue( "Write sent:", temp, 10);SerialPrintString("\r\n");}simpleBLEProcedureInProgress = FALSE;    }/*ghostyu*/else if ( ( pMsg->method == ATT_PREPARE_WRITE_RSP ) || ( pMsg->method == ATT_EXECUTE_WRITE_RSP ) || ( ( pMsg->method == ATT_ERROR_RSP ) &&( pMsg->msg.errorRsp.reqOpcode == ATT_EXECUTE_WRITE_REQ ) ) ){static uint8 message_times = 0;//LCD_WRITE_STRING_VALUE( "Message_Times", ++message_times, 10, HAL_LCD_LINE_2 );//if ( pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP )if ( pMsg->method == ATT_ERROR_RSP)  {uint8 status = pMsg->msg.errorRsp.errCode;//LCD_WRITE_STRING_VALUE( "Write LC Error", status, 10, HAL_LCD_LINE_1 );}else if( pMsg->method == ATT_PREPARE_WRITE_RSP )            //bleTimeout status{//HalLcdWriteString("Write LC Tout!",HAL_LCD_LINE_1);}else{// After a succesful write, display the value that was written and increment value//HalLcdWriteString("Write LC Success!",HAL_LCD_LINE_1);//LCD_WRITE_STRING_VALUE( "Message_Times", ++message_times, 10, HAL_LCD_LINE_2 );}simpleBLEProcedureInProgress = FALSE;    //重要}else if ( simpleBLEDiscState != BLE_DISC_STATE_IDLE ){simpleBLEGATTDiscoveryEvent( pMsg );}}

四、实验结果:

五、总结:

1、读取特征值中数据0的程序流程:先获取服务UUID,再获取服务中特征值UUID,再根据特征值UUID获取特征值句柄,再根据特征值句柄读取特征值中存储的值。写特征值过程与此过程类似

2、TI给出的SimpleBLECentral工程默认通过UP键进行读、写特征值,而本文采用的方法是连接从机后自动读取特征值数据

CC2541之发现服务、特征值及读取特征值中存储值的详细过程相关推荐

  1. 树莓派python编程读取电压_《树莓派Python编程指南》——3.2 在结构体中存储值-阿里云开发者社区...

    本节书摘来自华章计算机<树莓派Python编程指南>一书中的第3章,第3.2节,作者:(美) Alex Bradbury Ben Everard更多章节内容可以访问云栖社区"华章 ...

  2. 【BLE】CC2541之发现服务与特征值

    本篇博文最后修改时间:2017年01月06日,11:06. 一.简介 本文以SimpleBLECentral工程为例,解析CC2541作为主机时是如何发现从机的服务和特征值的. 二.实验平台 协议栈版 ...

  3. JSF通过EL读取List中的值

    使用JSF开发时,经常遇到要把MBean中的一个List或Map中的值通过EL显示在页面上,EL中访问Map或List的方法如下: 假设FacesConfig中配置一个bean的MBean,它的类中定 ...

  4. python读取url中存储的数据_Python实现从URL地址提取文件名的方法

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  5. python读取url中存储的数据_python 给定URL 如何获取其内容,并将其保存至HTML文档。...

    获取URL的内容需要用到标准库urllib包,其中的request模块. import urllib.request url='http://www.baidu.com' response=urlli ...

  6. 超简单:解析 yml 类型(application.yml)配置文件 、springboot 工程读取 yml 文件中的值

    方法三是我觉得最简单的. 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 1. 工程结构: 2. 我要读取  application.yml 中属性 ...

  7. javascript读取excel中的时间的格式问题

    把excel表格导入到Mysql数据库时,发现在使用js-xlsx读取excel中的时间时会读取成一串时间数字,这串数字是excel自有的时间戳,直接使用`new Date()`并不能获取时间对象. ...

  8. python如何读取excel数据-python怎么读取excel中的数值

    最近测试过程中需要用到python读取excel用例数据,于是去了解和学习了下xlrd库,这里只记录使用过程中读取excel数据相关操作. 安装xlrd库(推荐学习:Python视频教程) 可以下载x ...

  9. python解析excel公式_[python][openpyxl]读取excel中公式的结果值

    要读取cell中的值,但是,如果cell中的值是一个公式,则读取出来的是公式.有时候我们希望读取到公式计算出来的结果,可以使用load_workbook()中的data_only属性. load_wo ...

最新文章

  1. 小程序网络最大并发限制解决思路
  2. python3使用requests模块完成get/post/代理/自定义header/自定义Cookie
  3. Java 线程池必知的8 大拒绝策略
  4. bootstrap 引用注意事项
  5. arcgis-online-python-scripts
  6. Python处理小学体育中的跑步计时数据并统计得分
  7. 《看聊天记录都学不会C#?太菜了吧》(3)变量:我大哥呢?$:小弟我罩着你!
  8. 众智日照分析软件_飞时达CAD日照分析计算软件FastSUN V14.0.1发布升级
  9. 【渝粤教育】国家开放大学2018年春季 7403-21T素质与思想政治教育 参考试题
  10. Windiws环境安装轻量级文件服务器ftpserver
  11. 【python】 类、对象的练习题
  12. 7-1 Programming in C is fun! (5 分)
  13. eclipse启动tomcat无法访问的解决方法(转)
  14. Java创建多线程的方法总结
  15. selenium官网下载地址以及相关介绍
  16. 数字信号处理——多速率信号处理
  17. 手机端html只允许竖屏,关于移动端页面强制竖屏的方法
  18. jmeter录制脚本后请求太多_使用Jmeter录制web脚本
  19. egret 微信小游戏 分享游戏截图
  20. 【2022 年“SPSSPRO 杯”数学中国数学建模网络挑战赛】A题 人员的紧急疏散-第二阶段23页论文

热门文章

  1. jmeter正则表达式提取器的用法和正则
  2. Exynos4412开发板
  3. python pack是什么意思_python pack布局
  4. 协鑫集成第二批1000台E-KwBe光伏储能设备即将启运澳洲
  5. 要以一种平和的心态对待那些“可爱”的木马病毒
  6. matlab premnmx归一化函数的使用 1、premnmx 预处理数据使数据的最小值和最大值分别为-1和1. [PN,minp,maxp,TN,mint,maxt] = premnmx(P,T)
  7. 凡客第二春 垂死挣扎还是绝地反击
  8. Scara机器人关节空间轨迹规划-机器人工具箱函数jtraj
  9. Qt设计机器人仿真控制器——按键控制机器人关节转动
  10. PHP在线客服即时通讯源码