Add asr support.
This commit is contained in:
parent
60f1d97871
commit
11186066c7
|
|
@ -0,0 +1,491 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_sntp.h"
|
||||
#include "esp_tls.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "netdb.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "time.h"
|
||||
|
||||
/* 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"
|
||||
|
||||
// WiFi配置
|
||||
#define WIFI_SSID "Liang"
|
||||
#define WIFI_PASS "wifi1234"
|
||||
|
||||
// API配置
|
||||
#define API_URL "https://api.siliconflow.cn/v1/audio/transcriptions"
|
||||
#define BEARER_TOKEN "sk-qxdrdtbbzlrfkiwyorkjvtnzxtqrfbcjqwnuaffpgauuvswu"
|
||||
#define MODEL_NAME "FunAudioLLM/SenseVoiceSmall"
|
||||
|
||||
// SD卡配置
|
||||
#define MOUNT_POINT "/sdcard"
|
||||
#define AUDIO_FILE_NAME "demo.wav"
|
||||
#define AUDIO_FILE "/sdcard/demo.wav"
|
||||
|
||||
static const char* TAG = "AUDIO_TRANS";
|
||||
|
||||
// 边界字符串
|
||||
static const char* BOUNDARY =
|
||||
"------------------------735323031399963166993862150";
|
||||
static const char* CONTENT_TYPE =
|
||||
"multipart/form-data; "
|
||||
"boundary=------------------------735323031399963166993862150";
|
||||
|
||||
// WiFi事件处理
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
const int WIFI_CONNECTED_BIT = BIT0;
|
||||
|
||||
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data) {
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT &&
|
||||
event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "WiFi disconnected, reconnecting...");
|
||||
esp_wifi_connect();
|
||||
xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
|
||||
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_init_sta(void) {
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
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_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
&wifi_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||
&wifi_event_handler, NULL));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta =
|
||||
{
|
||||
.ssid = WIFI_SSID,
|
||||
.password = WIFI_PASS,
|
||||
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "WiFi initialization complete");
|
||||
}
|
||||
|
||||
esp_err_t init_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 ESP_OK;
|
||||
}
|
||||
// 读取文件到缓冲区
|
||||
static esp_err_t read_file_to_buffer(const char* filename, uint8_t** buffer,
|
||||
size_t* size) {
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file: %s", filename);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
*size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
*buffer = malloc(*size);
|
||||
if (*buffer == NULL) {
|
||||
fclose(f);
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
size_t read_size = fread(*buffer, 1, *size, f);
|
||||
fclose(f);
|
||||
|
||||
if (read_size != *size) {
|
||||
free(*buffer);
|
||||
ESP_LOGE(TAG, "Failed to read file: read %zu bytes, expected %zu",
|
||||
read_size, *size);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Read file %s, size: %zu bytes", filename, *size);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// HTTP事件处理器
|
||||
esp_err_t _http_event_handler(esp_http_client_event_t* evt) {
|
||||
switch (evt->event_id) {
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
if (!esp_http_client_is_chunked_response(evt->client)) {
|
||||
ESP_LOGI(TAG, "HTTP Response: %.*s", evt->data_len, (char*)evt->data);
|
||||
}
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
ESP_LOGI(TAG, "HTTP request finished");
|
||||
break;
|
||||
case HTTP_EVENT_ERROR:
|
||||
ESP_LOGE(TAG, "HTTP request error");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// 构建multipart请求体
|
||||
static char* build_multipart_body(const uint8_t* file_data, size_t file_size,
|
||||
const char* filename, size_t* body_len) {
|
||||
char* model_part = NULL;
|
||||
char* file_part = NULL;
|
||||
char* body = NULL;
|
||||
|
||||
// 构建model部分
|
||||
asprintf(&model_part,
|
||||
"--%s\r\n"
|
||||
"Content-Disposition: form-data; name=\"model\"\r\n"
|
||||
"\r\n"
|
||||
"%s\r\n",
|
||||
BOUNDARY, MODEL_NAME);
|
||||
|
||||
// 构建file部分头部
|
||||
asprintf(&file_part,
|
||||
"--%s\r\n"
|
||||
"Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"
|
||||
"Content-Type: application/octet-stream\r\n"
|
||||
"\r\n",
|
||||
BOUNDARY, filename);
|
||||
|
||||
// 构建结束边界
|
||||
char* end_boundary = NULL;
|
||||
asprintf(&end_boundary, "\r\n--%s--\r\n", BOUNDARY);
|
||||
|
||||
// 计算总长度
|
||||
*body_len =
|
||||
strlen(model_part) + strlen(file_part) + file_size + strlen(end_boundary);
|
||||
|
||||
// 分配内存并构建完整body
|
||||
body = malloc(*body_len);
|
||||
if (!body) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for request body");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char* ptr = body;
|
||||
|
||||
// 复制model部分
|
||||
memcpy(ptr, model_part, strlen(model_part));
|
||||
ptr += strlen(model_part);
|
||||
|
||||
// 复制file部分头部
|
||||
memcpy(ptr, file_part, strlen(file_part));
|
||||
ptr += strlen(file_part);
|
||||
|
||||
// 复制文件数据
|
||||
memcpy(ptr, file_data, file_size);
|
||||
ptr += file_size;
|
||||
|
||||
// 复制结束边界
|
||||
memcpy(ptr, end_boundary, strlen(end_boundary));
|
||||
|
||||
cleanup:
|
||||
free(model_part);
|
||||
free(file_part);
|
||||
free(end_boundary);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
// 发送音频转录请求
|
||||
static void send_audio_transcription(const char* filepath) {
|
||||
uint8_t* file_data = NULL;
|
||||
size_t file_size = 0;
|
||||
|
||||
// 读取音频文件
|
||||
ESP_LOGI(TAG, "Reading audio file: %s", filepath);
|
||||
if (read_file_to_buffer(filepath, &file_data, &file_size) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read audio file");
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建multipart请求体
|
||||
size_t body_len = 0;
|
||||
char* body =
|
||||
build_multipart_body(file_data, file_size, AUDIO_FILE_NAME, &body_len);
|
||||
if (!body) {
|
||||
ESP_LOGE(TAG, "Failed to build request body");
|
||||
free(file_data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 配置HTTP客户端
|
||||
esp_http_client_config_t config = {
|
||||
.url = API_URL,
|
||||
.method = HTTP_METHOD_POST,
|
||||
.event_handler = _http_event_handler,
|
||||
.buffer_size = 4096,
|
||||
.buffer_size_tx = 4096,
|
||||
.timeout_ms = 60000, // Increased timeout to 60 seconds
|
||||
.skip_cert_common_name_check = false, // Don't skip cert name check
|
||||
.keep_alive_enable = true,
|
||||
.use_global_ca_store = true,
|
||||
.crt_bundle_attach =
|
||||
esp_crt_bundle_attach, // Use certificate bundle if available
|
||||
};
|
||||
|
||||
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||
|
||||
// 设置请求头
|
||||
char auth_header[128];
|
||||
snprintf(auth_header, sizeof(auth_header), "Bearer %s", BEARER_TOKEN);
|
||||
|
||||
esp_http_client_set_header(client, "Authorization", auth_header);
|
||||
esp_http_client_set_header(client, "Content-Type", CONTENT_TYPE);
|
||||
esp_http_client_set_header(client, "Accept", "application/json");
|
||||
esp_http_client_set_header(client, "Content-Length", "");
|
||||
|
||||
// 设置POST数据
|
||||
esp_http_client_set_post_field(client, body, body_len);
|
||||
|
||||
// 执行请求
|
||||
ESP_LOGI(TAG, "Sending request to %s", API_URL);
|
||||
esp_err_t err = esp_http_client_perform(client);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
int status_code = esp_http_client_get_status_code(client);
|
||||
ESP_LOGI(TAG, "HTTP Status: %d", status_code);
|
||||
|
||||
if (status_code == 200) {
|
||||
ESP_LOGI(TAG, "Request successful");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Request failed with status: %d", status_code);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
// 清理
|
||||
esp_http_client_cleanup(client);
|
||||
free(body);
|
||||
free(file_data);
|
||||
}
|
||||
|
||||
// 创建一个简单的测试,先确认网络和证书工作
|
||||
static void debug_network_test(void) {
|
||||
ESP_LOGI(TAG, "Starting network debug test...");
|
||||
|
||||
// 1. 测试DNS解析
|
||||
struct hostent* he = gethostbyname("api.siliconflow.cn");
|
||||
if (he == NULL) {
|
||||
ESP_LOGE(TAG, "DNS resolution failed");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "DNS resolved successfully");
|
||||
}
|
||||
|
||||
// 2. 测试到服务器的TCP连接
|
||||
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Failed to create socket");
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_in dest_addr;
|
||||
dest_addr.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS
|
||||
dest_addr.sin_family = AF_INET;
|
||||
dest_addr.sin_port = htons(53);
|
||||
|
||||
if (connect(sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) != 0) {
|
||||
ESP_LOGE(TAG, "Socket connection failed");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Socket connection successful");
|
||||
close(sock);
|
||||
}
|
||||
}
|
||||
|
||||
// 根CA证书(api.siliconflow.cn的根CA,可直接复制使用)
|
||||
const char* rootCACertificate = R"EOF(
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
|
||||
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
|
||||
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
|
||||
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
|
||||
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
|
||||
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
|
||||
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
|
||||
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
|
||||
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
|
||||
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
|
||||
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
|
||||
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
|
||||
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
|
||||
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||
-----END CERTIFICATE-----
|
||||
)EOF";
|
||||
|
||||
void initTime(void) {
|
||||
// 1. 初始化SNTP
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
|
||||
// 使用多个NTP服务器以提高成功率
|
||||
esp_sntp_setservername(0, "ntp.aliyun.com");
|
||||
esp_sntp_setservername(1, "cn.ntp.org.cn");
|
||||
esp_sntp_setservername(2, "pool.ntp.org");
|
||||
|
||||
// 东八区时区配置(GMT+8)
|
||||
setenv("TZ", "CST-8", 1);
|
||||
tzset();
|
||||
esp_sntp_init();
|
||||
|
||||
// 2. 等待时间同步完成(最多等待15秒)
|
||||
int retry = 0;
|
||||
const int max_retry = 15;
|
||||
while (esp_sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET &&
|
||||
retry < max_retry) {
|
||||
ESP_LOGI(TAG, "等待NTP时间同步...(第%d次重试)", retry + 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // ESP-IDF的延时函数(替代Arduino的delay)
|
||||
retry++;
|
||||
}
|
||||
|
||||
time_t now = 0;
|
||||
struct tm timeinfo = {0};
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo); // 转换为本地时间(东八区)
|
||||
|
||||
ESP_LOGI(TAG, "NTP同步成功!当前时间:%04d-%02d-%02d %02d:%02d:%02d",
|
||||
timeinfo.tm_year + 1900, // 年份从1900开始计算
|
||||
timeinfo.tm_mon + 1, // 月份从0开始计算
|
||||
timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min,
|
||||
timeinfo.tm_sec);
|
||||
}
|
||||
|
||||
void sendHttpsRequest(const uint8_t* audioData, size_t audioSize) {
|
||||
// This function is not used in ESP-IDF context, so we'll leave it as a
|
||||
// placeholder The actual HTTPS request is handled by send_audio_transcription
|
||||
// function
|
||||
ESP_LOGI(TAG, "sendHttpsRequest is not used in ESP-IDF implementation");
|
||||
}
|
||||
|
||||
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) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
// 初始化全局CA证书存储
|
||||
ESP_ERROR_CHECK(esp_tls_init_global_ca_store());
|
||||
|
||||
// 初始化WiFi
|
||||
ESP_LOGI(TAG, "Initializing WiFi...");
|
||||
wifi_init_sta();
|
||||
|
||||
// 等待WiFi连接
|
||||
ESP_LOGI(TAG, "Waiting for WiFi connection...");
|
||||
xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, false, true,
|
||||
portMAX_DELAY);
|
||||
|
||||
debug_network_test();
|
||||
initTime();
|
||||
// 初始化SD卡
|
||||
ESP_LOGI(TAG, "Initializing SD card...");
|
||||
if (init_sdcard() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SD card initialization failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
struct stat st;
|
||||
if (stat(AUDIO_FILE, &st) != 0) {
|
||||
ESP_LOGE(TAG, "Audio file not found: %s", AUDIO_FILE);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Audio file found, size: %ld bytes", st.st_size);
|
||||
|
||||
// 发送请求
|
||||
send_audio_transcription(AUDIO_FILE);
|
||||
|
||||
// 卸载SD卡
|
||||
esp_vfs_fat_sdcard_unmount(MOUNT_POINT, NULL);
|
||||
ESP_LOGI(TAG, "SD card unmounted");
|
||||
}
|
||||
Loading…
Reference in New Issue