从新建私人服务的过程中我们知道,我们并不关心服务中特性建立的具体细节,而是把官方已经写好的服务添加到我们的工程中,并根据自己的需求对其中的参数进行了修改。而如果要在一个服务中获取多个特性的值,或对多个特性进行写入数据操作,就需要新建多个特性。这里使用的还是ble_lbs.c这个文件,将在上一节的基础上分析这个文件。
在ble_lbs_init()函数中,添加一个特性这样表示:
err_code = button_char_add(p_lbs, p_lbs_init);
VERIFY_SUCCESS(err_code);
我们通过编写xxx_char_add()这个函数来添加一个特性,这里是添加一个按键的通知特性,所以函数命名为button_char_add()。
在xxx_char_add()函数中,主要完成了两个工作:
(1)特性参数配置:
ble_gatts_char_md_t char_md; //特性参数结构体定义,通过设置其中的参数来设置特性参数
ble_gatts_attr_md_t cccd_md; //attr_md结构体定义,用来配置cccd参数
//配置特性参数
//配置ccmd的安全级别和储存位置
memset(&cccd_md, 0, sizeof(cccd_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); //可读
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm); //可写
cccd_md.vloc = BLE_GATTS_VLOC_STACK; //储存在堆栈中
memset(&char_md, 0, sizeof(char_md));
char_md.char_props.read = 1; //该特性可读
char_md.char_props.notify = 1; //该特性可通知
char_md.p_char_user_desc = NULL; //不使用描述符
char_md.p_char_pf = NULL; //不使用CPF
char_md.p_user_desc_md = NULL; //无属性元数据
char_md.p_cccd_md = &cccd_md; //使用cccd描述符
char_md.p_sccd_md = NULL; //不使用服务端描述符
(2)GATT属性配置
ble_gatts_attr_t attr_char_value;
ble_uuid_t ble_uuid;
ble_gatts_attr_md_t attr_md; //GATT属性结构体定义
//配置GATT属性
memset(&attr_md, 0, sizeof(attr_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); //可读
BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);//不可写
attr_md.vloc = BLE_GATTS_VLOC_STACK; //储存在堆栈中
attr_md.rd_auth = 0; //读写无需应用程序授权
attr_md.wr_auth = 0; //
attr_md.vlen = 0; //读写长度不可变
memset(&attr_char_value, 0, sizeof(attr_char_value));
ble_uuid.type = p_lbs->uuid_type; //uuid类型
ble_uuid.uuid = LBS_UUID_BUTTON_CHAR; //uuid
attr_char_value.p_uuid = &ble_uuid; //配置uuid
attr_char_value.p_attr_md = &attr_md; //配置属性参数
attr_char_value.init_len = sizeof(uint8_t); //属性数据的初始化长度
attr_char_value.init_offs = 0; //偏移量
attr_char_value.max_len = sizeof(uint8_t); //最大长度
attr_char_value.p_value = NULL; //属性数据指针
(3)在特性参数和GATT参数设定后,要设置操作句柄。在xxx_char_add()的输入参数中,第一个输入参数是该服务的结构体指针,可以跳转到该结构体的定义中查看:
struct ble_lbs_s
{
uint16_t service_handle;
ble_gatts_char_handles_t led_char_handles;
ble_gatts_char_handles_t button_char_handles; //需添加的操作句柄
uint8_t uuid_type;
ble_lbs_led_write_handler_t led_write_handler;
};
(4)句柄设置完成后,需要把上面设置好的特性和GATT添加到协议栈中GATT层中作为数据接口,使用sd_ble_gatts_characteristic_add()函数,及xxx_char_add()的最后一部分。
return sd_ble_gatts_characteristic_add(p_lbs->service_handle,
&char_md,
&attr_char_value,
&p_lbs->button_char_handles);
第1个参数是特性要加入的服务的句柄,指向你定义的这个点灯的服务。
第2个参数是特性的结构体&char_ md,它是-一个全局变量,它包含了前面特性设置的参数(读,写,通知等)。
第3个参数是值属性的描述结构体&attr _char_ value, 它包含了它的UUID,长度和初始值。
第4个参数是返回的特性和描述符的唯一句柄,这个句柄可以在以后用于区别服务里不同的特性。比如本例里返回变量button_ char_ handles (按键特性的句柄)作为唯一的按键通知的句柄。如果这个服务里有其他特性,则每种特性都有自己唯一声明的操作句柄。
(5)最后在服务初始化函数中调用,这个在最前面已经说了。
完成了新特性的建立后,我们还需要完善回调函数。