ESP32入门基础之ble spp client 和 ble spp server 的学习理解

Posted while(1)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32入门基础之ble spp client 和 ble spp server 的学习理解相关的知识,希望对你有一定的参考价值。

文章目录

1 工程简介

本次实验使用开发板如下

参考工程

  • 工程 ble_spp_client :为客户端,作为扫描,SPP 即 Serial Port Profile
  • 工程 ble_spp_server:为服务端,作为广播,SPP 即 Serial Port Profile

基本功能1:客户端串口输入数据会通过蓝牙将数据发送给服务端并由串口打印出来;服务端串口输入数据会通过蓝牙将数据发送给客户端并由串口打印出来;

2 工程分析

2.1 工程 ble_spp_client 分析

2.1.1 函数分析

从函数 app_main 开始

  • 初始化内存
  • 初始化bt controller并使能
  • 初始化bluedroid并使能
  • ble_client_appRegister
    在该函数下实现如下功能
    1. GAP下注册扫描回调函数。esp_ble_gap_register_callback(esp_gap_cb))
    2. GATT下注册回调函数。esp_ble_gattc_register_callback(esp_gattc_cb))
    3. GATT下设置MTU(Maximum Transmission Unit)值。
    4. 创建client queue、spp_client_reg_task。
  • 初始化串口 spp_uart_init
    在该函数下实现如下功能
    1. 初始化串口及串口中断、创建串口队列和串口任务。

2.1.2 两BLE扫描连接、配置、参数同步分析

—> 表示前面的函数直接调用后面的函数
- - - > 表示前面的函数执行后触发后面的函数

  1. app_main() —> ble_client_appRegister() —> esp_ble_gattc_app_register(PROFILE_APP_ID) (产生ESP_GATTC_REG_EVT事件)- - - >esp_gattc_cb() —> gattc_profile_event_handler()。完成扫描参数的配置。

    static void gattc_profile_event_handler(...)
    
        switch (event) 
        case ESP_GATTC_REG_EVT:
            esp_ble_gap_set_scan_params(&ble_scan_params);
            break;
    
  2. esp_ble_gap_set_scan_params(&ble_scan_params) (产生ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT事件)- - -> esp_gap_cb()。开始扫描

    static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
    
        switch(event)
        case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: 
            esp_ble_gap_start_scanning(duration);
            break;
        
    
  3. esp_ble_gap_start_scanning()(产生ESP_GAP_BLE_SCAN_START_COMPLETE_EVT事件) - - - > esp_gap_cb()。这里没有执行任何函数,在实际工程中可根据需求添加相关函数

    static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
    
    
        switch(event)
        case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
            //scan start complete event to indicate scan start successfully or failed
            if ((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) 
                ESP_LOGE(GATTC_TAG, "Scan start failed: %s", esp_err_to_name(err));
                break;
            
            ESP_LOGI(GATTC_TAG, "Scan start successed");
            break;
    
  4. esp_ble_gap_start_scanning()(产生ESP_GAP_BLE_SCAN_RESULT_EVT事件) - - - > esp_gap_cb()。扫描开始后会搜索空中的BLE蓝牙广播数据,

    static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
    
        switch(event)
        case ESP_GAP_BLE_SCAN_RESULT_EVT: 
            esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
            switch (scan_result->scan_rst.search_evt) 
            case ESP_GAP_SEARCH_INQ_RES_EVT:
                adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);//解析广播数据
                if (adv_name != NULL) 
                    // 判断名字是否为指定的名字
                    if ( strncmp((char *)adv_name, device_name, adv_name_len) == 0) 
                        memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
                        esp_ble_gap_stop_scanning();
                    
                
            
            break;
        
    

    如果搜索不到指定的蓝牙,会一直搜索,串口数据打印如下(上诉代码删除了串口输出函数,详细请参考工程)

    如果搜索到指定的蓝牙 static const char device_name[] = "ESP_SPP_SERVER";,会停止广播

  5. esp_ble_gap_stop_scanning()(产生ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT事件) - - - > esp_gap_cb()。停止扫描并根据搜索到的蓝牙广播进行连接。

    static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
    
        switch(event)
        case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
            ESP_LOGI(GATTC_TAG, "Scan stop successed");
            if (is_connect == false) 
                ESP_LOGI(GATTC_TAG, "Connect to the remote device.");
                //Open a direct connection or add a background auto connection
                esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, scan_rst.scan_rst.bda, scan_rst.scan_rst.ble_addr_type, true);
            
            break;
    
  6. esp_ble_gattc_open() (产生ESP_GATTC_CONNECT_EVT事件)- - - > esp_gattc_cb() —> gattc_profile_event_handler()。完成连接

    static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    
        esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
        switch (event) 
        case ESP_GATTC_CONNECT_EVT:
            spp_gattc_if = gattc_if;
            is_connect = true;
            spp_conn_id = p_data->connect.conn_id;
            memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
            // This function is called to get service from local cache.This function report service search result by a callback event, and followed by a service search complete event.
            esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid);
            break;
    
  7. esp_ble_gattc_search_service() (产生ESP_GATTC_SEARCH_RES_EVT事件)- - - > esp_gattc_cb() —> gattc_profile_event_handler()

    static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    
        esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
        switch (event) 
        case ESP_GATTC_SEARCH_RES_EVT:
            spp_srv_start_handle = p_data->search_res.start_handle;
            spp_srv_end_handle = p_data->search_res.end_handle;
            break;
    
  8. esp_ble_gattc_search_service() (产生ESP_GATTC_SEARCH_CMPL_EVT事件)- - - > esp_gattc_cb() —> gattc_profile_event_handler()。同步mtu的配置,需要提前调用函数esp_ble_gatt_set_local_mtu();

    static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    
        esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
        switch (event) 
        case ESP_GATTC_SEARCH_CMPL_EVT:
        	//Configure the MTU size in the GATT channel. This can be done,only once per connection. Before using, use esp_ble_gatt_set_local_mtu() to configure the local MTU size.
            esp_ble_gattc_send_mtu_req(gattc_if, spp_conn_id);
            break;
    
  9. esp_ble_gattc_send_mtu_req() (产生ESP_GATTC_CFG_MTU_EVT事件)- - - > esp_gattc_cb() —> gattc_profile_event_handler()

    	static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    	
    	    esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
    	    switch (event) 
    	        case ESP_GATTC_CFG_MTU_EVT:
           		if(esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK)
    	            ESP_LOGE(GATTC_TAG,"%s:get db falied\\n",__func__);
    	            break;
           		
            	cmd = SPP_IDX_SPP_DATA_NTY_VAL;
           		xQueueSend(cmd_reg_queue, &cmd, 10/portTICK_PERIOD_MS);
    	        break;
    

    这里将队列发送后,有函数 spp_client_reg_task(); 接收,队列参数为 SPP_IDX_SPP_DATA_NTY_VAL

    void spp_client_reg_task(void* arg)
    
        uint16_t cmd_id;
        for(;;) 
            vTaskDelay(100 / portTICK_PERIOD_MS);
            if(xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY)) 
                if(db != NULL) 
                    if(cmd_id == SPP_IDX_SPP_DATA_NTY_VAL)
                        ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \\n", cmd_id, (db+SPP_IDX_SPP_DATA_NTY_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle);
                        esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle);
    
  10. 函数 esp_ble_gattc_register_for_notify(...)SPP_IDX_SPP_DATA_NTY_VAL 属性注册完成后,就可以接收蓝牙信息了

    static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    
        esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
        switch (event) 
        case ESP_GATTC_NOTIFY_EVT:
            ESP_LOGI(GATTC_TAG,"ESP_GATTC_NOTIFY_EVT\\n");
            notify_event_handler(p_data);
            break;
    

    notify_event_handler(p_data); 函数中可根据用户需求来处理数据。

2.1.3 蓝牙数据发送流程分析

  1. 在电脑串口助手客户端发送数据后,数据在uart_task函数被接收并通过蓝牙将数据发送给服务端

    void uart_task(void *pvParameters)
    
        for (;;) 
            //Waiting for UART event.
            if (xQueueReceive(spp_uart_queue, (void * )&event, (portTickType)portMAX_DELAY)) 
                switch (event.type) 
                //Event of UART receving data
                case UART_DATA:
                        uart_read_bytes(...); //接收串口数据
                        esp_ble_gattc_write_char(...);//蓝牙发送数据
    

    esp_ble_gattc_write_char() (产生ESP_GATTC_WRITE_CHAR_EVT事件) - - - > esp_gattc_cb() —> gattc_profile_event_handler()

    		static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    		
    		    esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
    		    switch (event) 
    		    case ESP_GATTC_WRITE_CHAR_EVT:
    		        ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_CHAR_EVT:status = %d,handle = %d", param->write.status, param->write.handle);
    

  2. 在电脑串口助手服务端发送数据后,客户端收到数据后会触发ESP_GATTC_NOTIFY_EVT事件。 esp_gattc_cb() —> gattc_profile_event_handler()

    static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
    
        esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
        switch (event) 
        case ESP_GATTC_NOTIFY_EVT:
            ESP_LOGI(GATTC_TAG,"ESP_GATTC_NOTIFY_EVT\\n");
            notify_event_handler(p_data);
            break;
    

    notify_event_handler(p_data); 函数中可根据用户需求来处理数据。

2.2 工程 ble_spp_server 分析

与 《2.1 工程 ble_spp_client 分析》类似,这里省略。

2.3 属性(Attribute)分析

以上是关于ESP32入门基础之ble spp client 和 ble spp server 的学习理解的主要内容,如果未能解决你的问题,请参考以下文章

ESP32入门基础之ESP WebSocket Client

esp32与jdy蓝牙模块有通信障碍吗

ESP32入门基础之安全连接SMP-client

ESP32入门基础之资源管理

ESP32入门基础之资源管理

ESP32-C3入门教程 网络篇⑤——TCP Socket Client 客户端应用示例