fixed most upload issues, updated schematic
This commit is contained in:
+17
-29
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user