feat(plip): bump /game/file MAX_BODY 64K->256K for full TTS phrases
CI / platformio (pull_request) Failing after 3m46s

This commit is contained in:
2026-06-14 20:29:25 +02:00
parent b748c2d75f
commit f7104b4180
2 changed files with 81 additions and 30 deletions
+55 -15
View File
@@ -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
View File
@@ -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),