/* This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include #include #include #include "nvs.h" #include "nvs_flash.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "audio_pipeline.h" #include "i2s_stream.h" #include "board.h" #include "fifostream.h" #include "esp_bt.h" #include "esp_bt_main.h" #include "esp_gap_bt_api.h" #include "esp_bt_device.h" #include "esp_spp_api.h" #include "time.h" #include "sys/time.h" extern void pdmain_init( void); #define SPP_TAG "WOMBAT" #define SPP_SERVER_NAME "SPP_SERVER" #define EXCAMPLE_DEVICE_NAME "ESP_SPP_ACCEPTOR" #define SPP_SHOW_DATA 0 #define SPP_SHOW_SPEED 1 #define SPP_SHOW_MODE SPP_SHOW_DATA /*Choose show mode: show data or speed*/ void pdmain_print( const char *s) { ESP_LOGI("PD", "%s" , s); } static const esp_spp_mode_t esp_spp_mode = ESP_SPP_MODE_CB; static struct timeval time_new, time_old; static long data_num = 0; static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_AUTHENTICATE; static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE; static void print_speed(void) { float time_old_s = time_old.tv_sec + time_old.tv_usec / 1000000.0; float time_new_s = time_new.tv_sec + time_new.tv_usec / 1000000.0; float time_interval = time_new_s - time_old_s; float speed = data_num * 8 / time_interval / 1000.0; ESP_LOGI(SPP_TAG, "speed(%fs ~ %fs): %f kbit/s" , time_old_s, time_new_s, speed); data_num = 0; time_old.tv_sec = time_new.tv_sec; time_old.tv_usec = time_new.tv_usec; } extern int phaseinc; static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { switch (event) { case ESP_SPP_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); esp_bt_dev_set_device_name(EXCAMPLE_DEVICE_NAME); esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); esp_spp_start_srv(sec_mask,role_slave, 0, SPP_SERVER_NAME); break; case ESP_SPP_DISCOVERY_COMP_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT"); break; case ESP_SPP_OPEN_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT"); break; case ESP_SPP_CLOSE_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT"); break; case ESP_SPP_START_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT"); break; case ESP_SPP_CL_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT"); break; case ESP_SPP_DATA_IND_EVT: #if (SPP_SHOW_MODE == SPP_SHOW_DATA) ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle); esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); { char foo[80]; int nfoo = (param->data_ind.len > 78 ? 78 : param->data_ind.len), i; for (i = 0; i < nfoo; i++) foo[i] = param->data_ind.data[i]; foo[i] = 0; ESP_LOGI(SPP_TAG, "%s", foo); sscanf(foo, "%d%d", &i, &phaseinc); } #else gettimeofday(&time_new, NULL); data_num += param->data_ind.len; if (time_new.tv_sec - time_old.tv_sec >= 3) { print_speed(); } #endif break; case ESP_SPP_CONG_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CONG_EVT"); break; case ESP_SPP_WRITE_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT"); break; case ESP_SPP_SRV_OPEN_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT"); gettimeofday(&time_old, NULL); break; default: break; } } void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { switch (event) { case ESP_BT_GAP_AUTH_CMPL_EVT:{ if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { ESP_LOGI(SPP_TAG, "authentication success: %s", param->auth_cmpl.device_name); esp_log_buffer_hex(SPP_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN); } else { ESP_LOGE(SPP_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); } break; } case ESP_BT_GAP_PIN_REQ_EVT:{ ESP_LOGI(SPP_TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); if (param->pin_req.min_16_digit) { ESP_LOGI(SPP_TAG, "Input pin code: 0000 0000 0000 0000"); esp_bt_pin_code_t pin_code = {0}; esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); } else { ESP_LOGI(SPP_TAG, "Input pin code: 1234"); esp_bt_pin_code_t pin_code; pin_code[0] = '1'; pin_code[1] = '2'; pin_code[2] = '3'; pin_code[3] = '4'; esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); } break; } #if (CONFIG_BT_SSP_ENABLED == true) case ESP_BT_GAP_CFM_REQ_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); break; case ESP_BT_GAP_KEY_NOTIF_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); break; case ESP_BT_GAP_KEY_REQ_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); break; #endif default: { ESP_LOGI(SPP_TAG, "event: %d", event); break; } } return; } void audio_main(void) { audio_pipeline_handle_t pipeline; audio_element_handle_t i2s_stream_writer, i2s_stream_reader, fifostream; esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set(SPP_TAG, ESP_LOG_DEBUG); ESP_LOGI(SPP_TAG, "[ 1 ] Start codec chip"); audio_board_handle_t board_handle = audio_board_init(); audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START); ESP_LOGI(SPP_TAG, "[ 2 ] Create audio pipeline for playback"); audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG(); pipeline = audio_pipeline_init(&pipeline_cfg); ESP_LOGI(SPP_TAG, "[3.1] Create i2s stream to write data to codec chip"); i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT(); i2s_cfg.type = AUDIO_STREAM_WRITER; i2s_stream_writer = i2s_stream_init(&i2s_cfg); ESP_LOGI(SPP_TAG, "[3.1b] fifostream"); fifostream_cfg_t eq_cfg = DEFAULT_FIFOSTREAM_CONFIG(); int set_gain[] = {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0,}; eq_cfg.set_gain = set_gain; fifostream = fifostream_init(&eq_cfg); ESP_LOGI(SPP_TAG, "[3.2] Create i2s stream to read data from codec chip"); i2s_stream_cfg_t i2s_cfg_read = I2S_STREAM_CFG_DEFAULT(); i2s_cfg_read.type = AUDIO_STREAM_READER; i2s_stream_reader = i2s_stream_init(&i2s_cfg_read); ESP_LOGI(SPP_TAG, "[3.3] Register all elements to audio pipeline"); audio_pipeline_register(pipeline, i2s_stream_reader, "i2s_read"); audio_pipeline_register(pipeline, fifostream, "wombat"); audio_pipeline_register(pipeline, i2s_stream_writer, "i2s_write"); ESP_LOGI(SPP_TAG, "[3.4] Link it together [codec_chip]-->i2s_stream_reader-->i2s_stream_writer-->[codec_chip]"); const char *link_tag[3] = {"i2s_read", "wombat", "i2s_write"}; audio_pipeline_link(pipeline, &link_tag[0], 3); ESP_LOGI(SPP_TAG, "[ 4 ] Set up event listener"); audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG(); audio_event_iface_handle_t evt = audio_event_iface_init(&evt_cfg); ESP_LOGI(SPP_TAG, "[4.1] Listening event from all elements of pipeline"); audio_pipeline_set_listener(pipeline, evt); ESP_LOGI(SPP_TAG, "[ 5 ] Start audio_pipeline"); audio_pipeline_run(pipeline); ESP_LOGI(SPP_TAG, "[ 7 ] Listen for all pipeline events"); while (1) { audio_event_iface_msg_t msg; esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY); if (ret != ESP_OK) { ESP_LOGE(SPP_TAG, "[ * ] Event interface error : %d", ret); continue; } /* Stop when the last pipeline element (i2s_stream_writer in this case) receives stop event */ if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_writer && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED))) { ESP_LOGW(SPP_TAG, "[ * ] Stop event received"); break; } } ESP_LOGI(SPP_TAG, "[ 8 ] Stop audio_pipeline"); audio_pipeline_stop(pipeline); audio_pipeline_wait_for_stop(pipeline); audio_pipeline_terminate(pipeline); audio_pipeline_unregister(pipeline, i2s_stream_reader); audio_pipeline_unregister(pipeline, i2s_stream_writer); /* Terminate the pipeline before removing the listener */ audio_pipeline_remove_listener(pipeline); /* Make sure audio_pipeline_remove_listener & audio_event_iface_remove_listener are called before destroying event_iface */ audio_event_iface_destroy(evt); /* Release all resources */ audio_pipeline_deinit(pipeline); audio_element_deinit(i2s_stream_reader); audio_element_deinit(i2s_stream_writer); } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bluedroid_init()) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bluedroid_enable()) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s gap register failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_spp_register_callback(esp_spp_cb)) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s spp register failed: %s\n", __func__, esp_err_to_name(ret)); return; } if ((ret = esp_spp_init(esp_spp_mode)) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s spp init failed: %s\n", __func__, esp_err_to_name(ret)); return; } #if (CONFIG_BT_SSP_ENABLED == true) /* Set default parameters for Secure Simple Pairing */ esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); #endif /* * Set default parameters for Legacy Pairing * Use variable pin, input pin code when pairing */ esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE; esp_bt_pin_code_t pin_code; esp_bt_gap_set_pin(pin_type, 0, pin_code); pdmain_init(); audio_main(); }