UART Protocol¶
The firmware outputs structured text over USART1 at 921,600 baud (8N1).
The host Python script (board_test.py) captures and parses this output using
regex patterns.
Output Structure¶
A complete run produces output in this order:
[INIT] Setting vector table...
[INIT] HAL and clocks configured
[INIT] Enabling caches...
[INIT] No overdrive (CPU @ 600 MHz, NPU @ 800 MHz)...
[INIT] Configuring UART...
[INIT] Configuring external memories...
[OK] External memories mapped
[INIT] Configuring NPU...
=== BirdNET-STM32 SD Card Inference ===
[INFO] Frontend: raw (waveform → NPU)
[INFO] Sample rate: 24000 Hz, chunk: 2.500s (60000 samples), classes: 25
[INIT] Initialising NPU network...
[OK] NPU input: "..." 240000 bytes
[OK] NPU output: "..." 100 bytes
[INIT] Mounting SD card (SDMMC2)...
[OK] SD card mounted
[OK] 25 class labels compiled in
[INIT] Scanning /audio/ for .wav files...
[OK] Found 8 audio files
[1/8] recording_001.wav
[WAV] 24000 Hz, 16-bit, 1 ch, 72000 samples
[BENCH] read=72ms stft=0ms npu=13ms total=85ms
recording_001.wav:
[1] Common Chiffchaff: 72.3%
[2] Eurasian Blue Tit: 15.1%
[2/8] recording_002.wav
[WAV] 24000 Hz, 16-bit, 1 ch, 72000 samples
[BENCH] read=72ms stft=0ms npu=13ms total=85ms
recording_002.wav:
[1] Great Tit: 89.1%
...
=== DONE ===
Processed: 8 / 8 files (0 errors)
Benchmark: read=576ms stft=0ms npu=104ms total=680ms (avg read=72ms stft=0ms npu=13ms total=85ms)
[OK] SD card unmounted. Halting.
Line-by-Line Reference¶
Init Lines¶
| Prefix | Meaning |
|---|---|
[INIT] |
Board initialization step in progress |
[OK] |
Step completed successfully |
[ERROR] |
Fatal error — firmware halts after printing |
[WARN] |
Non-fatal warning (e.g., no audio files found) |
[INFO] |
Informational (model parameters) |
Per-File Lines¶
File header:
Format:[index/total] filename — 1-indexed.
WAV info:
Parsed sample rate, bit depth, channels, and total samples.Benchmark:
Per-file timing in milliseconds (1 ms resolution fromHAL_GetTick()):
read— SD card I/O (FatFsf_read+ PCM16→float32 conversion)stft— frontend CPU time (STFT for hybrid/librosa; zero for raw)npu— NPU inference (including cache flush/invalidate and memcpy)total— sum of the above
Skip/error lines:
[SKIP] Sample rate 22050 != 24000
[SKIP] Cannot open file
[SKIP] Invalid WAV format
[ERROR] Inference failed
Detection results:
Top-K predictions sorted by descending score. Only scores ≥APP_SCORE_THRESHOLD are printed. The score is formatted as integer.tenths%
(e.g., 72.3% = 0.723).
Summary Lines¶
Done marker — signals end of processing:
File count:
processed / total (errors) — processed + errors = total.
Aggregate benchmark:
Cumulative timing and per-file averages. Only printed ifprocessed > 0.
Host-Side Parsing¶
The Python board_test.py uses these regex patterns:
# File header
r"^\[(\d+)/(\d+)\]\s+(.+)$"
# WAV info
r"^\s+\[WAV\]\s+(\d+)\s+Hz,\s+(\d+)-bit,\s+(\d+)\s+ch,\s+(\d+)\s+samples"
# Per-file benchmark
r"^\s+\[BENCH\]\s+read=(\d+)ms\s+stft=(\d+)ms\s+npu=(\d+)ms\s+total=(\d+)ms"
# Detection result
r"^\s+\[(\d+)\]\s+(.+?):\s+([\d.]+)%"
# Skip / error
r"^\s+\[SKIP\]\s+(.+)$"
r"^\s+\[ERROR\]\s+(.+)$"
# Done marker
r"^=== DONE ===$"
# Summary
r"^Processed:\s+(\d+)\s*/\s*(\d+)\s+files\s+\((\d+)\s+errors\)"
# Aggregate benchmark
r"^Benchmark:.*?read=(\d+)ms\s+stft=(\d+)ms\s+npu=(\d+)ms\s+total=(\d+)ms\s+"
r"\(avg read=(\d+)ms\s+stft=(\d+)ms\s+npu=(\d+)ms\s+total=(\d+)ms\)"
Parsed Output Structure¶
The parser produces a dictionary:
{
"results": [
{
"file": "recording_001.wav",
"detections": [
{"label": "Common Chiffchaff", "score": 0.723},
{"label": "Eurasian Blue Tit", "score": 0.151},
],
"bench": {"read_ms": 72, "stft_ms": 0, "npu_ms": 13, "total_ms": 85},
},
# ...
],
"processed": 8,
"total": 8,
"errors": 0,
"benchmark": {
"avg_read_ms": 72, "avg_stft_ms": 0, "avg_npu_ms": 13, "avg_total_ms": 85,
},
}
Real-Time Factor¶
The host displays a real-time factor (RTF) after the run:
RTF = avg_total_ms / (chunk_duration × 1000). Values below 1 mean faster than
real time; the host prints the reciprocal as the speedup.
Timeout Behavior¶
The host waits for the === DONE === marker or a configurable timeout
(default 300 s). If the timeout expires:
- Partial results are still parsed and displayed.
- A warning is printed indicating incomplete processing.
- The command returns the partial parsed result after printing a warning.
Common causes of timeout: firmware crash (bus fault, assertion), SD card not inserted, wrong serial port, baud rate mismatch.