NRF52832蓝牙的Profile之 LED灯读写 自己理解的
关于自定义服务的一般流程
首先我们可以看到在main函数里面有一个ServiceInit()函数。
这个函数实现了我们自定义服务的初始化。
函数实现如下:
static void services_init(void)
{
uint32_t err_code;
ble_led_init_t led_init;
//led状态默认值
uint8_t led_value[LED_UUID_CHAR_LEN] = {1,1,1,1};
//清除赋值
memset(&led_init, 0, sizeof(led_init));
//设置特征值初始取值。
led_init.p_led_value = led_value;
//设置处理主机(GATTC)Write事件的处理函数,用于接收数据。
led_init.led_write_handler = led_write_handler;
//函数参数是指针的形式,因此携带的参数要使用&符号,取变量地址。
err_code = ble_led_init(&m_led, &led_init);
APP_ERROR_CHECK(err_code);
}
其中,ble_led_init_t 类型是我们自己定义的一个结构体,里面一般包含一些简单的信息,例如led的初始值,和用于写led的回调函数。
ble_led_init_t的声明如下:
typedef void (*ble_led_write_handler_t) (uint8_t * new_state);
typedef struct
{
uint8_t *p_led_value;
ble_led_write_handler_t led_write_handler; //我们自己定义的回调函数,一般包含自己需要接收的参数
} ble_led_init_t;
我们回到ServiceInit函数,接着往下看:
初始化了led_value[LED_UUID_CHAR_LEN]这个数组的值,其中LED_UUID_CHAR_LEN这个宏表示接收到特征值的长度。
接下来为我们自己定义的结构体赋值,接着就调用了ble_led_init()这个函数。
我们主要的工作都是在这个函数里面完成的。我们可以先看一下给这个函数的2个参数 &m_led,&led_init;
led_init是我们刚刚上面定义的结构体,里面基本上是一个回调函数的指针。这里不用管他。
最主要的是我们的第一个参数,m_led。
我们可以进行跟踪,看到m_led是在main函数外面用一个宏来声明的。
BLE_LED_DEF(m_led); // LED实例
我们来分解一下,分解后BLE_LED_DEF(m_led)这个实际上是
static ble_led_t m_led;
NRF_SDH_BLE_OBSERVER( m_led_obs,
BLE_LED_BLE_OBSERVER_PRIO,
ble_led_on_ble_evt,&m_led);
NRF_SDH_BLE_OBSERVER这个函数是像系统注册一个观察者,
其中底层我们不必深究,这里我们只需要知道,通过这个函数注册了一个BLE_OBSERVER观察者,
当系统发生BLE事件的时候。就会调用我们这里定义的ble_led_on_ble_evt回调方法,ble_led_t这个结构体中
我们定义了一些系统事件的句柄。原型如下:
// LED服务结构体
struct ble_led_s
{
uint8_t uuid_type; //UUID的类型
uint16_t service_handle; //服务的句柄
ble_gatts_char_handles_t led_char_handles; //特征的句柄
ble_led_write_handler_t led_write_handler; //当特征接收到写入值的时候,调用的回调函数。
};
我们接下来进入ble_led_init函数,这个函数是我们自己写的
这里的第一个参数p_led,就是我们上面讲的m_led结构体。第2个参数是我们定义另外一个结构体。
uint32_t ble_led_init(ble_led_t * p_led, const ble_led_init_t * p_led_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// 初始化服务结构体
// 给我们的m_led结构体中的回调函数赋值
p_led->led_write_handler = p_led_init->led_write_handler;
// 添加服务(128bit UUID),uuid_type由此判断是128位
ble_uuid128_t base_uuid = {LED_UUID_BASE};
//这个函数的功能是得到一个uuid的类型。写法基本上是固定的。第二个参数是得到uuid_type的类型
err_code = sd_ble_uuid_vs_add(&base_uuid, &p_led->uuid_type);
VERIFY_SUCCESS(err_code);
ble_uuid.type = p_led->uuid_type; //type value = 2(BLE_UUID_TYPE_VENDOR_BEGIN), is 128bit uuid; value = 1(BLE_UUID_TYPE_BLE), is 16bit uuid
ble_uuid.uuid = LED_UUID_SERVICE;
//注册服务,作为主服务,传入我们刚才生成的uuid结构体,第3个参数为返回的服务的句柄,我们保存到我们定义的m_led结构体中
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_led->service_handle);
VERIFY_SUCCESS(err_code);
// 添加LED特征值(属性是Write和Read、长度是4)
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = LED_UUID_CHAR; //特征的uui
add_char_params.uuid_type = p_led->uuid_type;
add_char_params.init_len = LED_UUID_CHAR_LEN; //数据的初始长度
add_char_params.max_len = LED_UUID_CHAR_LEN; //数据的最大
add_char_params.p_init_value = p_led_init->p_led_value; //数据的初始值
add_char_params.char_props.read = 1; //读使能
add_char_params.char_props.write = 1; //写使能
add_char_params.read_access = SEC_OPEN; //安全性为开放
add_char_params.write_access = SEC_OPEN; //安全性为开放
//调用特征添加方法,把我们的特征添加到服务里面去,所以这里的第一个参数为我们建立的服务的句柄,上面得到的
//第2个参数为我们为特征配置的一些基本信息,第3个参数为返回值,得到的是特征的句柄,保存到我们的结构体中。
return characteristic_add(p_led->service_handle, &add_char_params, &p_led->led_char_handles);
}
这样就完成了服务的添加。
因为我们已经向系统注册了一个观察者对象。所以在系统发生一些BLE的事件时,都会调用我们上面写的回调函数
ble_led_on_ble_evt()这个回调函数。
这个回调函数的写法如下:
//******************************************************************************
// fn :ble_led_on_ble_evt
//
// brief : BLE事件处理函数
//
// param : p_ble_evt -> ble事件
// p_context -> ble事件处理程序的参数(暂时理解应该是不同的功能,注册时所携带的结构体参数)
// return : none
void ble_led_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_led_t * p_led = (ble_led_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
// GATTClient的Write事件,本从机里对应为GATTS(Server端)
case BLE_GATTS_EVT_WRITE:
on_write(p_led, p_ble_evt);
break;
default:
break;
}
}
//******************************************************************************
// fn :on_write
//
// brief : 处理Write事件的函数。该事件来自主机(GATTC)的Write写特征值
//
// param : p_led -> led服务结构体
// p_ble_evt -> ble事件
//
// return : none
static void on_write(ble_led_t * p_led, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
//参数判断和筛选
if ( (p_evt_write->handle == p_led->led_char_handles.value_handle)
&& (p_evt_write->len <= LED_UUID_CHAR_LEN)
&& (p_led->led_write_handler != NULL))
{
//调用mainc中service_init函数设置的处理函数。并传递从无线端接收到的数据。
p_led->led_write_handler((uint8_t*)p_evt_write->data);
}
}
我们可以看到第二个参数为一个上下文的指针,这里我们可以理解为我们注册的时候,给的那个结构体对象。也就是上面所说的m_led这个结构体。
第一个参数为发生的事件,我们可以用switch语句对我们感兴趣的事件进行处理。
这样我们的整个流程就已经完成了!