1

I am trying to perform simple audio denoising using an STM32F401RE Nucleo board.

I have .wav audio files that are:

  • mono
  • 16-bit PCM
  • 16 kHz
  • about 7–8 seconds long

Python reads the audio samples and sends them one by one over UART to the STM32.
The STM32 applies a simple moving-average filter and sends the processed samples back to Python.
Python then reconstructs the filtered samples into a new .wav file.

However the reconstructed audio file is incorrect:

  • it becomes only about 2–4 seconds long
  • the output audio contains mostly noise/glitches instead of speech

Example output from Python:

Total samples in file: 130672 Filtered samples received: 72021 

So it seems that many samples are lost during UART transmission or processing.

I want to understand how to correctly stream audio samples over UART to STM32 without losing data.

STM32 Code (filter + UART processing)

uint8_t rx_char; uint8_t buffer[50]; uint8_t index = 0; int16_t x0 = 0; int16_t x1 = 0; int16_t x2 = 0; int16_t x3 = 0; HAL_UART_Receive(&huart2, &rx_char, 1, HAL_MAX_DELAY); if(rx_char == '\r' || rx_char == '\n') { buffer[index] = '\0'; int16_t value = (int16_t)atoi((char*)buffer); x3 = x2; x2 = x1; x1 = x0; x0 = value; int32_t sum = (2*(int32_t)x0 + (int32_t)x1 + (int32_t)x2); int16_t result = sum / 4; char msg[50]; sprintf(msg,"\r\nResult: %d\r\n\r\n",result); HAL_UART_Transmit(&huart2,(uint8_t*)msg,strlen(msg),HAL_MAX_DELAY); memset(buffer,0,sizeof(buffer)); index = 0; } else { if(index < 49) buffer[index++] = rx_char; } 

Python Code

import wave import numpy as np import serial import time ser = serial.Serial('COM9',115200,timeout=1) filtered_samples = [] file_path = r"recordings/raw_enrollment_16k/user_1/sample_1.wav" with wave.open(file_path,'rb') as w: frames = w.readframes(w.getnframes()) samples = np.frombuffer(frames,dtype=np.int16) print("Total samples in file:", len(samples)) for s in samples: ser.write((str(int(s)) + "\n").encode()) time.sleep(0.0005) while ser.in_waiting: response = ser.readline().decode(errors='ignore').strip() if response: try: value = int(response.split()[-1]) filtered_samples.append(value) except: pass print("Filtered samples received:", len(filtered_samples)) 
1

1 Answer 1

3

You are running into multiple issues here.

  1. UART bandwidth is too low

Your audio stream is:

16 kHz * 16 bit = 256 kbit/s raw data 

But you are not sending raw samples. You are sending ASCII numbers like

-12345\n 

That is typically ~6–7 bytes per sample. UART also sends start/stop bits, so the real bandwidth becomes roughly

16000 samples/s * 7 bytes * 10 bits ≈ 1.12 Mbit/s 

If your UART runs at e.g. 115200 baud , that's an order of magnitude too slow. So samples must be dropped somewhere.

Thus, use a high baud rate. On STM32, even 1 or 2 Megabaud (2,000,000 baud/s) should be supported.

  1. ASCII encoding is extremely inefficient

This pipeline is very expensive.

Python
int → string → UART TX

STM32
UART RX → atoi() → average filter → sprintf() → UART TX

Python
UART RX → string → int

Both atoi() and especially sprintf() are slow and unnecessary here. Avoid any printf-style formatting inside a sample processing loop.

Instead send samples as binary int16_t directly over the wire.

Python:

ser.write(np.int16(s).tobytes()) 

STM32:

int16_t sample; HAL_UART_Receive(&huart2, (uint8_t*)&sample, sizeof(sample), HAL_MAX_DELAY); 

Return the result the same way:

HAL_UART_Transmit(&huart2, (uint8_t*)&result, sizeof(result), HAL_MAX_DELAY); 

That reduces the required bandwidth to roughly

16000 * 2 bytes * 10 bits ≈ 320 kbit/s 

which is easily doable at even 921600 baud.

  1. HAL_UART_Receive() in polling mode is not great for streaming

You are using blocking polling:

HAL_UART_Receive(..., HAL_MAX_DELAY) 

For higher throughput you should use at least

HAL_UART_Receive_IT() 

or ideally

HAL_UART_Receive_DMA() 

The polling UART access mode will cause it so that if the internal UART buffer (probably like 1 character, or 16 character FIFO at most) is full, and the CPU is not actively in the HAL_UART_Receive() call, the data is lost. That's a UART "overrun" -- a character was received but couldn't be stored because the buffer was not empty.

UART DMA is commonly used for streaming data like audio. Using either the interrupt (IT) or direct memory access (DMA) modes to receive UART data greatly reduces the CPU load, as either the CPU is interrupted only for a very short time when a new character appears to store it in a buffer (interrupt), or is even not doing the store at all because the DMA did it for it.

Note that to setup the UART in interrupt or DMA mode, you should go back to the STM32CubeMX configurator. It will require a different initialization and the addition of interrupt handlers for the UART peripheral to properly handle it.

Sign up to request clarification or add additional context in comments.

1 Comment

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.