feat(plip): stage files onto SD via /game/file
CI / platformio (push) Failing after 10m32s
CI / platformio (pull_request) Failing after 11m45s

/game/file only wrote SPIFFS (960 KB, too small for a voice pack). A
path prefixed sd/ (or /sdcard/) now routes to the microSD: the card is
mounted on demand (audio_ensure_sd, independent of hook state) and the
parent dir is created. MAX_BODY 256->512 KB so full say() hints fit
(streamed in 2 KB chunks, RAM-safe). Enable FATFS LFN_HEAP so >8.3
filenames work (greet_0142738200.wav etc). Validated: 40-sample FR
voice pack staged to /sdcard/voice on the bench PLIP (960 MB card).
This commit was merged in pull request #33.
This commit is contained in:
clement
2026-06-18 17:41:53 +02:00
parent ace740a629
commit be369fa260
4 changed files with 50 additions and 7 deletions
+6
View File
@@ -137,6 +137,12 @@ static void ensure_sd_mounted(void)
}
}
bool audio_ensure_sd(void)
{
ensure_sd_mounted();
return s_sd_mounted;
}
/* ── WAV parser ──────────────────────────────────────────────────────────── */
static esp_err_t parse_wav_header(const uint8_t *buf, size_t len, wav_info_t *out)
+6
View File
@@ -39,6 +39,12 @@ esp_err_t audio_play_async(const char *path);
/* Stop current playback immediately. */
esp_err_t audio_stop(void);
/* Mount the microSD card at /sdcard on demand (best-effort, idempotent) and
* report whether it is mounted. Unlike playback, this does NOT depend on the
* hook state — it lets the REST layer stage a voice pack onto the SD while the
* handset is down. Returns true if /sdcard is usable. */
bool audio_ensure_sd(void);
/* True while the audio worker is playing a clip (tone/WAV). The conversation
* LISTEN loop polls this to stay half-duplex: never capture while playing
* (avoids the earpiece→mic feedback that saturated the line). */
+34 -7
View File
@@ -22,6 +22,7 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include "audio.h"
#include "tones.h"
@@ -53,7 +54,8 @@
#define SPIFFS_LABEL "storage"
#define SPIFFS_BASE "/spiffs"
#define MAX_BODY (256 * 1024)
#define MAX_BODY (512 * 1024) /* streamed in 2 KB chunks → RAM-safe; raised
so full-length say() hint WAVs fit on SD */
static volatile bool s_connected = false;
static httpd_handle_t s_httpd = NULL;
@@ -209,14 +211,39 @@ static esp_err_t handle_file_post(httpd_req_t *req)
if (req->content_len <= 0 || req->content_len > MAX_BODY)
return send_json(req, "413 Payload Too Large", "{\"error\":\"too large\"}");
ensure_spiffs();
/* Route to the microSD when the path is sd-prefixed ("sd/…", "sdcard/…"
* or "/sdcard/…"); otherwise stage in SPIFFS (default). The SD is mounted
* on demand here so a voice pack can be staged without an off-hook play. */
const char *base;
const char *rel = fpath;
bool to_sd = false;
if (strncmp(fpath, "sd/", 3) == 0) { rel = fpath + 3; to_sd = true; }
else if (strncmp(fpath, "sdcard/", 7) == 0) { rel = fpath + 7; to_sd = true; }
else if (strncmp(fpath, "/sdcard/", 8) == 0) { rel = fpath + 8; to_sd = true; }
/* Build full SPIFFS path. */
char full[160];
if (fpath[0] == '/') {
snprintf(full, sizeof(full), "%s%s", SPIFFS_BASE, fpath);
if (to_sd) {
if (!audio_ensure_sd())
return send_json(req, "503 Service Unavailable", "{\"error\":\"no sd card\"}");
base = PLIP_SD_MOUNT; /* "/sdcard" */
} else {
snprintf(full, sizeof(full), "%s/%s", SPIFFS_BASE, fpath);
ensure_spiffs();
base = SPIFFS_BASE;
}
while (*rel == '/') rel++; /* avoid a double slash when joining */
char full[192];
snprintf(full, sizeof(full), "%s/%s", base, rel);
/* Create the immediate parent dir (one level) so a pack can live in a
* subfolder like /sdcard/voice/<file>.wav — fopen won't create it. FAT
* supports mkdir; SPIFFS is flat so we only do this for the SD path. */
if (to_sd) {
char *slash = strrchr(full, '/');
if (slash && slash != full) {
*slash = '\0';
mkdir(full, 0775); /* best-effort; ignore EEXIST */
*slash = '/';
}
}
FILE *f = fopen(full, "wb");
+4
View File
@@ -52,3 +52,7 @@ CONFIG_PLIP_HOOK_ACTIVE_HIGH=y
# Example: CONFIG_PLIP_GATEWAY_URL="http://192.168.0.175:8401"
# CONFIG_PLIP_GATEWAY_TOKEN="testtoken"
# Default (Kconfig): http://192.168.0.50:8401 with empty token.
# Long file names on the SD (voice sample pack uses >8.3 names)
CONFIG_FATFS_LFN_HEAP=y
CONFIG_FATFS_MAX_LFN=255