August 2, 2025
Redirecting printf and scanf to UART in Embedded Systems

Redirecting printf and scanf to UART in Embedded Systems

In embedded systems, debugging and user interaction are often challenging due to the lack of standard input/output mechanisms like consoles, keyboards, or screens. Instead, serial communication through peripherals like UART (Universal Asynchronous Receiver/Transmitter) becomes an essential tool for communication between the microcontroller and external devices. One of the most common approaches to facilitate debugging or user interaction is by redirecting the standard input and output functions, printf and scanf, to UART. This enables developers to print debug information, receive user input, or configure the system through a serial terminal.

In this article, we will explore how to redirect both printf and scanf to UART in embedded systems, allowing you to interact with and monitor your system using a serial terminal.

Why Redirect printf and scanf to UART?

  • No Display or Keyboard: Many embedded systems lack a display or input device. By redirecting printf and scanf to UART, you can easily send debug output and receive user input through a serial connection without needing additional hardware.
  • Real-Time Debugging: Redirecting printf to UART allows you to print diagnostic messages or data in real-time, making it easier to track the system’s behavior during development or troubleshooting.
  • User Interaction: scanf allows the user to interact with the system, send commands, or input data via a serial terminal. This is especially useful in embedded systems without a GUI or keyboard.
  • Cross-Platform Portability: This method can be adapted to various microcontrollers and platforms, allowing for consistent and portable user interaction and debugging.

Understanding the Solution

The C standard library functions printf and scanf are designed to work with standard input and output streams. By default, these functions interact with a console or terminal. In embedded systems, we redirect these functions to UART by overriding the standard input/output functions (fputc for printf and fgetc for scanf), enabling communication over a UART interface.

Steps to Redirect printf and scanf to UART

Initialize the UART Interface

Before we can redirect printf and scanf, we must initialize the UART peripheral for communication. This process involves setting parameters like the baud rate, word length, stop bits, and the mode of operation. Below is an example of how to initialize UART using STM32 HAL (Hardware Abstraction Layer):

// UART initialization for STM32 (adjust for your microcontroller)
void UART_Init(void) {
    UART_HandleTypeDef huart;
    
    huart.Instance = USART1;  // Replace with your UART instance (USART1, USART2, etc.)
    huart.Init.BaudRate = 115200;
    huart.Init.WordLength = UART_WORDLENGTH_8B;
    huart.Init.StopBits = UART_STOPBITS_1;
    huart.Init.Parity = UART_PARITY_NONE;
    huart.Init.Mode = UART_MODE_TX_RX;
    huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart.Init.OverSampling = UART_OVERSAMPLING_16;
    
    HAL_UART_Init(&huart);  // Initialize the UART peripheral
}

You should modify this function to match the specifications for your platform or microcontroller.

Redirect printf to UART

To redirect the printf function to UART, we need to override the fputc function. printf sends characters to the output stream, and by overriding fputc, we can direct these characters to the UART instead of the standard console.

Here is how to override fputc to send characters via UART:

#include "stm32f4xx_hal.h"  // Include the appropriate HAL library

// UART handle (extern declaration if defined elsewhere)
extern UART_HandleTypeDef huart1;  // Adjust based on your UART configuration

// Override fputc to send characters via UART
int fputc(int ch, FILE *f) {
    HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, HAL_MAX_DELAY);  // Transmit 1 byte over UART
    return ch;  // Return the character
}

This implementation uses HAL_UART_Transmit to send each character to UART. The HAL_MAX_DELAY argument ensures that the function waits until the transmission is complete.

Redirect scanf to UART

In a similar manner, to read input from UART using scanf, we need to override the fgetc function. The fgetc function is used by scanf to read characters one at a time from the input stream. By overriding fgetc, we can redirect the input to come from UART instead of the console.

Here’s how to implement fgetc for UART input:

// Override fgetc to read characters from UART
int fgetc(FILE *f) {
    uint8_t ch;
    HAL_UART_Receive(&huart1, &ch, 1, HAL_MAX_DELAY);  // Receive 1 byte from UART
    return ch;  // Return the received character
}

Using printf and scanf

Now that we’ve overridden fputc and fgetc, we can use printf and scanf as usual. When you call printf, the output will be sent via UART to your terminal, and when you call scanf, the input will be received through the UART interface.

Here’s an example demonstrating both:

#include <stdio.h>  // Include standard I/O functions

int main(void) {
    HAL_Init();  // Initialize HAL (system, clocks, etc.)
    UART_Init(); // Initialize UART peripheral

    char inputBuffer[100];  // Buffer to hold user input

    // Prompt the user for input via UART
    printf("Enter a string: ");
    
    // Read input from UART using scanf
    scanf("%99[^\n]", inputBuffer);  // Read up to 99 characters until newline

    // Print the received input via UART
    printf("You entered: %s\n", inputBuffer);

    while (1) {
        // Main loop
    }
}

Additional Considerations

Buffering: Both printf and scanf use buffering, which means output may not appear immediately. If you need immediate output, consider disabling buffering:

setvbuf(stdout, NULL, _IONBF, 0);  // Disable buffering for stdout
setvbuf(stdin, NULL, _IONBF, 0);   // Disable buffering for stdin

Handling Input: For more complex input processing, such as handling multiple lines or controlling input length, you might want to implement custom input handling or parse the received data manually.

Interrupts: In some embedded systems, using interrupts for UART transmission and reception can be more efficient, especially for handling larger amounts of data. You can modify the fgetc and fputc implementations to use interrupt-based functions like HAL_UART_Transmit_IT or HAL_UART_Receive_IT.

Conclusion

Redirecting printf and scanf to UART in embedded systems is an essential technique for debugging and user interaction. By overriding the fputc and fgetc functions, we can seamlessly route both output and input through a UART interface. This method simplifies communication with embedded systems and enhances their usability, providing an easy way to monitor system behavior and interact with it, all through a serial terminal. Whether you’re using STM32, Arduino, or other microcontroller platforms, this technique can be adapted to virtually any embedded system.

Leave a Reply

Your email address will not be published. Required fields are marked *