代码编织梦想

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);

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_45534242/article/details/124702693

[ble]低功耗蓝牙之gap、gatt-爱代码爱编程

一、开篇     本篇主要介绍一下关于BLE开发过程中必须了解的两个协议:GAP(通用访问协议)、GATT(通用属性协议)。两个协议都隶属于Host层,直接关系到应用层开发,与BLE开发人员的关系比较密切,其分别负责连接前数据广播和连接后的数据传输。 三、试验平台 Software Version:BLE_STACK_CC26XX_

cc254x--ble-爱代码爱编程

BLE协议栈 BLE体系结构,着重了解GAP和GATT。 PHY物理层在2.4GHz的ISM频段中跳频识别。LL连接层:控制设备的状态。设备可能有5中状态:就绪standby,广播advertising,搜索scanning,初始化initiating和连接connected。广播者传播数据,使得浏览者可以接收到。ini

蓝牙开发(一)----- 基于蓝牙ble的android应用开发-爱代码爱编程

文章目录 前言传统蓝牙 VS Ble蓝牙cosplay 角色扮演一些基本概念Ble开发的几个步骤准备第一步 配置清单文件第二步 检查设备,获取BluetoothAdapter第三步 注册广播,开启蓝牙第四步 扫描指定

蓝牙无线技术(BLE)介绍与开发点滴总结-爱代码爱编程

在项目实践学习中记录的点滴笔记,整理成章,希望能给大家提供工作与学习思路。 往期文章: 1、无线通信项目开发 - NB-IOT、LoRa、433、GPRS、2.4G、PKE近场通信,基础理论与开发点滴总结 2、蓝牙无线技术(BLE)与开发点滴总结 3、Zigbee无线技术与开发点滴总结 4、WIFI无线技术与开发点滴总结 本章

五 蓝牙低功耗(BLE)协议栈 之 ATT层-爱代码爱编程

一 介绍 ATT全称是Attribute protocol(数据交互协议),这一层的关键词是Attribute(属性)。一个属性其实就是一条数据,属性是BLE数据提供单元,也是蓝牙空中传播数据的最上层,BLE开发过程中接触最多的就是这一层。 比如有一个Person的类,这个类有name、age、address属性 public class Perso

走进蓝牙服务-爱代码爱编程

蓝牙服务是在我进入工作中参与公司实际开发的项目的时候才真的了解到,在学校使用的蓝牙基本都是蓝牙2.0的只能进行点对点的通信。距离短这是所有蓝牙的共性这个就不说,在我的理解中如果的是蓝牙2.0经常用在教育类和电子DIY。智能小车以及学校教育开发安卓连接一个蓝牙进行控制一个下位机开关或者风扇。安卓开发的时候可能会接触到蓝牙服务但是老师应该讲的都不多。 蓝牙

Android BLE(低功耗蓝牙)技术总结-爱代码爱编程

文章目录 前言一、蓝牙介绍1.什么是蓝牙?2.蓝牙版本介绍二、低功耗蓝牙(BLE)1.BLE介绍2.经典蓝牙(Classic Bluetooth)与低功耗蓝牙(BLE)的区别3.低功耗蓝牙(BLE)基本概念讲解GATT简介Profile(数据配置文件)Service(服务)Characteristic(特征)Descriptor(描述符)三、And

ble 协议android,「知识分享」解答Android 蓝牙开发之BLE介绍和蓝牙协议的一些知识...-爱代码爱编程

原标题:「知识分享」解答Android 蓝牙开发之BLE介绍和蓝牙协议的一些知识 其实BLE是个通用的技术术语,与平台无关的,即ios和Android以及一些嵌入式系统或单片机都可以有BLE模块。 那今天先对Android BLE相关的应用开发的进行讲解,不过在了解之前,我们有必要去了解BLE简单介绍和蓝牙协议的一些知识。 BLE简单介绍

蓝牙BLE(协议栈、OSAL、手机APP蓝牙工具nrf Connect的使用)-爱代码爱编程

目录 蓝牙4.0 BLE信道(RF Channel)BLE协议栈PHY层(Physical layer 物理层)LL层(Link Layer 链路层)HCI(Host controller interface 主机控制接口层)L2CAP层(Logic link control and adaptation protocol 逻辑链路控制和自适应协议

nrf52832 学习笔记(三)蓝牙从机广播_蓝牙从设备广播-爱代码爱编程

nrf52832 学习笔记(三)蓝牙从机广播 蓝牙从机要想被主机连接,首先需要发送广播信息,周围主机通过扫描广播信号,根据从机的广播信息,判断是否连接。 蓝牙协议栈初始化 不管是主机还是从机,要想使用蓝牙功能,都需要对

android 蓝牙ble全面解析 -爱代码爱编程

同学,别退出呀,我可是全网最牛逼的 Android 蓝牙分析博主,我写了上百篇蓝牙文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。Android 蓝牙低功耗ble 广播、扫描、连接、数据读写源码流程分析大全 - 点击下载 一、蓝牙BLE产生背景——蓝牙的发展历程         要说蓝牙BLE的产生