嵌入式 蓝牙 BLE服务service添加流程 --- 通用性流程-爱代码爱编程
Android 蓝牙低功耗ble 广播、扫描、连接、数据读写源码流程分析大全,非常详细的从btif-bta-btm-hci 数据 - 点击下载
一、概述
大家在做嵌入式开发的时候,不可避免的都要添加service,那么BLE的服务如何添加呢?不同的SDK是否添加方式不一样呢?就目前的SDK来说,例如 DIALOG、cypress、恒玄、瑞昱等等,BLE添加流程都是大同小异,只需要了解通用的流程,然后再根据各自的DEMO进性修改就行。
下面以DIALOG平台为例,添加一个BLEservice:
二、BLE私有服务架构介绍
2.1 服务框架概述
蓝牙BLE是基于GATT进行通信的,GATT(Generic Attribute Profile)是一种属性传输协议,简单的讲可以认为是一种属性传输的应用层协议。GATT是蓝牙4.0特有的Profile通用规范,BLE应用的Profile均基于GATT。GATT定义了一个服务框架规范,该框架包括对服务(Service)和服务特性(Characteristic)的定义和规范,和其中读、写、通知的特性等。可以将GATT理解成BLE框架,我们在GATT上面实现BLE功能。
BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。每一个BLE设备中可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor,一个Descriptor包含一个Value。在蓝牙实际数据交换中,就是通过读写这些“Characteristic”来实现的,BLE数据最终都是通过ATT PDU来传输的。
2.2 service概述
service可以理解为一个服务,是一个独立的服务单元,在ble从机中,一般有多个服务,例如电量信息服务、系统信息服务、心率、HID、体温都是标准蓝牙服务。每个service中又包含多个characteristic特征值。每个具体的characteristic特征值才是ble通信的主题。比如当前的电量是80%,所以会通过电量的characteristic特征值存在从机的profile里,这样主机就可以通过这个characteristic来读取80%这个数据。
2.3 characteristic 概述
characteristic特征值,ble主从机的通信均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。一般来说,Characteristic是手机与BLE终端交换数据的关键, 在BLE中,数据是通过characterristic进行包装的,Server是通过characteristic来表示数据的,虽然一条数据最有价值的部分是它的值(value),但是仅有value是不够的,比如,27 表示27温度还是27%湿度,同时每个value还应该有读写属性及权限属性,一个characteristic包含三种条目:characteristic 声明,characteristic的值,以及characteristic的描述符descriptor(可以多个描述符)。
2.4 Value 概述
指是Characteristic的属性值。
2.5 descriptor 概述
其对Characteristic的Value不同角度的描述和说明,特性描述是用来包含一些关于特性值的关联信息,特性描述有多种类型,一个特性的定义可以有任意多个的描述符,而所有描述符都是用来为特性值服务的,具体用到了哪些描述符需要根据相应的Profile进行确定。描述符跟随在特性值声明之后,如果有多种描述符进行声明,其次序是没有规定的。
2.6 UUID 概述
UUID,统一识别码,我们刚才提到的service和characteristic和descriptor,都需要一个唯一的uuid来标识。UUID既有16位的也有128位的。16位的UUID是经过蓝牙组织认证的,是需要购买的,而128位的UUID则可以自定义,当然也有许多通用的UUID。每个Service、Characteristic或者Descriptor都有一个 128 bit 的UUID来标识。但那些被蓝牙技术联盟的标准中定义的UUID是以16 bit 来表示的。实际上,16 bit 的UUID,是有附加 Bluetooth Base UUID,即变成0000****-0000-1000-8000-00805f9b34fb(16位UUID被输入在****的位置)。
三、BLE 服务实现
3.1 整体代码架构
3.2 服务连接函数
原型 static void custom_handle_connect_request(ble_service_t *svc, const
ble_evt_gap_connected_t *evt);
描述 进行gatt服务连接
输入 svc:ble service struct , evt:BLE_EVT_GAP_CONNECTED struct
返回 void
static void custom_handle_connect_request(ble_service_t *svc, const ble_evt_gap_connected_t *evt)
{
if ((!svc) || (!evt)) {
BLE_LOG("custom_handle_connect_request(),param error \r\n");
return ;
}
connIdx = evt->conn_idx; //获取到连接索引
BLE_LOG("custom_handle_connect_request(), conn_idx = %04x\r\r",connIdx);
if (BLE_STATUS_OK != ble_gattc_exchange_mtu(evt->conn_idx)) {
BLE_LOG("ble_gattc_exchange_mtu() error \r\r");
return;
}
if (BLE_STATUS_OK != ble_gap_phy_set(evt->conn_idx, BLE_GAP_PHY_PREF_2M, BLE_GAP_PHY_PREF_2M)) {
BLE_LOG("ble_gap_phy_set() error \r\n");
return;
}
return;
}
3.3 服务断连函数
原型 static void custom_handle_disconnect_request(ble_service_t *svc, const
ble_evt_gap_disconnected_t *evt)
描述 进行gatt服务断连
输入 svc:ble service struct , evt:BLE_EVT_GAP_DISCONNECTED struct
返回 void
static void custom_handle_disconnect_request(ble_service_t *svc, const ble_evt_gap_disconnected_t *evt) {
if ((!svc) || (!evt)) {
BLE_LOG("custom_handle_disconnect_request(), param error \r\n");
return;
}
connIdx = 0xFFFF;//每次断开需要将连接索引清空。
}
3.4 服务写入函数
原型 static void custom_handle_write_request(ble_service_t *svc, const ble_evt_gatts_write_req_t *evt)
描述 Client端给serve发送数据
输入 svc:ble service struct , evt:BLE_EVT_GAP_WRITE struct
返回 void
static void custom_handle_write_request(ble_service_t *svc, const ble_evt_gatts_write_req_t *evt)
{
if((!svc) || (!evt)) {
BLE_LOG("custom_handle_write_request(), param error \r\n");
return;
}
//数据写入确认。
if (BLE_STATUS_OK != ble_gatts_write_cfm(evt->conn_idx, evt->handle, ATT_ERROR_OK)) {
BLE_LOG(" ble_gatts_write_cfm error \r\n");
return;
}
if (NULL == g_alp_T) {
BLE_LOG("g_alp_T is NULL error \r\n");
return;
}
/* provide data to upon */
ble_app_write_data(BLE_SERVICE_CUSTOME, evt->value, evt->length);//将client write过来的数据上报到app层
if (BLE_STATUS_OK != custom_data_send(buf,sizeof(buf))) {
BLE_LOG("data send to app failed \r\n");
return ;
} else {
BLE_LOG("data already send to app \r\n");
return;
}
return;
}
上面notify 已经使能,下面为主动notify数据给client.
3.4 服务读出函数
原型 static void custom_handle_read_request(ble_service_t *svc, const ble_evt_gatts_read_req_t *evt)
描述 读出数据
输入 svc:ble service struct , evt:BLE_EVT_GAP_READ struct
返回 void
static void custom_handle_read_request(ble_service_t *svc, const ble_evt_gatts_read_req_t *evt)
{
if ((!svc) || (!evt)) {
BLE_LOG("custom_handle_read_request(), param error \r\n");
return;
}
uint16_t ccc = NOTIFY_ENABLE;
if (BLE_STATUS_OK != ble_gatts_read_cfm(evt->conn_idx, evt->handle,ATT_ERROR_OK, sizeof(ccc), &ccc)) {
BLE_LOG(" ble_gatts_read_cfm error \r\n");
return;
}
return;
}
read 一般主动来读电量等,然后service主动将value赋值给回复给client。
读的handle是77,读到的数据是(04)
3.5 服务发送函数
原型 ble_error_t custom_data_notify_send(const uint8_t *data, uint16_t len)
描述 发送数据
输入 Data:要发送的数据, len:数据长度
返回 Ble_error_t :错误码
ble_error_t custom_data_send(const uint8_t *data, uint16_t len)
{
if ((!data) || (len > MAX_DATA_LEN) || (len == 0)) {
BLE_LOG("custom_data_send(), param error \r\n");
return BLE_ERROR_FAILED;
}
ble_error_t ret = BLE_STATUS_OK;
if (g_alp_T == NULL) {
BLE_LOG("custom_data_send(), param error \r\n");
return BLE_ERROR_FAILED;
}
if (connIdx == -1) {
BLE_LOG("g_connIdx_T is not assigned error \r\n");
return BLE_ERROR_FAILED;
}
/* when send data ,start conn params switch */
ble_conn_auto_switch();
//调用sdk数据发送函数
ret = ble_gatts_send_event(connIdx,g_alp_T->custom_tx_h, GATT_EVENT_NOTIFICATION, len, data);
if (ret != BLE_STATUS_OK) {
BLE_LOG("ret ruturn error, ret = 0x%02x\r\n",ret);
return BLE_ERROR_FAILED;
}
return BLE_STATUS_OK;
}
3.6 服务初始化函数
原型 ble_service_t *custom_service_init(void)
描述 服务初始化
输入 void
返回 ble service struct
3.6.1 填充ble_service_t结构体
下面的结构体是service、characteristic、descripte handle句柄的。
/* custom profile */
typedef struct {
ble_service_t svc; /* custom service handle */
// handles
uint16_t custom_rx_h; /* custom rx characteristic handle */
uint16_t custom_tx_h; /* custom tx characteristic handle */
uint16_t custom_notify_ccc_h; /* custom descripte handle */
} custom_service_t;
下面结构体是service的内容,包括开始结束句柄和注册回调函数。
/*BLE Service struct */
typedef struct ble_service {
uint16_t start_h; /**< Service start handle */
uint16_t end_h; /**< Service end handle */
connected_evt_t connected_evt; /**< Connected event callback */
disconnected_evt_t disconnected_evt; /**< Disconnected event callback */
read_req_t read_req; /**< Read request callback */
write_req_t write_req; /**< Write request callback */
prepare_write_req_t prepare_write_req; /**< Prepare write request callback */
event_sent_t event_sent; /**< Event sent callback */
cleanup_t cleanup; /**< Cleanup callback */
} ble_service_t;
进行service的连接、断开、读、写回调函数的注册。
custom_service_t *g_alp_T = NULL;
g_alp_T->svc.connected_evt = custom_handle_connect_request;
g_alp_T->svc.read_req = custom_handle_read_request;
g_alp_T->svc.write_req = custom_handle_write_request;
g_alp_T->svc.disconnected_evt = custom_handle_disconnect_request;
3.6.2 计算属性att数量
原型 uint8_t ble_service_get_num_attr(const ble_service_config_t *config, uint16_t chars,uint16_t descs)
描述 函数计算注册服务所需的属性数
输入 param [in] config Service configuration structure
param [in] chars Number of characters
param [in] descs Number of descriptors
返回 return Number of attributes
这个函数计算注册服务所需的属性数,其中特征值和描述符的数量要根据自己的需求随时进行更改。
#define CHARA_NUM (2)
#define DESC_NUM (1)
/* 这个函数计算注册服务所需的属性数 */
numAttr = ble_service_get_num_attr(NULL, CHARA_NUM, DESC_NUM);
3.6.3 UUID字符形式转存到字符数组中
原型 bool ble_uuid_from_string(const char *str, att_uuid_t *uuid)
描述 UUID字符形式转存到字符数组中
输入 *Str:字符形式UUID
*uuid:最终存放uuid的数组
返回 Bool类型
#define CUSTOME_SERVICE_UUID "00002760-08C2-11E1-9073-0E8AC72E1001"
/* 将UUID字符串形式转换成 数组形式*/
ble_uuid_from_string(CUSTOME_SERVICE_UUID, &uuid);
/* 或者直接将UUID设置成uint8_t 数组形式 */
uint8_t g_wepayService[] = {0x66, 0x63, 0x97, 0xd6, 0xd8, 0x11, 0x6e, 0x87,\
0xa2, 0x4e, 0x58, 0xbe, 0x42, 0x34, 0x35, 0xcc};
/* 用来校验,是16 or 128 bit,存放在uuid数组中 */
ble_uuid_from_buf(g_wepayService, &uuid);
3.6.4 添加服务
原型 ble_error_t ble_gatts_add_service(const att_uuid_t *uuid, const gatt_service_t type,uint16_t num_attrs)
描述 添加服务
输入 *uuid:该服务的UUID
Type:主服务 or 次要服务
Num_attrs:属性数量
返回 ble_error_t
/** GATT service type */
typedef enum {
GATT_SERVICE_PRIMARY,
GATT_SERVICE_SECONDARY,
} gatt_service_t;
/* 将属性添加到服务中去 */
ble_gatts_add_service(&uuid, GATT_SERVICE_PRIMARY, numAttr);
《首要服务》«Primary Service»
公开设备功能的服务通常为首要服务。例如有一台支持心率计的蓝牙设备,那么心率计将被实例化为首要服务。
《次要服务》«Secondary Service»
不需要被客户端直接用或者不需要客户端理解的行为和特性的封装。次要服务是不需要被其他设备知道的,次要服务相当于公司的研发人员,而首要服务相当于销售人员,客户问销售要什么产品的报价什么,销售可以直接给客户,但是如果客户需要产品的具体性能指标参数,那么销售就只能找研发要了之后再给客户。也就是说次要服务只能被首要服务引用,也就是只能被首要服务用《Include》进行包含来引用。
3.6.5 添加特征值-write
原型 ble_error_t ble_gatts_add_characteristic(const att_uuid_t *uuid, gatt_prop_t prop, att_perm_t perm,uint16_t max_len, gatts_flag_t flags, uint16_t *h_offset,uint16_t *h_val_offset)
描述 添加特征值属性
输入 *uuid:该特征值的UUID
gatt_prop_t prop:读、写、通知等属性
att_perm_t perm: ATT attribute permission
gatts_flag_t flags: GATT Server flags
uint16_t max_len:数据长度
uint16_t *h_offset :NULL
uint16_t *h_val_offset: custom rx characteristic handle
返回 ble_error_t
ble_uuid_from_string(CUSTOME_SERVICE_WRITE_UUID, &uuid);
ble_gatts_add_characteristic(&uuid, GATT_PROP_WRITE | GATT_PROP_WRITE_NO_RESP | GATT_PROP_NOTIFY, ATT_PERM_WRITE_ENCRYPT | ATT_PERM_WRITE, MAX_DATA_LEN, GATTS_FLAG_CHAR_READ_REQ,NULL, &g_alp_T->custom_rx_h);
3.6.6 添加特征值-notify
原型 ble_error_t ble_gatts_add_characteristic(const att_uuid_t *uuid, gatt_prop_t prop, att_perm_t perm,uint16_t max_len, gatts_flag_t flags, uint16_t *h_offset,uint16_t *h_val_offset)
描述 添加特征值属性
输入 *uuid:该特征值的UUID
gatt_prop_t prop:读、写、通知等属性
att_perm_t perm: ATT attribute permission
gatts_flag_t flags: GATT Server flags
uint16_t max_len:数据长度
uint16_t *h_offset :NULL
uint16_t *h_val_offset: custom tx characteristic handle
返回 ble_error_t
ble_uuid_from_string(CUSTOME_SERVICE_NOTIFY_UUID, &uuid);
ble_gatts_add_characteristic(&uuid, GATT_PROP_NOTIFY, ATT_PERM_WRITE_ENCRYPT | ATT_PERM_WRITE, MAX_DATA_LEN,GATTS_FLAG_CHAR_READ_REQ, NULL, &g_alp_T->custom_tx_h);
3.6.7 添加描述符descriptor
原型 ble_error_t ble_gatts_add_descriptor(const att_uuid_t *uuid, att_perm_t perm, uint16_t max_len, gatts_flag_t flags, uint16_t *h_offset)
描述 添加特征值属性
输入 *uuid:该特征值的UUID
att_perm_t perm: ATT attribute permission
gatts_flag_t flags: 0
uint16_t max_len:数据长度
uint16_t * h_offset: custom descripte handle
返回 ble_error_t
ble_uuid_create16(UUID_GATT_CLIENT_CHAR_CONFIGURATION, &uuid);
ble_gatts_add_descriptor(&uuid,ATT_PERM_RW,DESC_LEN,0,&g_alp_T->custom_notify_ccc
3.6.8 注册服务
原型 ble_error_t ble_gatts_register_service(uint16_t *handle, ...)
描述 注册服务
输入 g_alp_T->svc.start_h:Service start handle
g_alp_T->custom_rx_h:rx characteristic handle
g_alp_T->custom_tx_h:tx characteristic handle
g_alp_T->custom_notify_ccc_h:descripte handle
返回 ble_error_t
ble_gatts_register_service(&g_alp_T->svc.start_h, &g_alp_T->custom_rx_h, &g_alp_T->custom_tx_h, &g_alp_T->custom_notify_ccc_h, 0); /* param must end wi
3.6.9 添加服务
原型 void ble_service_add(ble_service_t *svc)
描述 添加服务
输入 g_alp_T->svc.start_h:Service start handle
g_alp_T->custom_rx_h:rx characteristic handle
g_alp_T->custom_tx_h:tx characteristic handle
g_alp_T->custom_notify_ccc_h:descripte handle
返回 void
/* 计算出结束句柄 */
g_alp_T->svc.end_h = g_alp_T->svc.start_h + numAttr;
ble_service_add(&g_alp_T->svc);