手机APP使用到的是NORDIC官网下载的nRF Connect
BLE通信的载体都是通过数据文件或属性文件来实现。也就是profile。他们的层级关系是这样的
在BLE协议栈里面可以定义很多的profile。定义很多的server,每个server也可以定义很多的characteristic。
--------------------------------------------------分割线-----------------------------------------
例程解析
main函数
log_init(); // 初始化LOG打印,由RTT工作
timers_init(); // 初始化定时器
power_management_init();// 初始化电源控制
ble_stack_init(); // 初始化BLE栈堆
gap_params_init(); // 初始化GAP
gatt_init(); // 初始化GATT
services_init(); // 初始化Service
advertising_init(); // 初始化广播
// 初始化外设
LED_Init();
// 打印例程名称
NRF_LOG_INFO("demo0: simple peripheral");
advertising_start(); // 开启广播
1.在main函数中,也是正常的初始化GAP和GATT。
2.初始化完后就初始化service------这一步是重点
3.然后初始化硬件外设
4.初始化广播并开始广播。
第二步中的初始化service
这一步最终要实现的就是调用库函数的sd_ble_gatts_service_add()添加service。characteristic_add()为服务添加属性。
来具体看看。services_init和ble_led_init都是自定义函数。
//******************************************************************
// fn : services_init
//
// brief : 初始化复位(本例程展示NUS:Nordic Uart Service)
//
// param : none
//
// return : none
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 ,方便管理和代码。也可以不定义这个结构体。直接将handler函数传递给m_led变量。
- 之后才是调用两个库函数(sd_ble_gatts_service_add和characteristic_add),将配置传入库里面。
//******************************************************************************
// fn :ble_led_init
//
// brief : 初始化LED服务
//
// param : p_led -> led服务结构体
// p_led_init -> led服务初始化结构体
//
// return : uint32_t -> 成功返回SUCCESS,其他返回ERR NO.
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;
// 初始化服务结构体
p_led->led_write_handler = p_led_init->led_write_handler;
// 添加服务(128bit UUID),uuid_type由此判断是128位
ble_uuid128_t base_uuid = {LED_UUID_BASE};
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;
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;
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;
return characteristic_add(p_led->service_handle, &add_char_params, &p_led->led_char_handles);
}
- 函数sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid,
&p_led->service_handle);将UUID和事件处理函数绑定了。 - 函数characteristic_add(p_led->service_handle, &add_char_params,
&p_led->led_char_handles);将service和属性处理函数绑定了。
注意:通过跟踪发现led_char_handles其实是一个空指针。并没有具体的实函数。
属性的权限。上面例程使用了read和write。如果需要主动上报通知,则需要打开notify
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;
工作的数据流向
1.通过以上的配置后。若收到手机发过来的数据。
2.根据UUID调用p_led->service_handle这个指针指向m_led->ble_led_on_ble_evt。具体是这样的指针p_led实体是m_led。定义m_led的时候传入了参数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;
}
}
判断是否是WRITE事件。是的话就调用on_write自定义函数
//******************************************************************************
// 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);
}
}
on_write会判断(p_evt_write->handle == p_led->led_char_handles.value_handle)。这里判断是哪一个属性。然后调用led_write_handler这个函数。此函数是操作LED的函数。
到这里。整个链路就通了。