当BLE设备收到配对设备发来的更新特征值请求时会产生一个GATTC_WRITE_CMD_IND消息,这是GATT Task messages里的一种。

/// GATT Task messages
enum gattc_msg_id
{/* Default event *//// Command Complete eventGATTC_CMP_EVT = KE_FIRST_MSG(TASK_GATTC),/* ATTRIBUTE CLIENT *//// Server configuration requestGATTC_EXC_MTU_CMD,/*Discover All Services *//*Discover Services by Service UUID*//*Find Included Services*//*Discover Characteristics by UUID*//*Discover All Characteristics of a Service*//*Discover All Characteristic Descriptors*//// Discovery commandGATTC_DISC_CMD,/* GATT -> HL: Events to Upper layer *//*Discover All Services*//// Discovery services indicationGATTC_DISC_SVC_IND,/*Find Included Services*//// Discover included services indicationGATTC_DISC_SVC_INCL_IND,/*Discover All Characteristics of a Service*//// Discover characteristic indicationGATTC_DISC_CHAR_IND,/*Discover All Characteristic Descriptors*//// Discovery characteristic descriptor indicationGATTC_DISC_CHAR_DESC_IND,/*Read Value*//*Read Using UUID*//*Read Long Value*//*Read Multiple Values*//// Read commandGATTC_READ_CMD,/// Read responseGATTC_READ_IND,/*Write without response*//*Write without response with Authentication*//*Write Characteristic Value*//*Signed Write Characteristic Value*//*Write Long Characteristic Value*//*Characteristic Value Reliable Write*//*Write Characteristic Descriptors*//*Write Long Characteristic Descriptors*//*Characteristic Value Reliable Write*//// Write command requestGATTC_WRITE_CMD,/* Cancel / Execute pending write operations *//// Execute write characteristic requestGATTC_EXECUTE_WRITE_CMD,/* Reception of an indication or notification from peer device. *//// peer device triggers an event (indication or notification)GATTC_EVENT_IND,/// Registration to peer device events (Indication/Notification).GATTC_REG_TO_PEER_EVT_CMD,/* ATTRIBUTE SERVER *//*Notify Characteristic*//*Indicate Characteristic*//// send an event to peer deviceGATTC_SEND_EVT_CMD,/* Indicate that write operation is requested. *//// Write command indicated to upper layers.GATTC_WRITE_CMD_IND,/* Service Changed Characteristic Indication *//*** Send a Service Changed indication to a device* (message structure is struct gattm_svc_changed_ind_req)*/GATTC_SEND_SVC_CHANGED_CMD,/*** Inform the application when sending of Service Changed indications has been* enabled or disabled*/GATTC_SVC_CHANGED_CFG_IND,/* Confirm write command execution. *//// Write command confirmation from upper layers.GATTC_WRITE_CMD_CFM,/* Indicate that a read operation is requested. *//// Read command indicated to upper layers.GATTC_READ_CMD_IND,
};

GATTC_WRITE_CMD_IND消息会触发回调函数gattc_write_cmd_ind_handler()。该函数位于custs1_task.c文件。

/// Default State handlers definition
const struct ke_msg_handler custs1_connected[] =
{{GATTC_WRITE_CMD_IND,           (ke_msg_func_t)gattc_write_cmd_ind_handler},{GATTC_CMP_EVT,                 (ke_msg_func_t)gattc_cmp_evt_handler},{CUSTS1_VAL_NTF_REQ,            (ke_msg_func_t)custs1_val_ntf_req_handler},{CUSTS1_VAL_SET_REQ,            (ke_msg_func_t)custs1_val_set_req_handler},{CUSTS1_VAL_IND_REQ,            (ke_msg_func_t)custs1_val_ind_req_handler},
};

gattc_write_cmd_ind_handler()把消息从GAP层传递给app_entry_point_handler()APP接入点函数,该函数负责把消息再次传递给用户APP应用函数。

/******************************************************************************************* @brief Handles reception of the @ref GATT_WRITE_CMD_IND message.* @param[in] msgid Id of the message received (probably unused).* @param[in] param Pointer to the parameters of the message.* @param[in] dest_id ID of the receiving task instance (probably unused).* @param[in] src_id ID of the sending task instance.* @return If the message was consumed or not.*****************************************************************************************/
static int gattc_write_cmd_ind_handler(ke_msg_id_t const msgid,struct gattc_write_cmd_ind const *param,ke_task_id_t const dest_id,ke_task_id_t const src_id)
{uint16_t att_idx, value_hdl;uint8_t status = PRF_ERR_OK;uint8_t uuid[GATT_UUID_128_LEN];uint8_t uuid_len;att_size_t len;uint8_t *value;uint16_t perm;if (KE_IDX_GET(src_id) == custs1_env.con_info.conidx){att_idx = param->handle - custs1_env.shdl;//uint16_t handle:struct gattc_write_cmd_ind结构体成员;custs1_env.shdl:struct custs1_env_tagd结构体成员,服务启动句柄
//handle - custs1_env.shdl是求出索引值,后面用于判断是哪个特征if( att_idx < custs1_env.max_nb_att )//判断handle是否合法{// Retrieve UUID    检索 UUIDattmdb_att_get_uuid(param->handle, &uuid_len, &(uuid[0]));// In case of Client Characteristic Configuration, check validity and set value//在客户端特征配置的情况下,检查有效性和设置值if ((uint16_t)*(uint16_t *)&uuid[0] == ATT_DESC_CLIENT_CHAR_CFG){// Find the handle of the Characteristic Valuevalue_hdl = get_value_handle( param->handle );if ( !value_hdl ) ASSERT_ERR(0);// Get permissions to identify if it is NTF or IND.//获取权限以识别它是 NTF(通告) 还是 IND(指示)。attmdb_att_get_permission(value_hdl, &perm);status = check_client_char_cfg(PERM_IS_SET(perm, NTF, ENABLE), param);if (status == PRF_ERR_OK){// Set Client Characteristic Configuration value//设置客户端特征配置值status = attmdb_att_set_value(param->handle, param->length, (uint8_t*)&(param->value[0]));}}else{// Call the application function to validate the value before it is written to database//在将值写入数据库之前调用应用程序函数以验证值uint8_t i = 0;status = PRF_ERR_OK;while( cust_prf_funcs[i].task_id != TASK_NONE ){if( cust_prf_funcs[i].task_id == dest_id){if ( cust_prf_funcs[i].value_wr_validation_func != NULL)//如果用户定义了验证回调函数{status = cust_prf_funcs[i].value_wr_validation_func(att_idx, param->last, param->offset, param->length, (uint8_t *)&param->value[0]);break;} else i++;} else i++;}if (status == PRF_ERR_OK)//验证通过{if (param->offset == 0)//gattc_write_cmd_ind结构体下的成员,该结构体用于通知一个数据库修改操作已经被配对设备请求//offset,offset at which the data has to be written,要修改的数组的偏移值{// Set value in the database  如果偏移值是0,把值全部写入数据库//param->length: uint16_t length;  Data length to be written,  将要写入的数据长度;//(uint8_t *)&param->value[0]: uint8_t  value[__ARRAY_EMPTY],  将要写入属性数据库的数据;status = attmdb_att_set_value(param->handle, param->length, (uint8_t *)&param->value[0]);}else{// Update value in the database,如果偏移值不是0,则只更新部分数据status = attmdb_att_update_value(param->handle, param->length, param->offset,(uint8_t *)&param->value[0]);}}}if( (param->last) && (status == PRF_ERR_OK) )//bool last,通知它是多准备写入请求的最后一个请求,意即本次数据全部写完{// Get the value size and data. Can not use param->value, it might be a long value//获取值大小和数据。 不能使用 param->value,它可能是一个长值if( attmdb_att_get_value(param->handle, &len, &value) != ATT_ERR_NO_ERROR ){ASSERT_ERR(0);}// Inform APP//新建一个CUSTS1_VAL_WRITE_IND 消息的结构体,然后填充数据struct custs1_val_write_ind *req_id = KE_MSG_ALLOC_DYN(CUSTS1_VAL_WRITE_IND,custs1_env.con_info.appid, custs1_env.con_info.prf_id,custs1_val_write_ind,len);memcpy(req_id->value, (uint8_t*)&value[0], len);req_id->conhdl = gapc_get_conhdl(custs1_env.con_info.conidx);req_id->handle = att_idx;req_id->length = len;ke_msg_send(req_id);//把消息发给APP,该消息内含数据长度和全部数据}}else//Inexistent handle for sending a read/write characteristic request,不存在的handle用于发送读/写特征请求{status = PRF_ERR_INEXISTENT_HDL;}// Send Write Response only if client requests for RSP (ignored when 'Write Without Response' is used)if (param->response == 1){// Send Write Responseatts_write_rsp_send(custs1_env.con_info.conidx, param->handle, status);}}return (KE_MSG_CONSUMED);
}

函数通过 if( (param->last) && (status == PRF_ERR_OK) )判断数据全部接收完后,调用attmdb_att_get_value(param->handle, &len, &value)获取数据和数据长度。

然后新建一个CUSTS1_VAL_WRITE_IND 消息的结构体,并填充刚才接收的数据,最后通过调用ke_msg_send()函数把这个消息发给用户应用APP处理,该消息内含数据长度和全部数据。这里 Inform APP,很重要,把消息从GAP层发给了用户应用APP处理。

收到并处理该消息的是app_entry_point_handler()函数,该函数在sdk_app_api目录下的app_entry_point.c文件里。sdk_app_api目录的文件是负责连接底层SDK和用户APP的接口。

app_entry_point_handler()是用户应用程序入口点处理函数。


/******************************************************************************************* @brief Application entry point handler.* @param[in] msgid       Message Id* @param[in] param       Pointer to message* @param[in] dest_id     Destination task Id* @param[in] src_id      Source task Id* @return Message status*****************************************************************************************/
int app_entry_point_handler(ke_msg_id_t const msgid,void const *param,ke_task_id_t const dest_id,ke_task_id_t const src_id)
{int i = 0;enum ke_msg_status_tag process_msg_handling_result; 定义一个枚举类型,处理消息时返回的状态。Status returned by a task when handling a messagewhile (i < (sizeof(app_process_handlers) / sizeof(process_event_func_t)))//回调函数数组挨个轮询检查是否已经处理过{ASSERT_ERR(app_process_handlers[i]);if (app_process_handlers[i](msgid, param, dest_id, src_id, &process_msg_handling_result) == PR_EVENT_HANDLED)//如果已经处理过则消息到此终止。{return (process_msg_handling_result);}i++;}//如果消息未处理过,则调用回调函数app_process_catch_rest_cb(),该函数在user_callback_config.h文件里定义为 = user_catch_rest_hndl()函数//user cannot do anything else than consume the messageif (app_process_catch_rest_cb != NULL){app_process_catch_rest_cb(msgid,param, dest_id, src_id);}return (KE_MSG_CONSUMED);
};

app_entry_point_handler()函数里判断消息是否已经处理,如果处理了则退出终止消息传递流程。如果没有处理,则调用app_process_catch_rest_cb()回调函数进行处理。处理完毕后,return (KE_MSG_CONSUMED),告知该消息已经处理完毕。

文件user_callback_config.h里定义了回调函数app_process_catch_rest_cb = user_catch_rest_hndl();

static const catch_rest_event_func_t app_process_catch_rest_cb = (catch_rest_event_func_t)user_catch_rest_hndl;

也就是说GAP层发来的消息通过app_entry_point_handler()接入点处理函数后调用了用户定义的回调函数user_catch_rest_hndl来处理。该函数简介里说明,是处理未被SDK内部机制处理的消息。下面看一下user_catch_rest_hndl这个回调函数都做了啥:

/******************************************************************************************* @brief Handles the messages that are not handled by the SDK internal mechanisms.* //处理未被SDK内部机制处理的消息* @param[in] msgid   Id of the message received.* @param[in] param   Pointer to the parameters of the message.* @param[in] dest_id ID of the receiving task instance.* @param[in] src_id  ID of the sending task instance.* @return void****************************************************************************************
*/
void user_catch_rest_hndl(ke_msg_id_t const msgid,void const *param,ke_task_id_t const dest_id,ke_task_id_t const src_id)
{switch(msgid){case CUSTS1_VAL_WRITE_IND:{struct custs1_val_write_ind const *msg_param = (struct custs1_val_write_ind const *)(param);switch (msg_param->handle){case CUST1_IDX_CONTROL_POINT_VAL:user_custs1_ctrl_wr_ind_handler(msgid, msg_param, dest_id, src_id);break;case CUST1_IDX_LED_STATE_VAL:user_custs1_led_wr_ind_handler(msgid, msg_param, dest_id, src_id);break;case CUST1_IDX_ADC_VAL_1_NTF_CFG:user_custs1_adc_val_1_cfg_ind_handler(msgid, msg_param, dest_id, src_id);break;case CUST1_IDX_BUTTON_STATE_NTF_CFG:user_custs1_button_cfg_ind_handler(msgid, msg_param, dest_id, src_id);break;case CUST1_IDX_INDICATEABLE_IND_CFG:user_custs1_long_val_cfg_ind_handler(msgid, msg_param, dest_id, src_id);break;case CUST1_IDX_LONG_VALUE_NTF_CFG:user_custs1_long_val_cfg_ind_handler(msgid, msg_param, dest_id, src_id);break;case CUST1_IDX_LONG_VALUE_VAL:user_custs1_long_val_wr_ind_handler(msgid, msg_param, dest_id, src_id);break;default:break;}} break;case CUSTS1_VAL_NTF_CFM:{struct custs1_val_ntf_cfm const *msg_param = (struct custs1_val_ntf_cfm const *)(param);switch (msg_param->handle){case CUST1_IDX_ADC_VAL_1_VAL:break;case CUST1_IDX_BUTTON_STATE_VAL:break;case CUST1_IDX_LONG_VALUE_VAL:break;default:break;}} break;case CUSTS1_VAL_IND_CFM:{struct custs1_val_ind_cfm const *msg_param = (struct custs1_val_ind_cfm const *)(param);switch (msg_param->handle){case CUST1_IDX_INDICATEABLE_VAL:break;default:break;}} break;case GAPC_PARAM_UPDATED_IND:{// Cast the void pointer to the appropriate message structurestruct gapc_param_updated_ind const *msg_param = (struct gapc_param_updated_ind const *)(param);// Check if updated Conn Params filled to preffered onesif ((msg_param->con_interval >= user_connection_param_conf.intv_min) &&(msg_param->con_interval <= user_connection_param_conf.intv_max) &&(msg_param->con_latency == user_connection_param_conf.latency) &&(msg_param->sup_to == user_connection_param_conf.time_out)){}} break;default:break;}
}

根据msgid,选择对应分支进行处理。现在是一个特征值改变的指示消息CUSTS1_VAL_WRITE_IND (Indicate that the characteristic value has been written)。

进入CUSTS1_VAL_WRITE_IND分支后,根据消息具体内容的handle选择对应的服务回调函数来处理。这里是user_custs1_led_wr_ind_handler()函数。
下面看一下这个回调函数user_custs1_led_wr_ind_handler()。

void user_custs1_led_wr_ind_handler(ke_msg_id_t const msgid,struct custs1_val_write_ind const *param,ke_task_id_t const dest_id,ke_task_id_t const src_id)
{uint8_t val = 0;memcpy(&val, &param->value[0], param->length);if (val == CUSTS1_LED_ON)GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_PIN);else if (val == CUSTS1_LED_OFF)GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_PIN);
}/// Parameters of the @ref CUSTS1_VAL_WRITE_IND massage
struct custs1_val_write_ind
{/// Connection handleuint16_t conhdl;/// Handle of the attribute that has to be writtenuint16_t handle;/// Data length to be writtenuint16_t length;/// Data to be written in attribute databaseuint8_t  value[__ARRAY_EMPTY];
};

把传递来的消息具体数据内容*param结构体里的元素uint8_t value[__ARRAY_EMPTY]复制到val变量里。当然这里应该是param->length = 1,也就是数组只有一个元素。
复制完毕后,根据这个值来决定GPIO口输出状态。这里是点亮或熄灭LED灯,当然也可以是去做其他事情。

以上是整个BLE设备收到其它配对设备比如手机等发来的消息后的处理流程。

DA14580BLE接收流程分析相关推荐

  1. live555 接收rtsp视频流流程分析

    live555 接收rtsp视频流流程分析 RTSP交互流程 C表示RTSP客户端,S表示RTSP服务端 ① C->S: OPTIONrequest         //询问S有哪些方法可用 S ...

  2. GO Negotiation流程分析

    本文为<深入理解Android Wi-Fi.NFC和GPS卷>读书笔记,Android源码为Android 5.1 P2pStateMachine收到P2P_PROV_DISC_PBC_R ...

  3. Provision Discovery流程分析

    本文为<深入理解Android Wi-Fi.NFC和GPS卷>读书笔记,Android源码为Android 5.1 P2pStateMachine的ProvisionDiscoverySt ...

  4. VLC架构及流程分析

    0x00 前置信息 VLC是一个非常庞大的工程,我从它的架构及流程入手进行分析,涉及到一些很细的概念先搁置一边,日后详细分析. 0x01 源码结构(Android Java相关的暂未分析) # bui ...

  5. 解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  6. 转:Android之 MTP框架和流程分析

    2019独角兽企业重金招聘Python工程师标准>>> 转载:http://www.cnblogs.com/skywang12345/p/3474206.html 概要 本文的目的是 ...

  7. 添加用户信息的方法java_添加用户的流程分析

    添加用户的流程分析 Settings的上层接口 settings中的文件: /android/packages/apps/Settings/src/com/android/settings/users ...

  8. igmpproxy_Linux IGMP PROXY 学习笔记 之二 igmp proxy的处理流程分析

    上一节中我们分析了linux kernel中igmp proxy相关的数据结构与实现需求分析,本节我们分析kernel中对组播数据流和组播数据的处理流程. 对于目的ip地址为组播地址的数据,可以分为两 ...

  9. 【网络安全】Metasploit生成的Shellcode的导入函数解析以及执行流程分析(2)

    密码破解的利器--彩虹表(rainbow table) 确定 shellcode 依赖于哪些导入将使研究人员进一步了解其其余逻辑.不用动态分析shellcode,并且考虑到研究人员已经弄清楚了上面的哈 ...

最新文章

  1. 介绍一款开源的类Excel电子表格软件
  2. 利用async和await异步操作解决node.js里面fs模块异步读写,同步结果的问题
  3. 判断一个变量是不是指针
  4. 域电脑不能显示桌面_学会这些电脑操作,工作更高效,摸鱼更舒适!
  5. yuzu模拟器linux,Yuzu Early Acces
  6. vue2使用$set()使对象新增属性后触发视图更新
  7. 5.SpringMVC
  8. BugkuCTF-PWN题pwn1-瑞士军刀
  9. 一文带你搞懂从动态代理实现到Spring AOP
  10. C++---堆代码实现
  11. 用python语言实现反恐精英基础版-案例
  12. 【Vue】前端跨域解决方法
  13. python爬虫怎么爬同一个网站的多页数据-不踩坑的Python爬虫:如何在一个月内学会爬取大规模数据...
  14. 在ubuntu kylin优麒麟中运行league of legends英雄联盟(LOL)
  15. 计算机网络三种模型(OSI模型、TCP/IP模型、五层通用模型)、各层作用
  16. codeforces 407C Curious Array 数学
  17. 大数据分析图形绘制如何进行?
  18. couldn‘t find “libijkffmpeg.so“
  19. [iOS、Unity、Android] 浅谈闭包的使用方法
  20. 图像算法研究---背景虚化算法

热门文章

  1. 《Total Commander:万能文件管理器》——第10章.中文相关问题
  2. OpenCV系列之理解特征 | 三十六
  3. 全球移动互联网大会高德正能量:寻人信息平台已上线 雅安救援行动进行时
  4. 计算机派位能选到好学校吗,参加公办学校电脑派位 摇号失败可回原学区就读...
  5. 做线雕多久能恢复自然_线雕多久能恢复自然?
  6. 微拉美多久可以恢复,微拉美提升能保持几年
  7. PLC编程顺控逻辑框架
  8. 7个问题带你了解西门子人机界面
  9. oracle to_char(_fm09),oracle to_Char fm 函数
  10. 如何写好一份晋升PPT(附PPT模板)