今天写的东西就深入一点了,是一个昆天科的定制协议的理解,即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没有参与到循环中,如此重复。这是为什么呢?
|