Firefly开源社区
标题: 对profile QPPS的分析理解 [打印本页]
作者: 安安 时间: 2015-4-17 16:17
标题: 对profile QPPS的分析理解
今天写的东西就深入一点了,是一个昆天科的定制协议的理解,即QPPS profile。这个profile用于手机与模块的透传,非常的实用,因为我们在BLE上使用最多的就是透传。
首先烧录程序,完成后用light blue查看服务。
在profile中只有一个服务,UUID为128位的UUID,但是可以转换为16位UUID:FEE9,在这个服务下包含有8个特征,其中第一个特征UUID为9600结尾的一个128位UUID,是用于接收APP发送的数据的特征,在APP端显示为可写的一个特征值。后面是7个用于发送数据到APP的特征值,特征值UUID后四位由9601递增至9607,一共有七个特征值。这个特征值的数目实际是可以修改的,可以改为1~7,在usr_config.h中有如下定义,更改QPPS_NOTIFY_NUM的数值就可以改变发送特征值的数目。- ///Quintic private profile Role
- #define CFG_PRF_QPPS
- #define QPPS_NOTIFY_NUM 7
- //#define CFG_TASK_QPPS TASK_PRF1
复制代码
app向第一个特征值中写入数据,发送,则系统会调用到gatt_write_cmd_ind_handler函数处理APP端的操作命令。
- static int gatt_write_cmd_ind_handler(ke_msg_id_t const msgid,
- struct gatt_write_cmd_ind const *param,
- ke_task_id_t const dest_id,
- ke_task_id_t const src_id)
- {
- uint8_t status = PRF_ERR_OK;
- if (param->conhdl == qpps_env.conhdl)
- {
- // Client Char. Configuration
- uint8_t char_index = param->handle - (qpps_env.shdl + QPPS_IDX_VAL_NTF_CFG);
- if ((param->handle > (qpps_env.shdl + QPPS_IDX_VAL_CHAR)) && ((char_index % 3) == 0))
- {
- uint16_t value = 0x0000;
- //Extract value before check
- memcpy(&value, &(param->value), sizeof(uint16_t));
- if ((value == PRF_CLI_STOP_NTFIND) || (value == PRF_CLI_START_NTF))
- {
- if (value == PRF_CLI_STOP_NTFIND)
- {
- qpps_env.features &= ~(QPPS_VALUE_NTF_CFG << (char_index / 3));
- }
- else //PRF_CLI_START_NTF
- {
- qpps_env.features |= QPPS_VALUE_NTF_CFG << (char_index / 3);
- }
- }
- else
- {
- status = PRF_APP_ERROR;
- }
- if (status == PRF_ERR_OK)
- {
- uint8_t *old_value;
- atts_size_t length;
- attsdb_att_get_value(param->handle, &length, &old_value);
- if (value != co_read16p(old_value))
- {
- //Update the attribute value
- attsdb_att_set_value(param->handle, sizeof(uint16_t), (uint8_t *)&value);
- if(param->last)
- {
- //Inform APP of configuration change
- struct qpps_cfg_indntf_ind * ind = KE_MSG_ALLOC(QPPS_CFG_INDNTF_IND,
- qpps_env.appid, TASK_QPPS,
- qpps_cfg_indntf_ind);
- ind->char_index = (char_index / 3);
- memcpy(&ind->cfg_val, &value, sizeof(uint16_t));
- ke_msg_send(ind);
- }
- }
- }
- }
- else if (param->handle == (qpps_env.shdl + QPPS_IDX_RX_DATA_VAL))
- {
- if (param->length <= QPP_DATA_MAX_LEN)
- {
- //inform APP of configuration change
- struct qpps_data_val_ind * ind = KE_MSG_ALLOC_DYN(QPPS_DAVA_VAL_IND,
- qpps_env.appid,
- TASK_QPPS,
- qpps_data_val_ind, param->length);
- memcpy(&ind->conhdl, &(qpps_env.conhdl), sizeof(uint16_t));
- //Send received data to app value
- ind->length = param->length;
- memcpy(ind->data, param->value, param->length);
- ke_msg_send(ind);
- }
- else
- {
- status = QPPS_ERR_RX_DATA_EXCEED_MAX_LENGTH;
- }
- }
- else
- {
- status = QPPS_ERR_INVALID_PARAM;
- }
- }
- if (param->response)
- {
- //Send write response
- atts_write_rsp_send(qpps_env.conhdl, param->handle, status);
- }
- return (KE_MSG_CONSUMED);
- }
复制代码
由于app对特征值9600的写入操作实际上是对特征中QPPS_IDX_RX_DATA_VAL一值进行了写入操作,所以函数中以下代码就是经过了一系列的接收操作后,我们能在程序中看到的第一入口。程序中构建了一条标识为QPPS_DAVA_VAL_IND的消息,在信息中填入了从app处获取到的写入内容。
- if (param->handle == (qpps_env.shdl + QPPS_IDX_RX_DATA_VAL))
- {
- if (param->length <= QPP_DATA_MAX_LEN)
- {
- //inform APP of configuration change
- struct qpps_data_val_ind * ind = KE_MSG_ALLOC_DYN(QPPS_DAVA_VAL_IND,
- qpps_env.appid,
- TASK_QPPS,
- qpps_data_val_ind, param->length);
- memcpy(&ind->conhdl, &(qpps_env.conhdl), sizeof(uint16_t));
- //Send received data to app value
- ind->length = param->length;
- memcpy(ind->data, param->value, param->length);
- ke_msg_send(ind);
- }
- else
- {
- status = QPPS_ERR_RX_DATA_EXCEED_MAX_LENGTH;
- }
- }
- else
- {
- status = QPPS_ERR_INVALID_PARAM;
- }
- }
复制代码
通过查看QPPS_DAVA_VAL_IND,得知该消息处理函数为app_qpps_data_ind_handler。根据函数描述,app发送数据到开发板后,会在串口中输出数据,打开串口,设置好波特率(9600),可以看到app端发送数据的字节长度和第一个数据的16进制。如果需要取出完整数据,根据param->length把param->data数组的数据都取出来,就是完整的数据了。
- int app_qpps_data_ind_handler(ke_msg_id_t const msgid,
- struct qpps_data_val_ind *param,
- ke_task_id_t const dest_id,
- ke_task_id_t const src_id)
- {
- if (param->length > 0)
- {
- QPRINTF("len=%d, I%02X", param->length, param->data[0]);
- }
- QPRINTF("\r\n");
- return (KE_MSG_CONSUMED);
- }
复制代码
对于发送数据,昆天科做了一个很有意思的算法,让app在配置完所有的notify后持续发送递增数据到每一个发送特征值中,所以开启这个发送的办法在与打开所有的notify。我们看一下打开notify的操作,这个操作会触发gatt_write_cmd_ind_handler处理函数,代码片如下:
- uint8_t char_index = param->handle - (qpps_env.shdl + QPPS_IDX_VAL_NTF_CFG);
- if ((param->handle > (qpps_env.shdl + QPPS_IDX_VAL_CHAR)) && ((char_index % 3) == 0))
- {
- uint16_t value = 0x0000;
- //Extract value before check
- memcpy(&value, &(param->value), sizeof(uint16_t));
- if ((value == PRF_CLI_STOP_NTFIND) || (value == PRF_CLI_START_NTF))
- {
- if (value == PRF_CLI_STOP_NTFIND)
- {
- qpps_env.features &= ~(QPPS_VALUE_NTF_CFG << (char_index / 3));
- }
- else //PRF_CLI_START_NTF
- {
- qpps_env.features |= QPPS_VALUE_NTF_CFG << (char_index / 3);
- }
- }
- else
- {
- status = PRF_APP_ERROR;
- }
- if (status == PRF_ERR_OK)
- {
- uint8_t *old_value;
- atts_size_t length;
- attsdb_att_get_value(param->handle, &length, &old_value);
- if (value != co_read16p(old_value))
- {
- //Update the attribute value
- attsdb_att_set_value(param->handle, sizeof(uint16_t), (uint8_t *)&value);
- if(param->last)
- {
- //Inform APP of configuration change
- struct qpps_cfg_indntf_ind * ind = KE_MSG_ALLOC(QPPS_CFG_INDNTF_IND,
- qpps_env.appid, TASK_QPPS,
- qpps_cfg_indntf_ind);
- ind->char_index = (char_index / 3);
- memcpy(&ind->cfg_val, &value, sizeof(uint16_t));
- ke_msg_send(ind);
- }
- }
- }
- }
复制代码
当app开启notify的时候,会带来一个value,value代表着是开启还是关闭notify,所以首先要进行判断是哪一个特征中开启或者关闭了notify,然后判断是否需要进行开启和关闭操作(检查之前状态是否与当前操作冲突),如果需要开启或者关闭,则会发出一个标识为QPPS_CFG_INDNTF_IND的消息,消息中填入了操作的特征序号和值。
消息QPPS_CFG_INDNTF_IND的处理函数为app_qpps_cfg_indntf_ind_handler,此消息为指示性消息,notify已经在刚才 的处理中开启,所以本函数主要任务是指示了哪一个消息被开启或关闭,在判断所有的notify被开启后,函数调用了另一个函数app_test_send_data(app_qpps_env->tx_char_num - 1);,发送数据到9601~9607七个特征值去。
- int app_qpps_cfg_indntf_ind_handler(ke_msg_id_t const msgid,
- struct qpps_cfg_indntf_ind *param,
- ke_task_id_t const dest_id,
- ke_task_id_t const src_id)
- {
- if (app_qpps_env->conhdl == param->conhdl)
- {
- if (param->cfg_val == PRF_CLI_START_NTF)
- {
- QPRINTF("param->char_index %d\r\n",param->char_index);
- app_qpps_env->features |= (QPPS_VALUE_NTF_CFG << param->char_index);
- // App send data if all of characteristic have been configured
- QPRINTF("app_qpps_env->features 0x%X : 0x%X \r\n",app_qpps_env->features,app_qpps_env->tx_char_num);
- if (get_bit_num(app_qpps_env->features) == app_qpps_env->tx_char_num)
- {
- app_qpps_env->char_status = app_qpps_env->features;
- app_test_send_data(app_qpps_env->tx_char_num - 1);
- }
- }
- else
- {
- app_qpps_env->features &= ~(QPPS_VALUE_NTF_CFG << param->char_index);
- app_qpps_env->char_status &= ~(QPPS_VALUE_NTF_CFG << param->char_index);
- }
- }
- return (KE_MSG_CONSUMED);
- }
复制代码
函数通过对cnt计数和一些条件判断,对特征值依次发送数据。
- static void app_test_send_data(uint8_t max)
- {
- uint8_t cnt;
- for (cnt = 0; (max != 0) && cnt < app_qpps_env->tx_char_num; cnt++)
- {
- if ((app_qpps_env->char_status >> cnt) & QPPS_VALUE_NTF_CFG)
- {
- static uint8_t val[] = {0, '0', '1', '2','3','4','5','6','7','8','9','8','7','6','5','4','3','2','1','0'};
- // Increment the first byte for test
- val[0]++;
- max--;
- // Allow next notify until confirmation received in this characteristic
- app_qpps_env->char_status &= ~(QPPS_VALUE_NTF_CFG << cnt);
- app_qpps_data_send(app_qpps_env->conhdl, cnt, sizeof(val), val);
- }
- QPRINTF("send to cnt:%d,max %d\r\n",cnt,max);
- }
- QPRINTF("app_qpps_env->char_status: %X\r\n",app_qpps_env->char_status);
- }
复制代码
发送数据完成,系统会发出QPPS_DATA_SEND_CFM消息作为响应,于是在消息对应的处理函数app_qpps_data_send_cfm_handler中,再次发起消息发送。
- int app_qpps_data_send_cfm_handler(ke_msg_id_t const msgid,
- struct qpps_data_send_cfm *param,
- ke_task_id_t const dest_id,
- ke_task_id_t const src_id)
- {
- if (app_qpps_env->conhdl == param->conhdl && param->status == PRF_ERR_OK)
- {
- // Allow new notify
- app_qpps_env->char_status |= (1 << param->char_index);
- QPRINTF("app_qpps_env->char_status %X\r\n",app_qpps_env->char_status);
- // Send next group data until current data have been sent
- if (get_bit_num(app_qpps_env->char_status) == (app_qpps_env->tx_char_num - 1))
- {
- QPRINTF("t:app_qpps_env->char_status %X\r\n",app_qpps_env->char_status);
- app_test_send_data(app_qpps_env->tx_char_num - 1);
- }
- }
- else
- {
- QPRINTF("QPPS send error %d.\r\n", param->status);
- }
- return (KE_MSG_CONSUMED);
- }
复制代码
探讨:如果仔细观察,可以发现,从9601到9606第一次发送的数据分别是以01、02、03、...、06开头的,而07却再次回到了9601,接下来08、09、...、0c,0c发送在9607的特征上,9606并没有观察到发送的数据。也就是第一次循环的时候9607没有参与到发送循环中,而第二次发送时,9606没有参与到循环中,如此重复。这是为什么呢?
作者: 安安 时间: 2015-4-17 16:18
沙发!
作者: wyq165 时间: 2015-5-19 21:36
我是定了5个,QPPS和QPPC互连 观察发现其实都是轮流从1到5发送,而在QPPC中代码是收到200个字才打印一次。
作者: 安安 时间: 2015-5-20 11:38
作为QPPC来说,接收到的确实是1~5,在用手机连接时,打开所有notify,才会出现我帖子所说的现象,那是从机单方面测试用的。
作者: wyq165 时间: 2015-5-20 17:14
请问楼主手机上装的是什么软件?我用手机找不到QPPC的广播。
作者: 安安 时间: 2015-5-21 14:40
我之前没有说清楚,应该是手机连接QPPS,QPPC作为主机不会发出广播啊,只会扫描广播。QPPS在用手机连接,并且打开所有notify的时候会有测试发送命令
作者: carlinluo 时间: 2015-7-16 09:32
怎么感觉和我的代码都不一样啊03.#define QPPS_NOTIFY_NUM 7
都没有
作者: 安安 时间: 2015-7-16 14:19
你的代码应该是直接赋值为7的吧,因为之前的版本这样做很难找到这个地方去更改revevie的特征,所以后面版本做成宏了。
作者: huzi741 时间: 2015-7-17 18:59
楼主我想请教一个问题,ATT的CLIENT要读取SERIVER的数据用什么命令呀,写数据使用GATTC_WRITE_CMD,读数据的呢使用GATTC_READ_CMD吗还是GATTC_DISC_CMD,CLIENT接受要读取数据的命令以后的操作是啥呀,谢谢大神。
作者: 安安 时间: 2015-7-20 10:32
一切命令ATT命令都在app_task.c和app_gatt.c/app_gatt_task.c三个文件中。
作者: 小米粥 时间: 2015-7-21 09:50
请问楼主我用的是昆天科的开发板,烧录qpps程序怎么不能实现透传呢
作者: 安安 时间: 2015-7-21 11:42
如果是FireBLE源码的话,那么按键定义是不一样的,烧录SDK源码应该是可以跑的。来一块FireBLE呗,那就不用修改button_pin了:lol
作者: q123774812q 时间: 2015-8-4 10:30
nt app_gap_set_mode_req_cmp_evt_handler(ke_msg_id_t const msgid, struct gap_event_common_cmd_complete const *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
app_task_msg_hdl(msgid, param);
switch (ke_state_get(dest_id))
{
case APP_INIT:
//#if QN_DEMO_AUTO luhong 2015.8.3
// Created DB should has been finished by each profile service,
// Start Adv mode automatically here
app_gap_adv_start_req(GAP_GEN_DISCOVERABLE|GAP_UND_CONNECTABLE,
app_env.adv_data, app_set_adv_data(GAP_GEN_DISCOVERABLE),
app_env.scanrsp_data, app_set_scan_rsp_data(app_get_local_service_flag()),
GAP_ADV_FAST_INTV1, GAP_ADV_FAST_INTV2);
//#endif
不需要按键照样可以进入广播状态
作者: 卜道翁先生 时间: 2015-9-17 19:37
没有#define QPPS_NOTIFY_NUM 7 这个宏定义的话 要在哪里修改?
作者: hiccchen 时间: 2015-9-26 18:01
同问 之前我改过 后来我写Android写多了 把这个给忘了 现在又得回来找{:3_58:}
作者: 卜道翁先生 时间: 2015-10-6 14:59
请问QPPS写的特征值数能改么
作者: k693277161 时间: 2015-10-7 22:33
昆天科深入讲解的资料很少,这算很详细了
作者: 安安 时间: 2015-10-8 09:20
本帖最后由 安安 于 2015-10-8 09:31 编辑
在函数- void app_create_server_service_DB(void)
复制代码 中,有如下代码:
旧版本:
- #if BLE_QPP_SERVER
- app_qpps_env->tx_char_num = 7;
- app_qpps_create_db(app_qpps_env->tx_char_num);
- #endif
复制代码
新版本:- #if BLE_QPP_SERVER
- qpps_set_service_uuid((uint8_t *)QPP_SVC_PRIVATE_UUID);
- app_qpps_env->tx_char_num = QPPS_NOTIFY_NUM;
- app_qpps_create_db(app_qpps_env->tx_char_num);
- #endif
复制代码
旧版本的例程中是直接给app_qpps_env->tx_char_num赋值的,新版本的是通过定义QPPS_NOTIFY_NUM来给app_qpps_env->tx_char_num赋值的。
作者: 安安 时间: 2015-10-8 09:22
可以修改的,具体请看另一篇帖子:http://developer.t-firefly.com/thread-2163-1-2.html
欢迎光临 Firefly开源社区 (https://dev.t-firefly.com/) |
Powered by Discuz! X3.1 |