1. 程式人生 > >ESP-32S做BLE透傳模組日誌

ESP-32S做BLE透傳模組日誌

        本次專案是用ESP_32S做一個低功耗藍芽BLE的透傳模組,模組將掃描到的藍芽名稱以及裝置地址通過USB口(串列埠)傳出,上位機通過串列埠輸入要連線的藍芽名稱後,模組進行連線配對,配對成功後就將USB口收到的資料通過藍芽傳送到配對端。

       硬體:ESP-32S模組,以及必要的外圍電路。

      軟體系統:FreeRTOS,介面程式碼採用樂鑫提供的ESP32模組BSP包

1、串列埠初始化

static void Uart_init(void)
{
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_RTS,
        .rx_flow_ctrl_thresh = 122,
    };

    //Set UART parameters
    uart_param_config(UART_NUM_0, &uart_config);
    //Set UART pins
    uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //Install UART driver, and get the queue.
    uart_driver_install(UART_NUM_0, 4096, 8192, 10,&spp_uart_queue,0);
    xTaskCreate(uart_task, "uTask", 2048, (void*)UART_NUM_0, 8, NULL);
}

2、藍芽功能初始化

void ble_client_appRegister(void)
{
    esp_err_t status;
    char err_msg[20];

    ESP_LOGI(GATTC_TAG, "register callback");

    //register the scan callback function to the gap module
    if ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) {
        ESP_LOGE(GATTC_TAG, "gap register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
        return;
    }

    //register the callback function to the gattc module
    if ((status = esp_ble_gattc_register_callback(esp_gattc_cb)) != ESP_OK) {
        ESP_LOGE(GATTC_TAG, "gattc register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
        return;
    }
    esp_ble_gattc_app_register(PROFILE_APP_ID);

    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(200); //設定MTU最大為200
    if (local_mtu_ret){
        ESP_LOGE(GATTC_TAG, "set local  MTU failed: %s", esp_err_to_name_r(local_mtu_ret, err_msg, sizeof(err_msg)));
    }
    cmd_reg_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(spp_client_reg_task, "spp_client_reg_task", 2048, NULL, 10, NULL);

}

void BLE_init()
{
    esp_err_t ret;

    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

    memcpy(g_BLE_name, "TestBluetooth", 13);
    nvs_flash_init();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ESP_LOGI(GATTC_TAG, "%s init bluetooth\n", __func__);
    ret = esp_bluedroid_init();
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }
    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ble_client_appRegister();
    
}

3、啟動掃描

 呼叫ESP介面 esp_ble_gap_start_scanning(0xff);

 掃描到藍芽裝置後會觸發事件 ESP_GAP_BLE_SCAN_RESULT_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)
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_SHORT, &adv_name_len);
Uart_Send_ScanDevInfo(scan_result->scan_rst.bda,adv_name,adv_name_len);

4、收到上位機通過串列埠輸入連線裝置名稱後,將掃描到的裝置名稱與目標名稱進行對比,如果相符就停止掃描

if (adv_name != NULL) 
{   
     if(g_device_nameFlag && (strncmp((char *)adv_name, g_BLE_name, adv_name_len) == 0)) 
     {
          memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
           esp_ble_gap_stop_scanning();
     }
}

停止掃描後會觸發 ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT事件,在事件觸發時啟動連線

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

5、連線成功後會觸發gattc_profile_event_handler()中的 ESP_GATTC_CONNECT_EVT事件。

連線事件處理:設定資料長度

is_connect = true;
gl_profile_tab[PROFILE_APP_ID].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));
esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_APP_ID].remote_bda, sizeof(esp_bd_addr_t));
     
 esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req(gattc_if, spp_conn_id);
 if (mtu_ret){
     ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
 }

6、設定成功後觸發ESP_GATTC_CFG_MTU_EVT事件。在此事件中獲取裝置資訊

        if(p_data->cfg_mtu.status != ESP_OK){
            break;
        }
        ESP_LOGI(GATTC_TAG,"+MTU:%d\n", p_data->cfg_mtu.mtu);
        spp_mtu_size = p_data->cfg_mtu.mtu;
        
        spp_conn_id = p_data->connect.conn_id;
        spp_gattc_if = gattc_if;
        esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid);

7、獲取成功後觸發ESP_GATTC_SEARCH_RES_EVT事件,然後設定通訊需要的佇列、資料通道等,就可以進行通訊了,

 

8、當串列埠收到資料後,呼叫

 esp_ble_gattc_write_char( spp_gattc_if,spp_conn_id,
                           (db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle,
                           event.size,
                           pBuf,
                           ESP_GATT_WRITE_TYPE_RSP,
                           ESP_GATT_AUTH_REQ_NONE);

將資料傳輸給BLE服務端。

9、當模組收到BLE服務端的資料,會觸發通知事件:ESP_GATTC_NOTIFY_EVT,在該事件出發時,呼叫

 notify_event_handler(p_data);將資料通過串列埠傳送出去

static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
{
    uint8_t handle = 0;

    if(p_data->notify.is_notify == true){
        ESP_LOGI(GATTC_TAG,"+NOTIFY:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len);
    }else{
        ESP_LOGI(GATTC_TAG,"+INDICATE:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len);
    }
    handle = p_data->notify.handle;
    if(handle == db[SPP_IDX_SPP_DATA_NTY_VAL].attribute_handle){
        if((p_data->notify.value[0] == '#')&&(p_data->notify.value[1] == '#')){   //配置資料
            if((++notify_value_count) != p_data->notify.value[3]){
                if(notify_value_p != NULL){
                    free(notify_value_p);
                }
                notify_value_count = 0;
                notify_value_p = NULL;
                notify_value_offset = 0;
                ESP_LOGE(GATTC_TAG,"notify value count is not continuous,%s\n",__func__);
                return;
            }
            if(p_data->notify.value[3] == 1){
                notify_value_p = (char *)malloc(((spp_mtu_size-7)*(p_data->notify.value[2]))*sizeof(char));
                if(notify_value_p == NULL){
                    ESP_LOGE(GATTC_TAG, "malloc failed,%s L#%d\n",__func__,__LINE__);
                    notify_value_count = 0;
                    return;
                }
                memcpy((notify_value_p + notify_value_offset),(p_data->notify.value + 4),(p_data->notify.value_len - 4));
                if(p_data->notify.value[2] == p_data->notify.value[3]){
                    uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset));
                    free(notify_value_p);
                    notify_value_p = NULL;
                    notify_value_offset = 0;
                    return;
                }
                notify_value_offset += (p_data->notify.value_len - 4);
            }else if(p_data->notify.value[3] <= p_data->notify.value[2]){
                memcpy((notify_value_p + notify_value_offset),(p_data->notify.value + 4),(p_data->notify.value_len - 4));
                if(p_data->notify.value[3] == p_data->notify.value[2]){
                    uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset));
                    free(notify_value_p);
                    notify_value_count = 0;
                    notify_value_p = NULL;
                    notify_value_offset = 0;
                    return;
                }
                notify_value_offset += (p_data->notify.value_len - 4);
            }
        }else{
            uart_write_bytes(UART_NUM_0, (char *)(p_data->notify.value), p_data->notify.value_len);
        }
    }else if(handle == ((db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle)){

    }else{

    }
}

大體流程就是這樣的,具體資料細節處理業務就不細述了。