勤基科技 王泽行
本文主要讲解如何对特定BLE从机来开发主机BLE的代码调试,以实现主从机的数据通信,主从机芯片是Dialog的DA14585,并且基于官方串口透传(DSPS)的demo:
DA14585_DSPS\projects\target_apps\dsps\dsps_host\Keil_5。
该demo在官网https://support.dialog-semiconductor.com下DA14585的Reference designs板块能够下载。
一、在从机工程中,通信只有一个服务,但是这一个服务包括两个特征,一个用于接收数据,一个用于发送数据如下:
#define DEF_SVC1_UUID_128 {0xfb,0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xf0, 0xff, 0x00, 0x00}
#define DEF_SVC1_ADC_VAL_1_UUID_128 {0xfb,0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xf2, 0xff, 0x00, 0x00}//R
#define DEF_SVC1_ADC_VAL_2_UUID_128 {0xfb,0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xf1, 0xff, 0x00, 0x00}//T
const struct prf_char_def spsc_sps_char_16[SVC1_CHAR_MAX] =
{
[SVC1_ADC_VAL_1_CHAR] = {DEF_SVC1_ADC_VAL_1_UUID_16, ATT_MANDATORY, ATT_CHAR_PROP_NTF|ATT_CHAR_PROP_RD},
[SVC1_ADC_VAL_2_CHAR] = {DEF_SVC1_ADC_VAL_2_UUID_16, ATT_MANDATORY, ATT_CHAR_PROP_WR|ATT_CHAR_PROP_RD},
};
SVC1_ADC_VAL_1_UUID_128用于从机发送数据给主机,且以notify方式发送
SVC1_ADC_VAL_2_UUID_128用于主机发送数据给从机,且以GATTC_WRITE方式发送
二、在通过上面第一步添加好特征UUID及句柄后,我们需要在spsc_sps_char_desc中定义针对特征的描述符,如下:
/// State machine used to retrieve SPS service characteristic description informationconst struct prf_char_desc_def spsc_sps_char_desc[SVC1_DESC_MAX] =
{
///SPS Client Config
[SVC1_ADC_VAL_1_CLI_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, ATT_MANDATORY, SVC1_ADC_VAL_1_CHAR},
[SVC1_ADC_VAL_1_USER_CFG] = {ATT_DESC_CHAR_USER_DESCRIPTION, ATT_MANDATORY, SVC1_ADC_VAL_1_CHAR},
[SVC1_ADC_VAL_2_USER_CFG] = {ATT_DESC_CHAR_USER_DESCRIPTION, ATT_MANDATORY, SVC1_ADC_VAL_2_CHAR},
};
/// SPS Service Characteristics
enum
{
#ifndef CFG_PRF_CUS1C
SPSC_SRV_TX_DATA_CHAR = 0,
SPSC_SRV_RX_DATA_CHAR,
SPSC_CHAR_MAX
#else
SVC1_ADC_VAL_1_CHAR = 0,
SVC1_ADC_VAL_2_CHAR,
SVC1_CHAR_MAX,#endif
};
/// SPS Service Characteristic Descriptors
enum
{
#ifndef CFG_PRF_CUS1C
/// SPS client config
SPSC_SRV_TX_DATA_CLI_CFG,
SPSC_FLOW_CTRL_CLI_CFG,
SPSC_DESC_MAX,
#else
SVC1_ADC_VAL_1_CLI_CFG,
SVC1_ADC_VAL_1_USER_CFG,
SVC1_ADC_VAL_2_USER_CFG,
SVC1_DESC_MAX,
#endif
};
三、添加好特征属性后就是该如何使用这两个特征以便在主机进行扫描的时候与特定服务的从机进行服务添加交互。
1、在sps_client_enable_req_handler中,将首要服务添加进sps_data_service_uuid
static int sps_client_enable_req_handler(ke_msg_id_t const msgid,struct sps_client_enable_req const *param, ke_task_id_t const dest_id,ke_task_id_t const src_id)
{
#ifdef CONFIG_RTT rtt_printf("sps_client_enable_req_handler()\r\n");
#else arch_printf("sps_client_enable_req_handler()\r\n");
#endif
// SPS Client Role Task Environment
struct spsc_env_tag *spsc_env = PRF_ENV_GET(SPS_CLIENT, spsc);
if (param->con_type == PRF_CON_DISCOVERY)
{
// Start discovering SPS on peer
uint8_t sps_data_service_uuid[]=DEF_SVC1_UUID_128;
spsc_env->last_uuid_req = ATT_DECL_PRIMARY_SERVICE;
memcpy(spsc_env->last_svc_req, sps_data_service_uuid, ATT_UUID_128_LEN);
prf_disc_svc_send_128(&spsc_env->prf_env, spsc_env->last_svc_req);
// uint16_t sps_data_service_uuid = DEF_SVC1_UUID_128;
// spsc_env->last_uuid_req = ATT_DECL_PRIMARY_SERVICE;
// spsc_env->last_svc_req_16 = sps_data_service_uuid;
// prf_disc_svc_send(&spsc_env->prf_env, param->conidx,spsc_env->last_svc_req_16);
// Set state to discovering
ke_state_set(dest_id, SPS_CLIENT_DISCOVERING );
}
return (KE_MSG_CONSUMED);
}
2、将刚才添加的数据库spsc_sps_char_16添加进来:,如下:
static int gattc_disc_char_ind_handler(ke_msg_id_t const msgid,struct gattc_disc_char_ind const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
#ifdef CONFIG_RTT rtt_printf("gattc_disc_char_ind_handler()\r\n");
#else arch_printf("gattc_disc_char_ind_handler()\r\n");
#endif
// Get the address of the environment
struct spsc_env_tag *spsc_env = PRF_ENV_GET(SPS_CLIENT, spsc);
// prf_search_chars_128(spsc_env->sps.svc.ehdl, SVC1_CHAR_MAX, &spsc_env->sps.chars[0], &spsc_sps_char[0], param, &spsc_env->last_char_code);
prf_search_chars(spsc_env->sps.svc.ehdl, SVC1_CHAR_MAX, &spsc_env->sps.chars[0], &spsc_sps_char_16[0], param, &spsc_env->last_char_code);
return (KE_MSG_CONSUMED);
}
3、在如下函数gattc_disc_char_desc_ind_handler将句柄总长度和数据库添加进来,如下:
static int gattc_disc_char_desc_ind_handler(ke_msg_id_t const msgid,struct gattc_disc_char_desc_ind const *param, ke_task_id_t const dest_id,ke_task_id_t const src_id)
{
#ifdef CONFIG_RTT rtt_printf("gattc_disc_char_desc_ind_handler()\r\n");
#else arch_printf("gattc_disc_char_desc_ind_handler()\r\n");
#endif
// Get the address of the environment
struct spsc_env_tag *spsc_env = PRF_ENV_GET(SPS_CLIENT, spsc);
// Retrieve SPS descriptors
prf_search_descs(SVC1_DESC_MAX, &spsc_env->sps.descs[0], &spsc_sps_char_desc[0], param, spsc_env->last_char_code);
return (KE_MSG_CONSUMED);
}
4、sps_client_enable_cfm_send返回服务添加确认消息,如下:
void sps_client_enable_cfm_send(struct spsc_env_tag *spsc_env, uint8_t conidx, uint8_t status)
{
#ifdef CONFIG_RTT rtt_printf( "sps_client_enable_cfm_send()\r\n");
#else arch_printf( "sps_client_enable_cfm_send()\r\n");
#endif struct sps_client_enable_cfm * cfm = KE_MSG_ALLOC(SPS_CLIENT_ENABLE_CFM, TASK_APP, prf_src_task_get(&(spsc_env->prf_env), conidx), sps_client_enable_cfm);
cfm->status = status;
if (status == ATT_ERR_NO_ERROR)
{
// uint8_t flow;
cfm->sps = spsc_env->sps;
// Register SPS_CLIENT task in gatt for indication/notifications
prf_register_atthdl2gatt(&(spsc_env->prf_env), conidx, NULL);
//Set value 0x0001 to CFG
prf_gatt_write_ntf_ind(&spsc_env->prf_env, conidx, spsc_env->sps.descs[SVC1_ADC_VAL_1_CLI_CFG].desc_hdl, PRF_CLI_START_NTF);
// prf_gatt_write_ntf_ind(&spsc_env->prf_env, conidx, spsc_env->sps.descs[SVC1_ADC_VAL_2_CLI_CFG].desc_hdl, PRF_CLI_START_NTF);
//Place in connected state after discover state
ke_state_set(spsc_env->prf_env.prf_task, SPS_CLIENT_CONNECTED);
#ifdef CONFIG_RTT rtt_printf( "CLIENT ENABLED\r\n");
#else arch_printf( "CLIENT ENABLED\r\n");
#endif
}
else
{
#ifdef CONFIG_RTT rtt_printf( "CLIENT ENABLE ERROR\r\n");
#else arch_printf( "CLIENT ENABLE ERROR\r\n");
#endif
}
ke_msg_send(cfm);
}
返回状态status=ATT_ERR_NO_ERROR表示服务添加成功。
四、与从机的数据收发函数接口
int user_sps_client_data_rx_ind_handler(ke_msg_id_t const msgid,struct sps_client_data_rx_ind const *param,ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
uint8_t i;
if (param->length == 0)
return (KE_MSG_CONSUMED);
#ifdef CONFIG_RTT rtt_printf("Ble received: ");
for (i = 0; i < param->length; i++)
{
rtt_printf("%02x ", param->data[i]);
}
rtt_printf("\r\n");
#else arch_printf("Ble received: ");
#endif
#ifdef DATA_RTT rtt_printf("Ble received: ");
for (i = 0; i < param->length; i++)
{
rtt_printf("%02x ", param->data[i]);
}
rtt_printf("\r\n");
#endif
user_ble_push((uint8_t *)param->data,param->length);
return (KE_MSG_CONSUMED);
}
user_sps_client_data_rx_ind_handler用于接收来自从机的消息,包括数据和数据长度。
void user_send_ble_data(const uint8_t *data, uint16_t length)
{
arch_printf("user_send_ble_data()\r\n");
struct sps_client_data_tx_req * req = KE_MSG_ALLOC_DYN_NO_INIT(SPS_CLIENT_DATA_TX_REQ,prf_get_task_from_id,SK_ID_SPS_CLIENT), TASK_APP, sps_client_data_tx_req, length);
req->length = length;
memcpy(&req->data[0], data, length);
ke_msg_send(req);
}
user_send_ble_data用于发送数据。至此,服务添加完毕,这个时候我们就可以通过调用这两个接口函数与从机进行数据通信了。
Dialog技术讨论,QQ群871600668