fixed most upload issues, updated schematic

This commit is contained in:
zyphlar
2026-07-04 03:28:14 -07:00
parent 12140b7ff6
commit 8cba579780
10 changed files with 1305 additions and 1020 deletions
+17 -29
View File
@@ -7,9 +7,9 @@ Usage:
Supported formats: .wav, .mp3, .ogg, .flac, .aac, .m4a, .raw (anything ffmpeg handles)
Output format: 16-bit signed PCM WAV, 16 kHz, mono (WAV header included).
Output format: 8-bit unsigned PCM WAV, 8 kHz, mono (WAV header included).
- Already-conformant WAV files (PCM, mono, 8 or 16-bit, 8/16/32 kHz) are sent as-is.
- All other formats are converted via ffmpeg to 16-bit 16 kHz mono WAV.
- All other formats are converted via ffmpeg to 8-bit 8 kHz mono WAV.
- .raw files are treated as legacy 8-bit unsigned 16 kHz and wrapped in a WAV header.
Requires: pyserial (pip install pyserial)
@@ -24,7 +24,7 @@ import subprocess
import serial
import time
CHUNK_SIZE = 512
CHUNK_SIZE = 4096
BAUD_RATE = 115200
TIMEOUT_S = 10.0
@@ -92,10 +92,10 @@ def load_wav(path: str) -> bytes:
f" ffmpeg -i \"{path}\" -ar 16000 -ac 1 -acodec pcm_s16le out.wav"
)
print(f"Converting {os.path.basename(path)} via ffmpeg -> 16-bit 16 kHz mono WAV...")
print(f"Converting {os.path.basename(path)} via ffmpeg -> 8-bit 8 kHz mono WAV...")
result = subprocess.run(
['ffmpeg', '-y', '-i', path,
'-ar', '16000', '-ac', '1', '-f', 'wav', '-acodec', 'pcm_s16le', 'pipe:1'],
'-ar', '8000', '-ac', '1', '-f', 'wav', '-acodec', 'pcm_u8', 'pipe:1'],
capture_output=True
)
if result.returncode != 0:
@@ -141,41 +141,29 @@ def upload(port: str, track_num: int, wav: bytes) -> None:
if line:
print(f" firmware: {line}")
# Stream WAV data in chunks; poll for ERR
# Stream WAV data without per-chunk polling — the firmware only ever sends
# ERR mid-transfer on a flash write failure (very rare); polling after every
# chunk adds 50 ms of readline timeout overhead per packet, which for a large
# file at USB CDC speeds amounts to many minutes of wasted time.
sent = 0
err_line = None
ser.timeout = 0.05
ser.timeout = 0 # non-blocking reads while sending
while sent < total:
chunk = wav[sent:sent + CHUNK_SIZE]
ser.write(chunk)
sent += len(chunk)
pct = sent * 100 // total
print(f"\r {sent}/{total} bytes ({pct}%) ", end="", flush=True)
line = ser.readline().decode(errors="replace").strip()
# Drain any early ERR the firmware might have sent
line = ser.read(ser.in_waiting).decode(errors="replace").strip()
if line.startswith("ERR"):
err_line = line
break
print()
print(f"ERROR from firmware: {line}")
sys.exit(1)
print()
ser.timeout = 2.0
if err_line:
print(f"ERROR from firmware: {err_line}")
if "at=" in err_line:
accepted = int(err_line.split("at=")[1].split()[0])
safe = int(accepted * 0.9)
# estimate audio bytes (subtract header)
audio_accepted = max(0, safe - 44)
bits = struct.unpack_from('<H', wav, 34)[0] if len(wav) >= 44 else 16
rate = struct.unpack_from('<I', wav, 24)[0] if len(wav) >= 44 else 16000
max_s = audio_accepted / (bits // 8) / rate
print(f" LittleFS accepted {accepted} bytes before full.")
print(f" Safe clip length: ~{max_s:.1f}s")
print(f" Trim with: ffmpeg -i input.mp3 -t {max_s:.0f} -ar 16000 -ac 1 "
f"-acodec pcm_s16le out.wav")
sys.exit(1)
# Wait for OK
deadline = time.time() + TIMEOUT_S
# Wait for OK — allow up to 60 s for the final sector erase + flash writes
deadline = time.time() + 60.0
while True:
if time.time() > deadline:
sys.exit("Timed out waiting for OK")