Debugging using printf() Output through UART for in Micromouse - Basic Concept, Coding for STM32 printf(), Important Note
Debugging using printf() Output through UART for in Micromouse - Basic Concept, Coding for STM32 printf(), Important Note
Using printf() with UART is one of the easiest ways to display debugging messages, sensor values, and system status in Micromouse robot. It is widely used in microcontroller projects such as STM32, AVR, ESP32, and Arduino-compatible boards.
Instead of manually transmitting characters one by one, developers can use the familiar C standard library function printf() to send formatted text directly through UART.
What is UART?
UART (Universal Asynchronous Receiver Transmitter) is a serial communication protocol used for communication between:
- Microcontrollers
- PCs
- Bluetooth modules
- GPS modules
- Sensors
- Debug terminals
UART transfers data using two lines:
- TX (Transmit)
- RX (Receive)
Why Use printf() with UART?
Using printf() provides several advantages:
- Easier debugging
- Formatted output support
- Faster development
- Human-readable serial logs
- Simple variable monitoring
Example:
printf("Temperature = %d C\r\n", temp);
This is much easier than manually converting numbers to strings.
Basic Concept
Normally, printf() sends output to the standard console.
In embedded systems, there is no default console.
Therefore, developers redirect the printf() output to UART.
The process is:
printf() → low-level write function → UART transmit → Serial terminal
Hardware Required
Typical setup includes:
- Microcontroller board
- USB-to-UART converter
- Serial terminal software
Examples of USB-UART converters:
- CP2102
- FT232
- CH340
Popular serial terminal software:
- PuTTY
- Arduino IDE Serial monitor
UART Configuration Parameters
Common UART settings: The baud rate should be set to the required speed depending on the system's conditions. However, if it is too fast, data may be missed.
| Parameter | Typical Value |
|---|---|
| Baud Rate | 115200 |
| Data Bits | 8 |
| Stop Bits | 1 |
| Parity | None |
Coding for STM32 printf() via UART
Include Required Header
#include "stdio.h" Redirect printf()
Assume UART1 is used: Modify a part of the source file(Core/Src/syscalls.c) as follows.extern UART_HandleTypeDef huart1;
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
// modified for serial communication
int DataIdx = 0;
uint8_t thechar;
thechar= ' ';
while(thechar!= '\n' && thechar != '\r' && DataIdx<len)
{
__HAL_UART_CLEAR_OREFLAG(&huart1);
HAL_UART_Receive(&huart1, (uint8_t *)&thechar, 1, 100);
if ( thechar >= 0xFF)
{
printf("\n\r !!! Please enter a valid ASCII character \n");
return 0xFF;
}
*ptr++ =thechar;
DataIdx+=1;
}
*ptr = '\0';
return DataIdx;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
// modified for serial communication
HAL_StatusTypeDef hstatus;
hstatus = HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
if (hstatus == HAL_OK) return len;
return EIO;
}
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
// modified for serial communication
int DataIdx = 0;
uint8_t thechar;
thechar= ' ';
while(thechar!= '\n' && thechar != '\r' && DataIdx<len)
{
__HAL_UART_CLEAR_OREFLAG(&huart1);
HAL_UART_Receive(&huart1, (uint8_t *)&thechar, 1, 100);
if ( thechar >= 0xFF)
{
printf("\n\r !!! Please enter a valid ASCII character \n");
return 0xFF;
}
*ptr++ =thechar;
DataIdx+=1;
}
*ptr = '\0';
return DataIdx;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
// modified for serial communication
HAL_StatusTypeDef hstatus;
hstatus = HAL_UART_Transmit(&huart1, (uint8_t *) ptr, len, HAL_MAX_DELAY);
if (hstatus == HAL_OK) return len;
return EIO;
}
Example Code
#include "main.h"
#include "stdio.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_USART1_UART_Init();
int speed = 120;
float voltage = 3.3;
uint8_t data = 0x5A;
while (1)
{
printf("Hello UART\r\n");
printf("Speed = %d\r\n", speed); // for integer
printf("Voltage = %.2f V\r\n", voltage); // for float number
printf("Data = 0x%X\r\n", data); // hexadicimal
HAL_Delay(1000);
}
}
Any type of number can be naturally used by listing it together with a string.
printf("Left=%ld Right=%ld Error=%d\r\n", // 32 bit long integer
left_encoder,
right_encoder,
position_error);
This helps monitor robot behavior in real time.
Important Note About Floating Point
Some embedded compilers disable float formatting to save memory.
If float values do not print correctly:
Voltage = ?
Enable floating-point support in project settings.
For STM32 GCC:
-u _printf_float
Common Applications
UART printf() is commonly used for:
- Sensor debugging
- Motor tuning
- PID monitoring
- Micromouse debugging
- Bluetooth communication logs
- Error reporting
- State machine tracing
Best Practices
- Keep debug messages short
- Avoid printing inside interrupts
- Use
\r\nfor terminal compatibility - Disable debug logs in release firmware
Conclusion
Using printf() with UART is a powerful and convenient debugging technique in embedded systems. By redirecting standard output to UART, developers can easily monitor variables, sensor readings, and system behavior in real time.
Whether developing STM32 firmware, AVR applications, or Micromouse robots, UART printf() remains one of the most valuable tools for embedded debugging and diagnostics.
Comments
Post a Comment