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{
}
}
大體流程就是這樣的,具體資料細節處理業務就不細述了。