feat(plip): bump /game/file MAX_BODY 64K->256K for full TTS phrases
CI / platformio (pull_request) Failing after 3m46s
CI / platformio (pull_request) Failing after 3m46s
This commit is contained in:
+55
-15
@@ -204,9 +204,13 @@ static void play_wav_buf(const uint8_t *buf, size_t len)
|
||||
stream_pcm(buf + wi.data_offset, wi.data_size);
|
||||
}
|
||||
|
||||
/* Play WAV from filesystem (SPIFFS or SD). Falls back to tone on error.
|
||||
* Paths starting with /spiffs/ are read directly from SPIFFS; all other
|
||||
* paths (relative or /sdcard/…) require the SD card to be mounted first. */
|
||||
/* WAV streaming chunk size — keeps heap usage well under 8 KB. */
|
||||
#define PLAY_CHUNK_BYTES 4096
|
||||
|
||||
/* Play WAV from filesystem (SPIFFS or SD) using streaming I/O.
|
||||
* Only the WAV header (≤512 bytes) is buffered; PCM is piped directly
|
||||
* to I2S in PLAY_CHUNK_BYTES slices, avoiding any large heap allocation.
|
||||
* Paths starting with /spiffs/ are read from SPIFFS; all others use SD. */
|
||||
static void play_wav_file(const char *path)
|
||||
{
|
||||
bool is_spiffs = (strncmp(path, "/spiffs/", 8) == 0 || strncmp(path, "/spiffs", 7) == 0);
|
||||
@@ -239,27 +243,63 @@ static void play_wav_file(const char *path)
|
||||
audio_play_tone(880.0f, 200);
|
||||
return;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (fsize < WAV_MIN_HEADER || fsize > 512 * 1024) {
|
||||
ESP_LOGW(TAG, "file size %ld out of range", fsize);
|
||||
/* Read enough of the file to parse the WAV header (up to 512 bytes). */
|
||||
uint8_t hdr_buf[512];
|
||||
size_t hdr_read = fread(hdr_buf, 1, sizeof(hdr_buf), f);
|
||||
if (hdr_read < WAV_MIN_HEADER) {
|
||||
ESP_LOGW(TAG, "WAV too short: %zu bytes", hdr_read);
|
||||
fclose(f);
|
||||
audio_play_tone(880.0f, 200);
|
||||
return;
|
||||
}
|
||||
uint8_t *buf = malloc((size_t)fsize);
|
||||
if (!buf) {
|
||||
ESP_LOGW(TAG, "OOM for %ld bytes", fsize);
|
||||
|
||||
wav_info_t wi = {0};
|
||||
if (parse_wav_header(hdr_buf, hdr_read, &wi) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "WAV parse failed: %s", full);
|
||||
fclose(f);
|
||||
audio_play_tone(880.0f, 200);
|
||||
return;
|
||||
}
|
||||
if (wi.bits_per_sample != 16) {
|
||||
ESP_LOGW(TAG, "WAV: %d-bit not supported (need 16-bit)", wi.bits_per_sample);
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
size_t nread = fread(buf, 1, (size_t)fsize, f);
|
||||
fclose(f);
|
||||
|
||||
play_wav_buf(buf, nread);
|
||||
free(buf);
|
||||
ESP_LOGI(TAG, "WAV: %"PRIu32" Hz %d-bit %d ch, %"PRIu32" bytes PCM",
|
||||
wi.sample_rate, wi.bits_per_sample, wi.channels, wi.data_size);
|
||||
|
||||
/* Seek to the PCM data section. */
|
||||
if (fseek(f, (long)wi.data_offset, SEEK_SET) != 0) {
|
||||
ESP_LOGW(TAG, "fseek to data failed");
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stream PCM to I2S in small chunks — no large malloc needed. */
|
||||
static uint8_t s_play_chunk[PLAY_CHUNK_BYTES]; /* static: avoids stack pressure */
|
||||
uint32_t remaining = wi.data_size;
|
||||
size_t total_written = 0;
|
||||
|
||||
while (!s_stop_req && remaining > 0) {
|
||||
uint32_t to_read = (remaining < PLAY_CHUNK_BYTES) ? remaining : PLAY_CHUNK_BYTES;
|
||||
size_t n = fread(s_play_chunk, 1, to_read, f);
|
||||
if (n == 0) break;
|
||||
size_t i2s_written = 0;
|
||||
esp_err_t ret = i2s_channel_write(s_spk_handle, s_play_chunk, n,
|
||||
&i2s_written, pdMS_TO_TICKS(500));
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "I2S write error: %s", esp_err_to_name(ret));
|
||||
break;
|
||||
}
|
||||
total_written += i2s_written;
|
||||
remaining -= (uint32_t)n;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
float dur = (float)total_written / (float)(wi.sample_rate * wi.channels * (wi.bits_per_sample / 8));
|
||||
ESP_LOGI(TAG, "play done: %zu bytes written, %.2fs", total_written, dur);
|
||||
}
|
||||
|
||||
/* Generate a simple embedded 3-tone cue C5-E5-G5 as proof-of-life. */
|
||||
|
||||
+26
-15
@@ -47,7 +47,7 @@
|
||||
|
||||
#define SPIFFS_LABEL "storage"
|
||||
#define SPIFFS_BASE "/spiffs"
|
||||
#define MAX_BODY (64 * 1024)
|
||||
#define MAX_BODY (256 * 1024)
|
||||
|
||||
static volatile bool s_connected = false;
|
||||
static httpd_handle_t s_httpd = NULL;
|
||||
@@ -176,6 +176,9 @@ static esp_err_t handle_scenario_post(httpd_req_t *req)
|
||||
|
||||
/* ── POST /game/file?path=... (Phase B proof: write file to SPIFFS) ─────── */
|
||||
|
||||
/* Streaming chunk size for /game/file — keeps heap usage under 4 KB. */
|
||||
#define FILE_RECV_CHUNK 2048
|
||||
|
||||
static esp_err_t handle_file_post(httpd_req_t *req)
|
||||
{
|
||||
/* Extract ?path= query parameter */
|
||||
@@ -191,17 +194,7 @@ static esp_err_t handle_file_post(httpd_req_t *req)
|
||||
|
||||
ensure_spiffs();
|
||||
|
||||
char *body = malloc((size_t)req->content_len + 1);
|
||||
if (!body) return send_json(req, "500 Internal Server Error", "{\"error\":\"oom\"}");
|
||||
|
||||
int total = 0;
|
||||
while (total < (int)req->content_len) {
|
||||
int got = httpd_req_recv(req, body + total, req->content_len - total);
|
||||
if (got <= 0) { free(body); return ESP_FAIL; }
|
||||
total += got;
|
||||
}
|
||||
|
||||
/* Write to SPIFFS under /spiffs prefix. */
|
||||
/* Build full SPIFFS path. */
|
||||
char full[160];
|
||||
if (fpath[0] == '/') {
|
||||
snprintf(full, sizeof(full), "%s%s", SPIFFS_BASE, fpath);
|
||||
@@ -212,12 +205,30 @@ static esp_err_t handle_file_post(httpd_req_t *req)
|
||||
FILE *f = fopen(full, "wb");
|
||||
if (!f) {
|
||||
ESP_LOGW(TAG, "fopen %s failed: errno=%d", full, errno);
|
||||
free(body);
|
||||
return send_json(req, "500 Internal Server Error", "{\"error\":\"write failed\"}");
|
||||
}
|
||||
size_t written = fwrite(body, 1, (size_t)total, f);
|
||||
|
||||
/* Stream body to file using a small stack buffer — no large malloc needed. */
|
||||
char chunk[FILE_RECV_CHUNK];
|
||||
int remaining = (int)req->content_len;
|
||||
size_t written = 0;
|
||||
while (remaining > 0) {
|
||||
int to_recv = (remaining < FILE_RECV_CHUNK) ? remaining : FILE_RECV_CHUNK;
|
||||
int got = httpd_req_recv(req, chunk, to_recv);
|
||||
if (got <= 0) {
|
||||
fclose(f);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
size_t fw = fwrite(chunk, 1, (size_t)got, f);
|
||||
if ((int)fw != got) {
|
||||
ESP_LOGW(TAG, "fwrite short: %zu/%d (disk full?)", fw, got);
|
||||
fclose(f);
|
||||
return send_json(req, "500 Internal Server Error", "{\"error\":\"write failed\"}");
|
||||
}
|
||||
written += fw;
|
||||
remaining -= got;
|
||||
}
|
||||
fclose(f);
|
||||
free(body);
|
||||
|
||||
char resp[256];
|
||||
snprintf(resp, sizeof(resp),
|
||||
|
||||
Reference in New Issue
Block a user