Record files. but 40s to 20s

This commit is contained in:
Colin 2026-01-02 01:10:53 +08:00
parent bda0fd6096
commit 157336a955
9 changed files with 1192 additions and 497 deletions

View File

@ -40,6 +40,12 @@
"gpio.h": "c",
"string.h": "c",
"i2s_reg.h": "c",
"complex": "c"
"complex": "c",
"errno.h": "c",
"freertos.h": "c",
"stdbool.h": "c",
"esp_afe_sr_models.h": "c",
"esp_afe_config.h": "c",
"esp_afe_sr_iface.h": "c"
}
}

View File

@ -1,2 +1,2 @@
idf_component_register(SRCS "main.c"
idf_component_register(SRCS "main.c" "app_sr.c" "usb_msc.c"
INCLUDE_DIRS ".")

383
main/app_sr.c Normal file
View File

@ -0,0 +1,383 @@
#include "app_sr.h"
#include <errno.h>
// #include "app_ui.h"
// #include "audio_player.h"
// #include "esp32_s3_szp.h"
#include "esp_afe_sr_iface.h"
#include "esp_afe_sr_models.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "esp_mn_speech_commands.h"
#include "esp_process_sdkconfig.h"
#include "esp_wn_iface.h"
#include "esp_wn_models.h"
#include "format_wav.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "model_path.h"
static const char* TAG = "app_sr";
srmodel_list_t* models = NULL;
static esp_afe_sr_iface_t* afe_handle = NULL;
static esp_afe_sr_data_t* afe_data = NULL;
int detect_flag = 0;
static volatile int task_flag = 0;
FILE* f1;
FILE* f2;
size_t wav_written1;
size_t wav_written2;
esp_err_t record_start1() {
esp_err_t ret = ESP_OK;
const wav_header_t wav_header1 =
WAV_HEADER_PCM_DEFAULT(16000 * 2 * 16 / 8 * 20, 16, 16000, 2);
ESP_LOGI(TAG, "Opening file %s", "/sdcard/aaaaa.wav");
f1 = fopen("/sdcard/aaaaa.wav", "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
if (f1 == ESP_FAIL) ESP_LOGI(TAG, "error while opening wav file");
if (fwrite(&wav_header1, sizeof(wav_header_t), 1, f1) == 1)
ESP_LOGI(TAG, "error while writing wav header");
wav_written1 = 0;
return ESP_OK;
}
esp_err_t record_start2() {
esp_err_t ret = ESP_OK;
const wav_header_t wav_header2 =
WAV_HEADER_PCM_DEFAULT(16000 * 1 * 16 / 8 * 20, 16, 16000, 1);
f2 = fopen("/sdcard/bbbbb.wav", "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
if (f2 == ESP_FAIL) ESP_LOGI(TAG, "error while opening wav file");
if (fwrite(&wav_header2, sizeof(wav_header_t), 1, f2) == 1)
ESP_LOGI(TAG, "error while writing wav header");
wav_written2 = 0;
return ESP_OK;
}
esp_err_t record_add1(int16_t* data1, uint32_t size) {
uint32_t byte_rate = 16000 * 2 * 16 / 8;
uint32_t wav_size = byte_rate * 20;
if (wav_written1 < wav_size) {
fwrite(data1, size, 1, f1);
// ESP_RETURN_ON_FALSE(fwrite(data1, size, 1, f1), ESP_FAIL, TAG,
// "error while writing samples to wav file");
wav_written1 += size;
return ESP_OK;
} else {
if (wav_written1 < 0xFFFFFFFF) {
ESP_LOGI(TAG, "Recording done 1! Flushing file buffer");
fclose(f1);
wav_written1 = 0xFFFFFFFF;
}
}
return ESP_OK;
}
bool record_end1() { return wav_written1 == 0xFFFFFFFF; }
bool record_end2() { return wav_written2 == 0xFFFFFFFF; }
esp_err_t record_add2(int16_t* data2, uint32_t size) {
uint32_t byte_rate = 16000 * 1 * 16 / 8;
uint32_t wav_size = byte_rate * 20;
if (wav_written2 < wav_size) {
fwrite(data2, size, 1, f2);
// ESP_RETURN_ON_FALSE(fwrite(data2, size, 1, f2), ESP_FAIL, TAG,
// "error while writing samples to wav file");
wav_written2 += size;
return ESP_OK;
} else {
if (wav_written2 < 0xFFFFFFFF) {
ESP_LOGI(TAG, "Recording done 2! Flushing file buffer");
fclose(f2);
wav_written2 = 0xFFFFFFFF;
}
}
return ESP_OK;
}
void afe(esp_afe_sr_data_t* afe_data, int16_t* i2s_buff) {
afe_handle->feed(afe_data, i2s_buff); // 把获取到的I2S数据输入给afe_data
afe_fetch_result_t* res = afe_handle->fetch(afe_data);
}
void feed_Task(void* arg) {
esp_afe_sr_data_t* afe_data = arg; // 获取参数
int audio_chunksize = afe_handle->get_feed_chunksize(afe_data); // 获取帧长度
int nch = afe_handle->get_channel_num(afe_data); // 获取声道数
// int feed_channel = bsp_get_feed_channel(); // 获取ADC输入通道数
int feed_channel = 2;
assert(nch <= feed_channel);
int16_t* i2s_buff = heap_caps_malloc(
audio_chunksize * sizeof(int16_t) * feed_channel,
MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); // 分配获取I2S数据的缓存大小
assert(i2s_buff);
printf("AFE audio_chunksize : %d\n", audio_chunksize);
printf("AFE nch: %d\n", nch);
printf("AFE feed_channel: %d\n", feed_channel);
while (task_flag) {
// bsp_get_feed_data(
// false, i2s_buff,
// audio_chunksize * sizeof(int16_t) * feed_channel); // 获取I2S数据
record_add1(i2s_buff, audio_chunksize * sizeof(int16_t) * 2);
afe_handle->feed(afe_data, i2s_buff); // 把获取到的I2S数据输入给afe_data
}
if (i2s_buff) {
free(i2s_buff);
i2s_buff = NULL;
}
vTaskDelete(NULL);
}
void detect_Task(void* arg) {
esp_afe_sr_data_t* afe_data = arg; // 接收参数
int afe_chunksize =
afe_handle->get_fetch_chunksize(afe_data); // 获取fetch帧长度
char* mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX,
ESP_MN_CHINESE); // 初始化命令词模型
printf("multinet:%s\n", mn_name); // 打印命令词模型名称
esp_mn_iface_t* multinet = esp_mn_handle_from_name(mn_name);
model_iface_data_t* model_data =
multinet->create(mn_name, 6000); // 设置唤醒后等待事件 6000代表6000毫秒
esp_mn_commands_clear(); // 清除当前的命令词列表
esp_mn_commands_add(1, "bo fang yin yue"); // 播放音乐
esp_mn_commands_add(2, "zan ting"); // 暂停
esp_mn_commands_add(3, "ji xu"); // 继续
esp_mn_commands_add(4, "shang yi shou"); // 上一首
esp_mn_commands_add(5, "xia yi shou"); // 下一首
esp_mn_commands_add(6, "sheng yin da yi dian"); // 声音大一点
esp_mn_commands_add(7, "sheng yin xiao yi dian"); // 声音小一点
esp_mn_commands_update(); // 更新命令词
int mu_chunksize =
multinet->get_samp_chunksize(model_data); // 获取samp帧长度
assert(mu_chunksize == afe_chunksize);
// 打印所有的命令
multinet->print_active_speech_commands(model_data);
printf("------------detect start------------\n");
while (task_flag) {
afe_fetch_result_t* res = afe_handle->fetch(afe_data); // 获取模型输出结果
record_add2(res->data, res->data_size);
// int16_t *data; // the data of audio.
// int data_size; // the size of data. The unit is
// byte. float data_volume; // the volume of input
// audio, the unit is decibel(dB). This value is calculated before agc.
// (note: invalid in vc).
// // if enable wakenet, the window
// length is the receptive fields of
// wakenet(about 1.5s), otherwise is
// the frame length.
// wakenet_state_t wakeup_state; // the value is wakenet_state_t
// int wake_word_index; // if the wake word is detected.
// It will store the wake word index which start from 1. int
// wakenet_model_index; // if there are multiple wakenets,
// this value identifies which model be wakes up. Index start from 1.
// afe_vad_state_t vad_state; // the value is afe_vad_state_t
// int trigger_channel_id; // the channel index of output
// int wake_word_length; // the length of wake word. It's
// unit is the number of samples. int ret_value; //
// the return state of fetch function void* reserved; // reserved for future
// use
if (res->vad_state > 0) {
printf("AFE data_size: %d\n", res->data_size);
printf("AFE data_volume: %f\n", res->data_volume);
printf("AFE vad_state: %d\n", res->vad_state);
printf("AFE ret_value: %d\n", res->ret_value);
}
if (!res || res->ret_value == ESP_FAIL) {
printf("fetch error!\n");
break;
}
if (res->wakeup_state == WAKENET_DETECTED) {
printf("WAKEWORD DETECTED\n");
multinet->clean(model_data); // clean all status of multinet
} else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) { // 检测到唤醒词
// play_voice = -1;
afe_handle->disable_wakenet(afe_data); // 关闭唤醒词识别
detect_flag = 1; // 标记已检测到唤醒词
printf("AFE_FETCH_CHANNEL_VERIFIED, channel index: %d\n",
res->trigger_channel_id);
}
if (detect_flag == 1) {
esp_mn_state_t mn_state =
multinet->detect(model_data, res->data); // 检测命令词
if (mn_state == ESP_MN_STATE_DETECTING) {
continue;
}
if (mn_state == ESP_MN_STATE_DETECTED) { // 已检测到命令词
esp_mn_results_t* mn_result =
multinet->get_results(model_data); // 获取检测词结果
for (int i = 0; i < mn_result->num; i++) { // 打印获取到的命令词
printf("TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
i + 1, mn_result->command_id[i], mn_result->phrase_id[i],
mn_result->string, mn_result->prob[i]);
}
// 根据命令词 执行相应动作
// switch (mn_result->command_id[0]) {
// case 1: // bo fang yin yue 播放音乐
// ai_play();
// break;
// case 2: // zan ting 暂停
// ai_pause();
// break;
// case 3: // ji xu 继续
// ai_resume();
// break;
// case 4: // shang yi shou 上一首
// ai_prev_music();
// break;
// case 5: // xia yi shou 下一首
// ai_next_music();
// break;
// case 6: // sheng yin da yi dian 声音大一点
// ai_volume_up();
// break;
// case 7: // sheng yin xiao yi dian 声音小一点
// ai_volume_down();
// break;
// default:
// break;
// }
printf("\n-----------listening-----------\n");
}
if (mn_state == ESP_MN_STATE_TIMEOUT) { // 达到最大检测命令词时间
esp_mn_results_t* mn_result = multinet->get_results(model_data);
printf("timeout, string:%s\n", mn_result->string);
afe_handle->enable_wakenet(afe_data); // 重新打开唤醒词识别
detect_flag = 0; // 清除标记
printf("\n-----------awaits to be waken up-----------\n");
continue;
}
}
}
if (model_data) {
multinet->destroy(model_data);
model_data = NULL;
}
printf("detect exit\n");
vTaskDelete(NULL);
}
esp_afe_sr_data_t* sr_init() {
// 获取模型 名称“model”和分区表中装载模型的名称一致
models = esp_srmodel_init("model");
afe_handle = (esp_afe_sr_iface_t*)&ESP_AFE_SR_HANDLE;
// 先配置afe句柄
// 随后才可以调用afe接口
// afe_config_t afe_config = AFE_CONFIG_DEFAULT(); // 配置afe
afe_config_t afe_config = {
.aec_init = true,
.se_init = true,
.vad_init = true,
.wakenet_init = true,
.voice_communication_init = false,
.voice_communication_agc_init = false,
.voice_communication_agc_gain = 15,
.vad_mode = VAD_MODE_3,
.wakenet_model_name = NULL,
.wakenet_model_name_2 = NULL,
.wakenet_mode = DET_MODE_2CH_90,
.afe_mode = SR_MODE_LOW_COST,
.afe_perferred_core = 0,
.afe_perferred_priority = 5,
.afe_ringbuf_size = 50,
.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM,
.afe_linear_gain = 1.0,
.agc_mode = AFE_MN_PEAK_AGC_MODE_2,
.pcm_config =
{
.total_ch_num = 3,
.mic_num = 2,
.ref_num = 1,
.sample_rate = 16000,
},
.debug_init = false,
.debug_hook = {{AFE_DEBUG_HOOK_MASE_TASK_IN, NULL},
{AFE_DEBUG_HOOK_FETCH_TASK_IN, NULL}},
.afe_ns_mode = NS_MODE_SSP,
.afe_ns_model_name = NULL,
.fixed_first_channel = true,
};
// 配置唤醒模型 必须在create_from_config之前配置
afe_config.aec_init = true;
afe_config.se_init = true;
afe_config.vad_init = true;
afe_config.wakenet_init = false;
afe_config.voice_communication_init = false;
afe_config.voice_communication_agc_init = true;
afe_config.voice_communication_agc_gain = 15;
afe_config.vad_mode = VAD_MODE_3;
afe_config.wakenet_model_name = NULL;
afe_config.wakenet_model_name_2 = NULL;
afe_config.wakenet_mode = DET_MODE_2CH_90;
afe_config.afe_mode = SR_MODE_LOW_COST;
afe_config.afe_perferred_core = 0;
afe_config.afe_perferred_priority = 5;
afe_config.afe_ringbuf_size = 50;
afe_config.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
afe_config.afe_linear_gain = 4.0;
afe_config.agc_mode = AFE_MN_PEAK_AGC_MODE_2;
afe_config.pcm_config.total_ch_num = 3;
afe_config.pcm_config.mic_num = 2;
afe_config.pcm_config.ref_num = 1;
afe_config.pcm_config.sample_rate = 16000;
afe_config.debug_init = false;
// afe_config.debug_hook = {{AFE_DEBUG_HOOK_MASE_TASK_IN, NULL},
// {AFE_DEBUG_HOOK_FETCH_TASK_IN, NULL}};
afe_config.afe_ns_mode = NS_MODE_SSP;
afe_config.afe_ns_model_name = NULL;
// afe_config.wakenet_model_name =
// esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
return afe_handle->create_from_config(&afe_config); // 创建afe_data
// ESP_LOGI(TAG, "wakenet:%s", afe_config.wakenet_model_name); //
// 打印唤醒名称
}
void app_sr_init(void) {
afe_data = sr_init();
task_flag = 1;
record_start1();
record_start2();
xTaskCreatePinnedToCore(&detect_Task, "detect", 8 * 1024, (void*)afe_data, 5,
NULL, 1);
xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5,
NULL, 0);
while (wav_written2 != 0xFFFFFFFF || wav_written1 != 0xFFFFFFFF) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}

22
main/app_sr.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <stdbool.h>
#include "esp_afe_sr_iface.h"
#include "esp_afe_sr_models.h"
#include "esp_err.h"
void app_sr_init(void);
void app_sr_init(void);
void afe(esp_afe_sr_data_t* afe_data, int16_t* i2s_buff);
bool record_end1();
bool record_end2();
esp_err_t record_start1();
esp_err_t record_start2();
esp_err_t record_add1(int16_t* data1, uint32_t size);
esp_err_t record_add2(int16_t* data1, uint32_t size);

78
main/format_wav.h Normal file
View File

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Header structure for WAV file with only one data chunk
*
* @note See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
*
* @note Assignment to variables in this struct directly is only possible for
* little endian architectures (including Xtensa & RISC-V)
*/
typedef struct {
struct {
char chunk_id[4]; /*!< Contains the letters "RIFF" in ASCII form */
uint32_t chunk_size; /*!< This is the size of the rest of the chunk
following this number */
char chunk_format[4]; /*!< Contains the letters "WAVE" */
} descriptor_chunk; /*!< Canonical WAVE format starts with the RIFF header */
struct {
char subchunk_id[4]; /*!< Contains the letters "fmt " */
uint32_t subchunk_size; /*!< This is the size of the rest of the Subchunk
which follows this number */
uint16_t audio_format; /*!< PCM = 1, values other than 1 indicate some form
of compression */
uint16_t num_of_channels; /*!< Mono = 1, Stereo = 2, etc. */
uint32_t sample_rate; /*!< 8000, 44100, etc. */
uint32_t byte_rate; /*!< ==SampleRate * NumChannels * BitsPerSample s/ 8 */
uint16_t block_align; /*!< ==NumChannels * BitsPerSample / 8 */
uint16_t bits_per_sample; /*!< 8 bits = 8, 16 bits = 16, etc. */
} fmt_chunk; /*!< The "fmt " subchunk describes the sound data's format */
struct {
char subchunk_id[4]; /*!< Contains the letters "data" */
uint32_t
subchunk_size; /*!< ==NumSamples * NumChannels * BitsPerSample / 8 */
int16_t data[0]; /*!< Holds raw audio data */
} data_chunk; /*!< The "data" subchunk contains the size of the data and the
actual sound */
} wav_header_t;
/**
* @brief Default header for PCM format WAV files
*
*/
#define WAV_HEADER_PCM_DEFAULT(wav_sample_size, wav_sample_bits, \
wav_sample_rate, wav_channel_num) \
{ \
.descriptor_chunk = {.chunk_id = {'R', 'I', 'F', 'F'}, \
.chunk_size = \
(wav_sample_size) + sizeof(wav_header_t) - 8, \
.chunk_format = {'W', 'A', 'V', 'E'}}, \
.fmt_chunk = {.subchunk_id = {'f', 'm', 't', ' '}, \
.subchunk_size = 16, /* 16 for PCM */ \
.audio_format = 1, /* 1 for PCM */ \
.num_of_channels = (wav_channel_num), \
.sample_rate = (wav_sample_rate), \
.byte_rate = (wav_sample_bits) * (wav_sample_rate) * \
(wav_channel_num) / 8, \
.block_align = (wav_sample_bits) * (wav_channel_num) / 8, \
.bits_per_sample = (wav_sample_bits)}, \
.data_chunk = { \
.subchunk_id = {'d', 'a', 't', 'a'}, \
.subchunk_size = (wav_sample_size) \
} \
}
#ifdef __cplusplus
}
#endif

View File

@ -3,6 +3,8 @@ dependencies:
espressif/esp_tinyusb:
version: "^2.0.0"
espressif/es7210: "^1.0.0"
chmorgan/esp-file-iterator: "1.0.0" # 获取文件
espressif/esp-sr: "^1.6.0" # 语音识别
idf:
version: ">=5.4.0"

View File

@ -1,21 +1,68 @@
#include <stdio.h>
#include <string.h>
#include "app_sr.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "driver/i2s_tdm.h"
#include "driver/i2s_types.h"
#include "driver/sdmmc_host.h"
#include "es7210.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_pm.h"
#include "esp_private/esp_clk.h"
#include "esp_sleep.h"
#include "esp_timer.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "nvs_flash.h"
/* I2C port and GPIOs */
#define I2C_NUM (0)
#define I2C_SDA_IO (1)
#define I2C_SCL_IO (2)
#define BSP_I2C_SDA (GPIO_NUM_1) // SDA引脚
#define BSP_I2C_SCL (GPIO_NUM_2) // SCL引脚
#define BSP_I2C_NUM (0) // I2C外设
#define BSP_I2C_FREQ_HZ 100000 // 100kHz
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (38)
#define I2S_BCK_IO (14)
#define I2S_WS_IO (13)
#define I2S_DI_IO (12)
/* SD card GPIOs */
#define SD_CMD_IO (48)
#define SD_CLK_IO (47)
#define SD_DAT0_IO (21)
/* SD card & recording configurations */
#define RECORD_TIME_SEC (20)
#define SD_MOUNT_POINT "/sdcard"
/* I2S configurations */
#define I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S)
#define I2S_CHAN_NUM (2)
#define I2S_SAMPLE_RATE (16000)
#define I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256)
#define I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT)
#define I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1)
/* ES7210 configurations */
#define ES7210_I2C_ADDR (0x41)
#define ES7210_I2C_CLK (100000)
#define ES7210_MIC_GAIN (ES7210_MIC_GAIN_30DB)
#define ES7210_MIC_BIAS (ES7210_MIC_BIAS_2V87)
#define ES7210_ADC_VOLUME (32)
// 配置参数
#define SAMPLE_RATE 16000
#define I2S_PORT I2S_NUM_0
@ -36,7 +83,7 @@ static QueueHandle_t i2s_event_queue = NULL;
static SemaphoreHandle_t data_ready_sem = NULL;
// 中断服务程序的数据处理任务句柄
static TaskHandle_t i2s_dma_task_handle = NULL;
static TaskHandle_t rx_task_handle = NULL;
// 自定义数据结构用于队列传递
typedef struct {
@ -44,17 +91,113 @@ typedef struct {
size_t size;
} i2s_queue_data_t;
// 初始化GPIO
static void gpio_init(void) {
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << LED_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf);
gpio_set_level(LED_PIN, 0);
// static i2s_chan_handle_t es7210_i2s_init(void) {
// i2s_chan_handle_t i2s_rx_chan = NULL; // 定义接收通道句柄
// ESP_LOGI(TAG, "Create I2S receive channel");
// i2s_chan_config_t i2s_rx_conf = I2S_CHANNEL_DEFAULT_CONFIG(
// I2S_NUM_AUTO, I2S_ROLE_MASTER); // 配置接收通道
// ESP_ERROR_CHECK(
// i2s_new_channel(&i2s_rx_conf, NULL, &i2s_rx_chan)); // 创建i2s通道
// ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode");
// // 定义接收通道为I2S TDM模式 并配置
// i2s_tdm_config_t i2s_tdm_rx_conf = {
// .slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
// I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT_MASK),
// .clk_cfg = {.clk_src = I2S_CLK_SRC_DEFAULT,
// .sample_rate_hz = I2S_SAMPLE_RATE,
// .mclk_multiple = I2S_MCLK_MULTIPLE},
// .gpio_cfg = {.mclk = I2S_MCK_IO,
// .bclk = I2S_BCK_IO,
// .ws = I2S_WS_IO,
// .dout = -1, // ES7210 only has ADC capability
// .din = I2S_DI_IO},
// };
// ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(
// i2s_rx_chan, &i2s_tdm_rx_conf)); // 初始化I2S通道为TDM模式
// return i2s_rx_chan;
// }
sdmmc_card_t* mount_sdcard(void) {
sdmmc_card_t* sdmmc_card = NULL;
ESP_LOGI(TAG, "Mounting SD card");
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 8 * 1024};
ESP_LOGI(TAG, "Initializing SD card");
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t sdmmc_host = SDMMC_HOST_DEFAULT(); // SDMMC主机接口配置
sdmmc_slot_config_t slot_config =
SDMMC_SLOT_CONFIG_DEFAULT(); // SDMMC插槽配置
slot_config.width = 1; // 设置为1线SD模式
slot_config.clk = SD_CLK_IO;
slot_config.cmd = SD_CMD_IO;
slot_config.d0 = SD_DAT0_IO;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // 打开内部上拉电阻
ESP_LOGI(TAG, "Mounting filesystem");
esp_err_t ret;
while (1) {
ret = esp_vfs_fat_sdmmc_mount(SD_MOUNT_POINT, &sdmmc_host, &slot_config,
&mount_config, &sdmmc_card);
if (ret == ESP_OK) {
break;
} else if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). ",
esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(
TAG, "Card size: %lluMB, speed: %dMHz",
(((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >>
20,
sdmmc_card->max_freq_khz / 1000);
return sdmmc_card;
}
esp_err_t i2c_init(void) {
i2c_config_t i2c_conf = {.mode = I2C_MODE_MASTER,
.sda_io_num = BSP_I2C_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = BSP_I2C_SCL,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = BSP_I2C_FREQ_HZ};
i2c_param_config(BSP_I2C_NUM, &i2c_conf);
return i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0);
}
static void es7210_codec_init(void) {
// 创建es7210器件句柄
es7210_dev_handle_t es7210_handle = NULL;
es7210_i2c_config_t es7210_i2c_conf = {.i2c_port = I2C_NUM,
.i2c_addr = ES7210_I2C_ADDR};
ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle));
// 初始化es7210芯片
ESP_LOGI(TAG, "Configure ES7210 codec parameters");
es7210_codec_config_t codec_conf = {
.i2s_format = I2S_TDM_FORMAT,
.mclk_ratio = I2S_MCLK_MULTIPLE,
.sample_rate_hz = I2S_SAMPLE_RATE,
.bit_width = (es7210_i2s_bits_t)I2S_SAMPLE_BITS,
.mic_bias = ES7210_MIC_BIAS,
.mic_gain = ES7210_MIC_GAIN,
.flags.tdm_enable = true};
ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, ES7210_ADC_VOLUME));
}
// I2S数据接收任务 - 从DMA缓冲区读取数据并放入队列
@ -74,7 +217,6 @@ static void i2s_rx_task(void* arg) {
ESP_LOGI(TAG, "I2S接收任务开始运行");
while (1) {
// 从I2S通道读取数据 - 使用 short timeout to avoid blocking indefinitely
esp_err_t ret = ESP_OK;
ret = i2s_channel_read(rx_chan, data_buffer, DMA_BUF_LEN * sizeof(int16_t),
&bytes_read, pdMS_TO_TICKS(1));
@ -100,16 +242,14 @@ static void i2s_rx_task(void* arg) {
// } else {
// ESP_LOGE(TAG, "无法分配内存来复制I2S数据");
// }
ESP_LOGI(TAG, "处理 %d 字节的I2S数据", bytes_read);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
esp_sleep_enable_timer_wakeup(50 * 1000);
esp_light_sleep_start();
record_add1(data_buffer, bytes_read);
if (!record_end1()) ESP_LOGI(TAG, "处理 %d 字节的I2S数据", bytes_read);
// 点亮LED指示数据处理
// gpio_set_level(LED_PIN, 1);
// vTaskDelay(pdMS_TO_TICKS(5)); // 短暂点亮LED
// gpio_set_level(LED_PIN, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(50 * 1000);
// esp_light_sleep_start();
} else if (ret != ESP_OK && ret != ESP_ERR_TIMEOUT) {
ESP_LOGE(TAG, "I2S读取错误: %s", esp_err_to_name(ret));
}
@ -137,19 +277,16 @@ static void i2s_process_task(void* arg) {
// 处理接收到的数据
ESP_LOGI(TAG, "处理 %d 字节的I2S数据", queue_data.size);
// 点亮LED指示数据处理
gpio_set_level(LED_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(5)); // 短暂点亮LED
gpio_set_level(LED_PIN, 0);
// 在这里可以添加对音频数据的处理逻辑
// 例如:音频分析、滤波、存储等
// 释放数据缓冲区
if (queue_data.buffer) {
free(queue_data.buffer);
}
// if (queue_data.buffer) {
// free(queue_data.buffer);
// }
}
esp_light_sleep_start();
} else {
// 超时未收到信号,可以执行其他操作或简单地继续循环
// 这样可以避免任务无限期阻塞
@ -158,21 +295,59 @@ static void i2s_process_task(void* arg) {
}
}
// I2S事件回调函数 - 当DMA完成数据传输时被调用 (由DMA中断触发)
static bool IRAM_ATTR i2s_evt_callback(i2s_chan_handle_t handle,
i2s_event_data_t* event,
void* user_ctx) {
BaseType_t need_yield = pdFALSE;
if (event && event->size > 0) {
// 复制数据到新分配的缓冲区以便在任务中处理
// void* data_buffer = malloc(event->size);
// if (data_buffer) {
// memcpy(data_buffer, event->data,
// event->size); // 使用 event->data instead of event->buffer
// 创建要发送到队列的事件数据
i2s_queue_data_t queue_event = {.buffer = NULL, .size = event->size};
// 发送数据到队列,供处理任务使用
if (xQueueSendFromISR(i2s_event_queue, &queue_event, &need_yield) !=
pdTRUE) {
// 队列满,丢弃数据并释放缓冲区
// free(data_buffer);
} else {
// 通知数据处理任务
if (data_ready_sem) {
xSemaphoreGiveFromISR(data_ready_sem, &need_yield);
}
// }
}
// ESP_LOGI(TAG, "处理 %d 字节的I2S数据", event->size);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(50 * 1000);
// esp_light_sleep_start();
}
return (need_yield == pdTRUE);
}
// 初始化I2S
static esp_err_t i2s_init(void) {
// 创建I2S事件队列
// i2s_event_queue = xQueueCreate(DMA_BUF_COUNT, sizeof(i2s_queue_data_t));
// if (!i2s_event_queue) {
// ESP_LOGE(TAG, "创建I2S事件队列失败");
// return ESP_FAIL;
// }
i2s_event_queue = xQueueCreate(DMA_BUF_COUNT, sizeof(i2s_queue_data_t));
if (!i2s_event_queue) {
ESP_LOGE(TAG, "创建I2S事件队列失败");
return ESP_FAIL;
}
// 创建数据就绪信号量
// data_ready_sem = xSemaphoreCreateBinary();
// if (!data_ready_sem) {
// ESP_LOGE(TAG, "创建数据就绪信号量失败");
// return ESP_FAIL;
// }
data_ready_sem = xSemaphoreCreateBinary();
if (!data_ready_sem) {
ESP_LOGE(TAG, "创建数据就绪信号量失败");
return ESP_FAIL;
}
// 配置I2S通道
i2s_chan_config_t chan_cfg =
@ -183,28 +358,53 @@ static esp_err_t i2s_init(void) {
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_chan));
// 配置STD模式
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg =
I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_MONO),
.gpio_cfg = {.mclk = I2S_GPIO_UNUSED,
.bclk = I2S_BCK_PIN,
.ws = I2S_WS_PIN,
.dout = I2S_GPIO_UNUSED,
.din = I2S_DATA_PIN,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
}}};
// 定义接收通道为I2S TDM模式 并配置
i2s_tdm_config_t i2s_tdm_rx_conf = {
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT_MASK),
.clk_cfg = {.clk_src = I2S_CLK_SRC_DEFAULT,
.sample_rate_hz = I2S_SAMPLE_RATE,
.mclk_multiple = I2S_MCLK_MULTIPLE},
.gpio_cfg = {.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = -1, // ES7210 only has ADC capability
.din = I2S_DI_IO},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(
rx_chan, &i2s_tdm_rx_conf)); // 初始化I2S通道为TDM模式
// // 配置STD模式
// i2s_std_config_t std_cfg = {
// .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
// .slot_cfg =
// I2S_STD_MSB_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_MONO),
// .gpio_cfg = {.mclk = I2S_GPIO_UNUSED,
// .bclk = I2S_BCK_PIN,
// .ws = I2S_WS_PIN,
// .dout = I2S_GPIO_UNUSED,
// .din = I2S_DATA_PIN,
// .invert_flags = {
// .mclk_inv = false,
// .bclk_inv = false,
// .ws_inv = false,
// }}};
// ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
// 注册事件回调 - 这是DMA中断触发的回调
// i2s_event_callbacks_t callbacks = {
// .on_recv = i2s_evt_callback,
// .on_recv_q_ovf = NULL,
// };
// ESP_ERROR_CHECK(
// i2s_channel_register_event_callback(rx_chan, &callbacks, NULL));
// 启用通道
ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
ESP_LOGI(TAG, "I2S初始化完成使用DMA");
ESP_LOGI(TAG, "I2S初始化完成使用DMA中断");
return ESP_OK;
}
@ -231,9 +431,9 @@ void config_power_manager() {
// 8 18.8
}
// 主函数
void init_msc(sdmmc_card_t* card);
void app_main(void) {
// 初始化NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
@ -242,35 +442,37 @@ void app_main(void) {
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "启动I2S DMA接收示例");
sdmmc_card_t* sdmmc_card = mount_sdcard();
// 初始化GPIO
gpio_init();
config_power_manager();
i2c_init();
// 初始化I2S
if (i2s_init() != ESP_OK) {
ESP_LOGE(TAG, "I2S初始化失败");
return;
}
// 创建I2S接收任务
TaskHandle_t rx_task_handle = NULL;
// i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init();
es7210_codec_init();
ESP_LOGI(TAG, "启动I2S DMA接收示例");
// app_sr_init(); // 语音识别初始化
config_power_manager();
record_start1();
BaseType_t task_ret = xTaskCreate(i2s_rx_task, "i2s_rx_task", 4096, NULL,
tskIDLE_PRIORITY + 2, &rx_task_handle);
// BaseType_t task_ret =
// xTaskCreate(i2s_process_task, "i2s_process_task", 4096, NULL,
// tskIDLE_PRIORITY + 1, &rx_task_handle);
if (task_ret != pdTRUE) {
ESP_LOGE(TAG, "创建I2S接收任务失败");
ESP_LOGE(TAG, "创建I2S数据处理任务失败");
return;
}
// 创建I2S数据处理任务
// task_ret = xTaskCreate(i2s_process_task, "i2s_process_task", 4096, NULL,
// tskIDLE_PRIORITY + 1, &i2s_dma_task_handle);
// if (task_ret != pdTRUE) {
// ESP_LOGE(TAG, "创建I2S数据处理任务失败");
// return;
// }
ESP_LOGI(TAG, "系统启动完成I2S使用DMA处理数据");
// 主循环可以执行其他任务
@ -278,6 +480,13 @@ void app_main(void) {
// // 这里可以执行其他非I2S相关的任务
// vTaskDelay(pdMS_TO_TICKS(1000));
// }
while (!record_end1()) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
init_msc(sdmmc_card);
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, sdmmc_card);
}
// 应用程序结束时的清理函数

View File

@ -1,421 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "app_sr.h"
#include "app_ui.h"
#include "driver/i2c.h"
#include "driver/i2s_std.h"
#include "driver/i2s_tdm.h"
#include "driver/sdmmc_host.h"
#include "es7210.h"
#include "esp32_s3_szp.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_check.h"
#include "esp_netif_sntp.h"
#include "esp_pm.h"
#include "esp_private/esp_clk.h"
#include "esp_sleep.h"
#include "esp_sntp.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "esp_wifi.h"
#include "format_wav.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "sdmmc_cmd.h"
/* I2C port and GPIOs */
#define I2C_NUM (0)
#define I2C_SDA_IO (1)
#define I2C_SCL_IO (2)
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (38)
#define I2S_BCK_IO (14)
#define I2S_WS_IO (13)
#define I2S_DI_IO (12)
/* SD card GPIOs */
#define SD_CMD_IO (48)
#define SD_CLK_IO (47)
#define SD_DAT0_IO (21)
/* I2S configurations */
#define I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S)
#define I2S_CHAN_NUM (2)
#define I2S_SAMPLE_RATE (16000)
#define I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256)
#define I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT)
#define I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1)
/* ES7210 configurations */
#define ES7210_I2C_ADDR (0x41)
#define ES7210_I2C_CLK (100000)
#define ES7210_MIC_GAIN (ES7210_MIC_GAIN_30DB)
#define ES7210_MIC_BIAS (ES7210_MIC_BIAS_2V87)
#define ES7210_ADC_VOLUME (0)
/* SD card & recording configurations */
#define RECORD_TIME_SEC (20)
#define SD_MOUNT_POINT "/sdcard"
static const char* TAG = "main";
static char* record_file_name = NULL;
static i2s_chan_handle_t es7210_i2s_init(void) {
i2s_chan_handle_t i2s_rx_chan = NULL; // 定义接收通道句柄
ESP_LOGI(TAG, "Create I2S receive channel");
i2s_chan_config_t i2s_rx_conf = I2S_CHANNEL_DEFAULT_CONFIG(
I2S_NUM_AUTO, I2S_ROLE_MASTER); // 配置接收通道
ESP_ERROR_CHECK(
i2s_new_channel(&i2s_rx_conf, NULL, &i2s_rx_chan)); // 创建i2s通道
ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode");
// 定义接收通道为I2S TDM模式 并配置
i2s_tdm_config_t i2s_tdm_rx_conf = {
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, I2S_TDM_SLOT_MASK),
.clk_cfg = {.clk_src = I2S_CLK_SRC_DEFAULT,
.sample_rate_hz = I2S_SAMPLE_RATE,
.mclk_multiple = I2S_MCLK_MULTIPLE},
.gpio_cfg = {.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = -1, // ES7210 only has ADC capability
.din = I2S_DI_IO},
};
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(
i2s_rx_chan, &i2s_tdm_rx_conf)); // 初始化I2S通道为TDM模式
return i2s_rx_chan;
}
sdmmc_card_t* mount_sdcard(void) {
sdmmc_card_t* sdmmc_card = NULL;
ESP_LOGI(TAG, "Mounting SD card");
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 8 * 1024};
ESP_LOGI(TAG, "Initializing SD card");
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t sdmmc_host = SDMMC_HOST_DEFAULT(); // SDMMC主机接口配置
sdmmc_slot_config_t slot_config =
SDMMC_SLOT_CONFIG_DEFAULT(); // SDMMC插槽配置
slot_config.width = 1; // 设置为1线SD模式
slot_config.clk = SD_CLK_IO;
slot_config.cmd = SD_CMD_IO;
slot_config.d0 = SD_DAT0_IO;
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // 打开内部上拉电阻
ESP_LOGI(TAG, "Mounting filesystem");
esp_err_t ret;
while (1) {
ret = esp_vfs_fat_sdmmc_mount(SD_MOUNT_POINT, &sdmmc_host, &slot_config,
&mount_config, &sdmmc_card);
if (ret == ESP_OK) {
break;
} else if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). ",
esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(
TAG, "Card size: %lluMB, speed: %dMHz",
(((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >>
20,
sdmmc_card->max_freq_khz / 1000);
return sdmmc_card;
}
static void es7210_codec_init(void) {
// 创建es7210器件句柄
es7210_dev_handle_t es7210_handle = NULL;
es7210_i2c_config_t es7210_i2c_conf = {.i2c_port = I2C_NUM,
.i2c_addr = ES7210_I2C_ADDR};
ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle));
// 初始化es7210芯片
ESP_LOGI(TAG, "Configure ES7210 codec parameters");
es7210_codec_config_t codec_conf = {
.i2s_format = I2S_TDM_FORMAT,
.mclk_ratio = I2S_MCLK_MULTIPLE,
.sample_rate_hz = I2S_SAMPLE_RATE,
.bit_width = (es7210_i2s_bits_t)I2S_SAMPLE_BITS,
.mic_bias = ES7210_MIC_BIAS,
.mic_gain = ES7210_MIC_GAIN,
.flags.tdm_enable = true};
ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, ES7210_ADC_VOLUME));
}
// wifi事件组
static EventGroupHandle_t s_wifi_event_group = NULL;
// wifi事件
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define WIFI_START_BIT BIT2
#define WIFI_GET_SNTP_BIT BIT3
// wifi最大重连次数
#define EXAMPLE_ESP_MAXIMUM_RETRY 3
typedef struct {
char wifi_ssid[32]; // 获取wifi名称
char wifi_password[64]; // 获取wifi密码
char back_flag; // 是否退出
} wifi_account_t;
static void wifi_connect() {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
assert(esp_netif_create_default_wifi_sta());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// ESP_ERROR_CHECK(esp_event_handler_instance_register(
// WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
// ESP_ERROR_CHECK(esp_event_handler_instance_register(
// IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL,
// &instance_got_ip));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
wifi_account_t wifi_account;
strcpy(wifi_account.wifi_ssid, "Liang");
strcpy(wifi_account.wifi_password, "wifi1234");
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", wifi_account.wifi_ssid,
wifi_account.wifi_password);
wifi_account.back_flag = 0; // 正常连接
wifi_config_t wifi_config = {
.sta =
{
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
.sae_h2e_identifier = "",
},
};
strcpy((char*)wifi_config.sta.ssid, wifi_account.wifi_ssid);
strcpy((char*)wifi_config.sta.password, wifi_account.wifi_password);
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
}
time_t now;
struct tm timeinfo;
static void get_time_task() {
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("ntp.aliyun.com");
esp_netif_sntp_init(&config);
// wait for time to be set
int retry = 0;
// const int retry_count = 6;
while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) ==
ESP_ERR_TIMEOUT) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d)", retry++);
}
esp_netif_sntp_deinit();
// 设置时区
setenv("TZ", "CST-8", 1);
tzset();
// 获取系统时间
time(&now);
localtime_r(&now, &timeinfo);
ESP_LOGI(TAG, "%d年%02d月%02d日", timeinfo.tm_year + 1900,
timeinfo.tm_mon + 1, timeinfo.tm_mday);
ESP_LOGI(TAG, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min,
timeinfo.tm_sec);
}
static char* get_file_name_from_time(void) {
get_time_task();
strftime(record_file_name, 48, "/sdcard/Record_%Y%m%d%H%M%S.wav", &timeinfo);
return record_file_name;
}
static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_chan) {
ESP_RETURN_ON_FALSE(i2s_rx_chan, ESP_FAIL, TAG,
"invalid i2s channel handle pointer");
esp_err_t ret = ESP_OK;
uint32_t byte_rate = I2S_SAMPLE_RATE * I2S_CHAN_NUM * I2S_SAMPLE_BITS / 8;
uint32_t wav_size = byte_rate * RECORD_TIME_SEC;
const wav_header_t wav_header = WAV_HEADER_PCM_DEFAULT(
wav_size, I2S_SAMPLE_BITS, I2S_SAMPLE_RATE, I2S_CHAN_NUM);
get_file_name_from_time();
ESP_LOGI(TAG, "Opening file %s", record_file_name);
FILE* f = fopen(record_file_name, "w");
ESP_LOGI(TAG, "fopen error: %s", strerror(errno));
ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file");
ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL,
err, TAG, "error while writing wav header");
/* Start recording */
size_t wav_written = 0;
static int16_t i2s_readraw_buff[4096];
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG,
"error while starting i2s rx channel");
while (wav_written < wav_size) {
if (wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
ESP_LOGI(TAG, "Recording: %" PRIu32 "/%ds", wav_written / byte_rate + 1,
RECORD_TIME_SEC);
}
size_t bytes_read = 0;
/* Read RAW samples from ES7210 */
ESP_GOTO_ON_ERROR(i2s_channel_read(i2s_rx_chan, i2s_readraw_buff,
sizeof(i2s_readraw_buff), &bytes_read,
pdMS_TO_TICKS(1000)),
err, TAG, "error while reading samples from i2s");
/* Write the samples to the WAV file */
ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err,
TAG, "error while writing samples to wav file");
wav_written += bytes_read;
}
err:
i2s_channel_disable(i2s_rx_chan);
ESP_LOGI(TAG, "Recording done! Flushing file buffer");
fclose(f);
return ret;
}
void init_msc(sdmmc_card_t* card);
void config_power_manager() {
// 自动降频至8MHz 双核都空闲时进入 Light Sleep
esp_err_t ret;
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 240, .min_freq_mhz = 8, .light_sleep_enable = true};
ret = esp_pm_configure(&pm_config);
if (ret != ESP_OK) {
ESP_LOGE("PM", "Power management configuration failed (0x%x)", ret);
}
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// light_sleep_off
// deep sleep 5
// 240 44.8
// 160 38.4
// 80 32.9
// 8 18.8
}
void taskOnCore0(void* pvParam) {
while (1) {
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("taskOnCore0 当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(1 * 1000000);
// esp_light_sleep_start();
}
}
void taskOnCore1(void* pvParam) {
while (1) {
uint32_t cpu_freq = esp_clk_cpu_freq();
printf("taskOnCore1 当前CPU频率: %lu MHz\n", cpu_freq / 1000000);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// esp_sleep_enable_timer_wakeup(1 * 1000000);
// esp_light_sleep_start();
}
}
void app_main(void) {
esp_err_t ret;
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);
sdmmc_card_t* sdmmc_card = mount_sdcard();
bsp_i2c_init(); // I2C初始化
// pca9557_init(); // IO扩展芯片初始化
// bsp_lvgl_start(); // 初始化液晶屏lvgl接口
// bsp_spiffs_mount(); // SPIFFS文件系统初始化
bsp_codec_init(); // 音频初始化
// mp3_player_init(); // MP3播放器初始化
// app_sr_init(); // 语音识别初始化
// esp_deep_sleep_start();
// esp_light_sleep_start();
i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init();
es7210_codec_init();
wifi_connect();
// esp_wifi_stop();
// esp_wifi_set_mode(WIFI_MODE_NULL);
// esp_wifi_deinit();
// esp_bt_controller_disable();
// esp_bt_controller_deinit();
// esp_bluedroid_disable();
// esp_bluedroid_deinit();
// esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
// 主动休眠
// esp_deep_sleep_start();
// esp_light_sleep_start();
// config_power_manager();
// xTaskCreatePinnedToCore(taskOnCore0, // 任务函数
// "Core0_Task", // 任务名称
// 4096, // 堆栈大小
// NULL, // 参数
// 1, // 优先级
// NULL, // 任务句柄
// 0 // 绑定到核心0
// );
// xTaskCreatePinnedToCore(taskOnCore1, // 任务函数
// "Core1_Task", // 任务名称
// 4096, // 堆栈大小
// NULL, // 参数
// 1, // 优先级
// NULL, // 任务句柄
// 1 // 绑定到核心0
// );
record_file_name = malloc(64);
for (int i = 0; i < 2; i++) {
esp_err_t err = record_wav(i2s_rx_chan);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Audio success recorded into %s.", record_file_name);
} else {
ESP_LOGE(TAG, "Record failed, %s .", record_file_name);
}
}
init_msc(sdmmc_card);
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, sdmmc_card);
while (1) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}

416
main/usb_msc.c Normal file
View File

@ -0,0 +1,416 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* DESCRIPTION:
* This example contains code to make ESP32 based device recognizable by
* USB-hosts as a USB Mass Storage Device. It either allows the embedded
* application i.e. example to access the partition or Host PC accesses the
* partition over USB MSC. They can't be allowed to access the partition at the
* same time. For different scenarios and behaviour, Refer to README of this
* example.
*/
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include "driver/gpio.h"
#include "esp_check.h"
#include "esp_console.h"
#include "esp_partition.h"
#include "sdkconfig.h"
#include "tinyusb.h"
#include "tinyusb_default_config.h"
#include "tinyusb_msc.h"
/*
* We warn if a secondary serial console is enabled. A secondary serial console
* is always output-only and hence not very useful for interactive console
* applications. If you encounter this warning, consider disabling the secondary
* serial console in menuconfig unless you know what you are doing.
*/
#if SOC_USB_SERIAL_JTAG_SUPPORTED
#if !CONFIG_ESP_CONSOLE_SECONDARY_NONE
#warning \
"A secondary serial console is not useful when using the console component. Please disable it in menuconfig."
#endif
#endif
static const char* TAG = "msc";
static esp_console_repl_t* repl = NULL;
/* Storage global variables */
tinyusb_msc_storage_handle_t storage_hdl = NULL;
tinyusb_msc_mount_point_t mp;
static SemaphoreHandle_t _wait_console_smp = NULL;
/* TinyUSB descriptors
********************************************************************* */
#define EPNUM_MSC 1
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
enum { ITF_NUM_MSC = 0, ITF_NUM_TOTAL };
enum {
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80,
EDPT_MSC_OUT = 0x01,
EDPT_MSC_IN = 0x81,
};
static tusb_desc_device_t descriptor_config = {
.bLength = sizeof(descriptor_config),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x303A, // This is Espressif VID. This needs to be changed
// according to Users / Customers
.idProduct = 0x4002,
.bcdDevice = 0x100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
static uint8_t const msc_fs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
};
#if (TUD_OPT_HIGH_SPEED)
static const tusb_desc_device_qualifier_t device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0};
static uint8_t const msc_hs_configuration_desc[] = {
// Config number, interface count, string index, total length, attribute,
// power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN,
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
};
#endif // TUD_OPT_HIGH_SPEED
static char const* string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials
"Example MSC", // 4. MSC
};
/*********************************************************************** TinyUSB
* descriptors*/
#define BASE_PATH "/sdcard" // base path to mount the partition
#define PROMPT_STR CONFIG_IDF_TARGET
static int console_unmount(int argc, char** argv);
static int console_read(int argc, char** argv);
static int console_write(int argc, char** argv);
static int console_size(int argc, char** argv);
static int console_status(int argc, char** argv);
static int console_exit(int argc, char** argv);
const esp_console_cmd_t cmds[] = {
{
.command = "read",
.help = "read BASE_PATH/README.MD and print its contents",
.hint = NULL,
.func = &console_read,
},
{
.command = "write",
.help = "create file BASE_PATH/README.MD if it does not exist",
.hint = NULL,
.func = &console_write,
},
{
.command = "size",
.help = "show storage size and sector size",
.hint = NULL,
.func = &console_size,
},
{
.command = "expose",
.help = "Expose Storage to Host",
.hint = NULL,
.func = &console_unmount,
},
{
.command = "status",
.help = "Status of storage exposure over USB",
.hint = NULL,
.func = &console_status,
},
{
.command = "exit",
.help = "exit from application",
.hint = NULL,
.func = &console_exit,
}};
// Set mount point to the application and list files in BASE_PATH by filesystem
// API
static void mount(void) {
ESP_LOGI(TAG, "Mount storage...");
ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(
storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP));
// List all the files in this directory
ESP_LOGI(TAG, "\nls command output:");
struct dirent* d;
DIR* dh = opendir(BASE_PATH);
if (!dh) {
if (errno == ENOENT) {
// If the directory is not found
ESP_LOGE(TAG, "Directory doesn't exist %s", BASE_PATH);
} else {
// If the directory is not readable then throw error and exit
ESP_LOGE(TAG, "Unable to read directory %s", BASE_PATH);
}
return;
}
// While the next entry is not readable we will print directory files
while ((d = readdir(dh)) != NULL) {
printf("%s\n", d->d_name);
}
return;
}
// unmount storage
static int console_unmount(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "Storage is already exposed");
return -1;
}
ESP_LOGI(TAG, "Unmount storage...");
ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(
storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB));
return 0;
}
// read BASE_PATH/README.MD and print its contents
static int console_read(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG,
"Storage exposed over USB. Application can't read from storage.");
return -1;
}
ESP_LOGD(TAG, "read from storage:");
const char* filename = BASE_PATH "/README.MD";
FILE* ptr = fopen(filename, "r");
if (ptr == NULL) {
ESP_LOGE(TAG, "Filename not present - %s", filename);
return -1;
}
char buf[1024];
while (fgets(buf, 1000, ptr) != NULL) {
printf("%s", buf);
}
fclose(ptr);
return 0;
}
// create file BASE_PATH/README.MD if it does not exist
static int console_write(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG,
"storage exposed over USB. Application can't write to storage.");
return -1;
}
ESP_LOGD(TAG, "write to storage:");
const char* filename = BASE_PATH "/README.MD";
FILE* fd = fopen(filename, "r");
if (!fd) {
ESP_LOGW(TAG, "README.MD doesn't exist yet, creating");
fd = fopen(filename, "w");
fprintf(fd,
"Mass Storage Devices are one of the most common USB devices. It "
"use Mass Storage Class (MSC) that allow access to their internal "
"data storage.\n");
fprintf(fd,
"In this example, ESP chip will be recognised by host (PC) as Mass "
"Storage Device.\n");
fprintf(fd,
"Upon connection to USB host (PC), the example application will "
"initialize the storage module and then the storage will be seen "
"as removable device on PC.\n");
fclose(fd);
}
return 0;
}
// Show storage size and sector size
static int console_size(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage");
return -1;
}
uint32_t sec_count;
uint32_t sec_size;
ESP_ERROR_CHECK(tinyusb_msc_get_storage_sector_size(storage_hdl, &sec_size));
ESP_ERROR_CHECK(tinyusb_msc_get_storage_capacity(storage_hdl, &sec_count));
// Calculate size in MB or KB
uint64_t total_bytes = (uint64_t)sec_size * sec_count;
if (total_bytes >= (1024 * 1024)) {
uint64_t total_mb = total_bytes / (1024 * 1024);
printf("Storage Capacity %lluMB\n", total_mb);
} else {
uint64_t total_kb = total_bytes / 1024;
printf("Storage Capacity %lluKB\n", total_kb);
}
return 0;
}
// Show storage status
static int console_status(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
printf("storage exposed over USB: %s\n",
(mp == TINYUSB_MSC_STORAGE_MOUNT_USB) ? "Yes" : "No");
return 0;
}
// Exit from application
static int console_exit(int argc, char** argv) {
ESP_ERROR_CHECK(tinyusb_msc_delete_storage(storage_hdl));
ESP_ERROR_CHECK(tinyusb_driver_uninstall());
xSemaphoreGive(_wait_console_smp);
printf("Application Exit\n");
return 0;
}
/**
* @brief Storage mount changed callback
*
* @param handle Storage handle
* @param event Event information
* @param arg User argument, provided during callback registration
*/
static void storage_mount_changed_cb(tinyusb_msc_storage_handle_t handle,
tinyusb_msc_event_t* event, void* arg) {
switch (event->id) {
case TINYUSB_MSC_EVENT_MOUNT_START:
// Verify that all the files are closed before unmounting
break;
case TINYUSB_MSC_EVENT_MOUNT_COMPLETE:
ESP_LOGI(
TAG, "Storage mounted to application: %s",
(event->mount_point == TINYUSB_MSC_STORAGE_MOUNT_APP) ? "Yes" : "No");
break;
case TINYUSB_MSC_EVENT_MOUNT_FAILED:
case TINYUSB_MSC_EVENT_FORMAT_REQUIRED:
ESP_LOGE(TAG, "Storage mount failed or format required");
break;
default:
break;
}
}
void init_msc(sdmmc_card_t* card) {
ESP_LOGI(TAG, "Initializing storage...");
_wait_console_smp = xSemaphoreCreateBinary();
if (_wait_console_smp == NULL) {
ESP_LOGE(TAG, "Failed to create semaphore");
return;
}
// Initial mount point to APP
tinyusb_msc_storage_config_t storage_cfg = {
.mount_point = TINYUSB_MSC_STORAGE_MOUNT_USB,
.fat_fs =
{
.base_path = NULL, // Use default base path
.config.max_files = 5, // Maximum number of files that can be
// opened simultaneously
.format_flags = 0, // No special format flags
},
};
storage_cfg.medium.card = card;
ESP_ERROR_CHECK(tinyusb_msc_new_storage_sdmmc(&storage_cfg, &storage_hdl));
ESP_ERROR_CHECK(
tinyusb_msc_set_storage_callback(storage_mount_changed_cb, NULL));
// Re-mount to the APP
mount();
ESP_LOGI(TAG, "USB MSC initialization");
tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
tusb_cfg.descriptor.device = &descriptor_config;
tusb_cfg.descriptor.full_speed_config = msc_fs_configuration_desc;
tusb_cfg.descriptor.string = string_desc_arr;
tusb_cfg.descriptor.string_count =
sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
#if (TUD_OPT_HIGH_SPEED)
tusb_cfg.descriptor.high_speed_config = msc_hs_configuration_desc;
tusb_cfg.descriptor.qualifier = &device_qualifier;
#endif // TUD_OPT_HIGH_SPEED
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
ESP_LOGI(TAG, "USB MSC initialization DONE");
ESP_ERROR_CHECK(tinyusb_msc_get_storage_mount_point(storage_hdl, &mp));
if (mp == TINYUSB_MSC_STORAGE_MOUNT_USB) {
ESP_LOGE(TAG, "storage exposed over USB. Application can't access.");
}
#if 1
// Init console based on menuconfig settings
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = 64;
esp_console_dev_uart_config_t hw_config =
ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
for (int count = 0; count < sizeof(cmds) / sizeof(esp_console_cmd_t);
count++) {
ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[count]));
}
ESP_ERROR_CHECK(esp_console_start_repl(repl));
xSemaphoreTake(_wait_console_smp, portMAX_DELAY);
ESP_ERROR_CHECK(esp_console_stop_repl(repl));
vSemaphoreDelete(_wait_console_smp);
#endif
}