yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
ESP32 Core Board📖
1. ESP32 Introduction
  • 1. ESP32S3 Overview
  • 2. Environment Setup Introduction
  • 3. Dev Board Introduction
2. ESP-IDF Development Tutorial
1. Before Development
  • 1. Environment Setup (Windows)
  • 2. Environment Setup (Linux)
  • 3. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. DMA
  • 8. ADC
  • 9. PWM Breathing LED
  • 10. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. PSRAM External Memory Experiment
  • 5. Flash Read and Write Experiment
  • 6. USB Emulated Mass Storage Experiment
4. FreeRTOS operating system
  • 1.FreeRTOS Core Concepts Theory
  • 2.Task Creation and Management
  • 3.Queue
  • 4.Binary Semaphore
  • 5.Mutex
  • 6.Event Groups
  • 7.Software Timer
  • 8.Task Scheduler
  • 9.Low Power Mode
  • 10.Deadlock
  • 11.Priority Inversion
5. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. Socket Programming Basics
  • 9. HTTP Client Experiment
  • 10. HTTP Server Experiment
  • 11. MQTT IoT Communication Protocol
  • 12. WIFI OTA Upgrade
  • 13. Bluetooth Fundamentals
  • 14. Bluetooth Slave Mode
  • 15. Bluetooth Master Mode
6. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
7. Xiaozhi AI Voice
  • 1. Burn Xiaozhi Firmware
  • 2. Network and Configuration
  • 3. AI Agent Settings
  • 4. Program Analysis
  • 5. Free Chat
3. Arduino Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. ADC
  • 8. PWM Breathing LED
  • 9. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication
  • 2. I2C OLED Display
  • 3. SPI Bus Communication
  • 4. Flash Read and Write
  • 5. USB Emulated Mass Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Server Experiment
  • 5. UDP Client Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT IoT Communication Protocol
  • 11. WIFI OTA Upgrade
  • 12. Bluetooth Fundamentals
  • 13. Bluetooth Slave Mode
  • 14. Bluetooth Master Mode
5. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
4. MicroPython Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
  • 3. Thonny IDE
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. TIMER_IT
  • 5. Watchdog Timer
  • 6. ADC
  • 7. PWM Breathing LED
  • 8. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. Flash Read and Write Experiment
  • 5. Print Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT
  • 11. Bluetooth Fundamentals
  • 12. Bluetooth Slave Mode
  • 13. Bluetooth Master Mode
5. Audio Applications
  • 1. I2S Audio Playback Experiment
  • 2. Repeater Experiment
Download
  • Code
  • Hardware_Info
  • 3D_Model_File
  • Software_Tools
  • VMware
  • VM_File
Download ZIP

Welcome to ESP32 Core Board repository

1. Serial UART Communication Experiment

Serial UART Communication Experiment


Serial UART Communication Experiment1. UART Overview2. Core Concepts2.1 Asynchronous Serial Frame Format2.2 Baud Rate and Sampling2.3 Flow Control2.3.1 No Flow Control (None)2.3.2 RTS/CTS Hardware Flow Control2.3.3 XON/XOFF Software Flow Control2.3.4 How to Choose3. ESP32-S3 UART Hardware Specifications4. Project Architecture and Flow5. Hardware Dependencies6. Source Code Details6.1 Header Files and Macro Configuration6.2 uart_init() — Initialization6.3 uart_tx() — Send Data6.4 uart_rx() — Receive Data6.5 app_main() — Entry Point7. Operation Flow7.1 Build and Flash Process7.2 Loopback Test7.3 Edge Cases8. FAQ


1. UART Overview

UART (Universal Asynchronous Receiver/Transmitter) is an asynchronous serial communication protocol that transmits data between devices through two signal lines: TX (Transmit) and RX (Receive), without sharing a clock.

Typical use cases:

ApplicationDescription
Serial Debug / Log OutputConnect to PC serial terminal via UART0
Module CommunicationGPS, Bluetooth, Wi-Fi modules, 4G modules, etc.
Sensor Data AcquisitionCO₂ sensors, laser ranging, PM2.5, etc.
Industrial ControlRS-232 / RS-485 device communication
Loopback TestTX ↔ RX self-transmit/receive to verify hardware and drivers

This project demonstrates UART1 loopback communication: MCU sends a string via TX, RX receives the echoed data, achieving self-send/self-receive hardware verification.


2. Core Concepts

2.1 Asynchronous Serial Frame Format

UART transmits data in frames (common 8N1 format = 8 data bits + no parity + 1 stop bit):

​x
IDLE  ┌──┐ ┌──┬──┬──┬──┬──┬──┬──┬──┐ ┌──┐
      │St│ │D0│D1│D2│D3│D4│D5│D6│D7│ │Sp│  IDLE
──────┘  │ │  │  │  │  │  │  │  │ │  ├────────
         └──┘ └──┴──┴──┴──┴──┴──┴──┴──┘ └──┘

      Start bit ←──── Data bits (LSB first) ────→ Stop bit
ParameterMeaningCommon Values
Baud RateSymbols transmitted per second9600, 115200, 921600
Data BitsData width5 / 6 / 7 / 8
ParityParity checkNone / Even / Odd
Stop BitsStop bit length1 / 1.5 / 2

On the same bus, sender and receiver baud rate, data bits, parity, and stop bits must be exactly the same, otherwise garbled text will appear.

2.2 Baud Rate and Sampling

ESP32-S3's UART controller oversamples the RX pin at 16x baud rate clock, sampling near the midpoint of each bit to determine 0/1, avoiding misjudgment due to signal edge jitter.

Principle: The controller continuously samples the RX pin at 16x baud rate clock, taking the sample value near the center of each bit to determine 0/1, thus avoiding the jitter zone at signal edges.

2.3 Flow Control

When the sender continuously sends data to the receiver, if the receiver cannot process fast enough (CPU busy, buffer full, writing to Flash, etc.), data will be lost. Flow control gives the receiver a "pause/continue" notification mechanism, allowing the sender to match the receiver's processing rhythm.

Three flow control methods are described below.

2.3.1 No Flow Control (None)

Only TX and RX two lines, no negotiation between sender and receiver, sender just sends. This is the simplest method, but requires the receiver can always keep up with the sender's rate. Applicable for:

  • Low-speed communication (e.g., 9600 bps)

  • Very small data volume, occasional transmission

  • MCU processing capability far exceeds communication rate

  • This example scenario: ESP32-S3 handles 20+ bytes effortlessly at 115200 bps, no flow control needed

2.3.2 RTS/CTS Hardware Flow Control

Hardware flow control requires two additional lines: RTS (Request To Send) and CTS (Clear To Send), total TX, RX, RTS, CTS four lines.

Key rule: RTS is local output (telling the other party whether I can receive), CTS is local input (listening to whether the other party allows me to send).

  • Sender checks its CTS pin before sending:

    • Peer pulls high → can send

    • Peer pulls low → pause, wait

  • Receiver pulls RTS low when RX FIFO is nearly full, notifying the other party "stop sending"; after buffer is emptied, pulls RTS high, notifying "can continue".

Typical wiring is cross connection: local RTS → remote CTS, local CTS → remote RTS.

xxxxxxxxxx
Sender                          Receiver
  TX ──────────────────────────── RX
  RX ──────────────────────────── TX
  RTS ──────── cross ───────────── CTS
  CTS ──────── cross ───────────── RTS

Timing diagram:

xxxxxxxxxx
Buffer status:  [Idle] ────→ [Almost full] ────→ [Emptied] ────→ [Idle]

RTS signal:    ──────────┐          ┌──────────────
                       └──────────┘
                  Pull low (pause peer)  Pull high (resume peer)

Sender behavior:  Normal send ─→ Stop ─────→ Resume sending

ESP32-S3 has built-in RTS/CTS pins. When enabled, the UART controller automatically manages RTS signal level — when RX FIFO reaches configured threshold (e.g., 100 bytes), hardware automatically pulls RTS low; when FIFO drops below safe level, hardware automatically pulls RTS high. The entire process is transparent to software.

Hardware flow control advantages:

  • Good real-time performance: Hardware signal, fast response

  • No data bandwidth consumption: Control signals on separate lines

  • Transparent to software: No need to insert special characters in data

Disadvantages are also obvious:

  • Two more wires, higher wiring cost

  • Longer distance wiring requires thicker, more expensive cables

2.3.3 XON/XOFF Software Flow Control

Software flow control does not add extra physical lines, but inserts special characters in the data stream to control the rhythm:

CharacterASCII ValueMeaning
XOFF0x13 (DC3)Pause transmission
XON0x11 (DC1)Resume transmission

Workflow:

  • When receiver cannot keep up, send XOFF (0x13) on reverse channel

  • Sender receives XOFF and immediately pauses

  • After receiver finishes processing, send XON (0x11)

  • Sender receives XON and resumes transmission

xxxxxxxxxx
Sender                            Receiver
  │                               │
  ├──────── data ────────────────→ │
  │                               │ Buffer almost full
  │←────────── XOFF (0x13) ───────┤
  │  Pause                          │
  │                               │ Buffer emptied
  │←────────── XON  (0x11) ───────┤
  ├─────── data ──────────────────→│
  │                               │

Software flow control advantages:

  • No extra pins needed: Only TX, RX two lines required

  • Suitable for pin-constrained or long-distance two-wire communication scenarios

Disadvantages:

  • Consumes data bandwidth: XON/XOFF characters cannot appear in transmitted data (if data itself contains 0x11/0x13, escape is needed)

  • Response delay: Special characters must queue for sending, not as instant as hardware signals

  • Not suitable for binary data transmission (0x11/0x13 may appear frequently, escaping overhead is high)

2.3.4 How to Choose

ScenarioRecommended Method
Low speed, small data, ample MCU performanceNo flow control
High speed large data (≥ 921600 bps), sufficient pinsRTS/CTS hardware flow control
Pin-constrained, printable data (no binary)XON/XOFF software flow control
Full-duplex large data, high real-time requirementsHardware flow control
RS-485 half-duplexNo flow control needed (inherently half-duplex)

3. ESP32-S3 UART Hardware Specifications

FeatureParameter
UART Controller Count3 (UART0, UART1, UART2)
Baud Rate Range80 bps ~ 5 Mbps
FIFO Depth128 bytes × independent transmit/receive
Hardware Flow ControlRTS/CTS supported
RS-485 Half-DuplexSupported (DE pin auto-switching)
DMA SupportSupported (high throughput scenarios)
IRDA InfraredSupported
Wake-up FunctionUART wake-up in Light-sleep supported

Note: UART0 is used for system log and Console output by default. In actual products, UART1 or UART2 is typically used for communication with external devices.


4. Project Architecture and Flow

xxxxxxxxxx
app_main()
  ├─ uart_init()         ← Configure UART1 parameters + install driver
  │   ├─ gpio_config()                  Configure RX pin internal pull-up
  │   ├─ uart_param_config()            Configure 115200-8N1 no flow control
  │   ├─ uart_set_pin()                 Bind TX=GPIO14, RX=GPIO4
  │   └─ uart_driver_install()          Install driver, allocate FIFO buffer
  │
  └─ while(1):           ← Main loop
      ├─ uart_flush_input()   Clear receive buffer
      ├─ uart_tx()            Send string + log
      ├─ uart_rx()            Wait 100ms read back + log
      └─ vTaskDelay(2000ms)   Delay 2 seconds

5. Hardware Dependencies

Hardware Connection: GPIO14 (TX) ↔ GPIO4 (RX) shorted (loopback test)

UART Wiring Diagram

UART Wiring Diagram Back

Required Components: driver/uart, driver/gpio, FreeRTOS


6. Source Code Details

6.1 Header Files and Macro Configuration

xxxxxxxxxx
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/gpio.h"

#define TAG     "UART"         // Log tag
#define TXD_PIN 14             // TX pin (GPIO14)
#define RXD_PIN 4              // RX pin (GPIO4)
// TX & RX buffer size (bytes)
#define BUF_SIZE 256

Why TX uses GPIO14 and RX uses GPIO4? ESP32-S3's GPIO matrix can map any GPIO to UART peripheral. Choosing GPIO14/GPIO4 only needs to avoid strapping pins already in use (0/3/45/46) and Flash/PSRAM pins (26-37). Different peripheral courses on the same development board (such as DMA) also use this pin combination, facilitating Dupont wire reuse without repeated connection/disconnection.

6.2 uart_init() — Initialization

uart_init() completes four-step configuration:

  1. GPIO Configuration — Set RX pin as input and enable internal pull-up to prevent garbled text when floating.

  2. Parameter Configuration — Set 115200-8N1 (115200 baud rate, 8 data bits, no parity, 1 stop bit, no flow control).

  3. Pin Binding — Bind TX=GPIO14, RX=GPIO4 to UART1 (RTS/CTS not used, pass UART_PIN_NO_CHANGE).

  4. Driver Installation — Allocate 256-byte transmit/receive FIFO buffer for UART1 (queue depth 0, no internal task created).

uart_init() powers on UART1 peripheral, sets parameters, binds pins, installs driver. Four steps complete, ready for transmit/receive.

xxxxxxxxxx
static void uart_init(void)
{
    /* Config RX pin as input + internal pull-up, avoid garbage on floating */
    gpio_config_t rx_config = {
        .pin_bit_mask = 1ULL << RXD_PIN,
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&rx_config);

    /* 115200 baud, 8 data bits, no parity, 1 stop bit, no flow control */
    uart_config_t config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
    };

    uart_param_config(UART_NUM_1, &config);

    /* Bind TX/RX pins to UART1, RTS/CTS not used */
    uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN,
                 UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    /* Install UART driver, allocate 256-byte TX/RX FIFO */
    uart_driver_install(UART_NUM_1, BUF_SIZE, BUF_SIZE,
                        0, NULL, 0);
}

6.3 uart_tx() — Send Data

uart_tx() first prints [TX] log via ESP_LOGI(), then calls uart_write_bytes() to write the entire string to TX FIFO, finally calls uart_wait_tx_done() to wait for all data to be sent (max wait 200ms).

uart_tx() prints log first, then puts the string into TX FIFO, finally waits for hardware to finish sending before returning.

xxxxxxxxxx
static void uart_tx(const char *data)
{
    /* Log first for visibility into what's being sent */
    ESP_LOGI(TAG, "[TX] %s", data);

    /* Write the full string into TX FIFO */
    uart_write_bytes(UART_NUM_1, data, strlen(data));

    /* Wait for HW to flush all FIFO data (timeout 200ms) */
    uart_wait_tx_done(UART_NUM_1, pdMS_TO_TICKS(200));
}

6.4 uart_rx() — Receive Data

uart_rx() calls uart_read_bytes() with 100ms timeout to read data. After receiving data, iterates through each byte:

  • Printable characters (0x20 ~ 0x7E) remain unchanged

  • Control characters (\r, \n, etc.) are replaced with . to prevent log display abnormalities

uart_rx() waits 100ms to see if any data comes back. If received, non-printable characters (carriage return, newline, etc.) are uniformly replaced with . to prevent log garbling.

xxxxxxxxxx
static void uart_rx(void)
{
    uint8_t buf[BUF_SIZE];

    /* Read from RX FIFO, timeout 100ms */
    int len = uart_read_bytes(UART_NUM_1, buf, BUF_SIZE - 1,
                              100 / portTICK_PERIOD_MS);
    if (len > 0) {
        /* Replace non-printable chars (\r \n etc.) with '.' for log readability */
        for (int i = 0; i < len; i++) {
            if (buf[i] < 0x20 || buf[i] > 0x7E) {
                buf[i] = '.';
            }
        }
        buf[len] = 0;
        ESP_LOGI(TAG, "[RX] %s", buf);
    }
}

6.5 app_main() — Entry Point

app_main() is the entry point of the entire program: initialize UART1, then in an infinite loop flush buffer → send a sentence → receive back → wait 2 seconds, cycling to verify self-send/self-receive.

xxxxxxxxxx
void app_main(void)
{
    /* Initialize UART1 */
    uart_init();
    ESP_LOGI(TAG, "UART Started, TX=GPIO%d, RX=GPIO%d", TXD_PIN, RXD_PIN);

    while (1) {
        /* Flush RX buffer to avoid stale data interference */
        uart_flush_input(UART_NUM_1);

        /* Send test string */
        uart_tx("GOOD GOOD STUDY,DAY DAY UP!!!!!!\r\n");

        /* Read back loopback data */
        uart_rx();

        /* Wait 2s before next round */
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

Each round first uart_flush_input() clears residual data to prevent noise from the previous round's unread data from mixing into the current reception.


7. Operation Flow

7.1 Build and Flash Process

For detailed flash process: 1. Before Development → 3. Build and Flash

7.2 Loopback Test

Short GPIO14 (TX) and GPIO4 (RX) with Dupont wire. After powering on, self-send/self-receive once every 2 seconds:

xxxxxxxxxx
I (1263) UART: UART Started, TX=GPIO14, RX=GPIO4
I (1264) UART: [TX] GOOD GOOD STUDY,DAY DAY UP!!!!!!

I (1280) UART: [RX] GOOD GOOD STUDY,DAY DAY UP!!!!!!
I (3280) UART: [TX] GOOD GOOD STUDY,DAY DAY UP!!!!!!

I (3296) UART: [RX] GOOD GOOD STUDY,DAY DAY UP!!!!!!

In the log, [TX] and [RX] content are exactly the same, indicating hardware and drivers are working normally.

Serial Running Diagram

7.3 Edge Cases

  • No wiring → RX no data: Signal sent by TX has no path back to RX, uart_read_bytes() times out and returns 0, no [RX] line printed.

    No wiring or wrong wiring serial diagram


8. FAQ

SymptomCauseSolution
Log output garbledTerminal baud rate doesn't match codeSet terminal to 115200 (matching code baud_rate)
[TX] has output but [RX] has noneTX/RX not shorted or wrong pinsCheck Dupont wire: GPIO14 ↔ GPIO4
Boot error uart_driver_install failedUART number already in useUART0 reserved by system, use UART1 or UART2
Occasional byte loss100ms timeout too short / buffer too smallIncrease BUF_SIZE or extend uart_read_bytes timeout
No output, terminal unresponsiveUSB-TTL converter not connected or wrong pinsConfirm using USB-TTL module to connect GPIO14/GPIO4: GPIO14 to module RX, GPIO4 to module TX
RGB LED Effects
I2C_OLED Display Experiment

COMPANY

  • About us
  • Team Style
  • Blog

LEARNING & EXPLORATION

  • Tutorials
  • Edge AI hardware
  • Educational robots
  • Embedded intelligent hardware

WHERE TO BUY

  • Yahboom Online Store
  • Amazon
  • Alibaba
  • AliExpress
  • Mercado

TECHNOLOGY SUPPORT

  • Email: support@yahboom.com

BE A DISTRIBUTOR

  • Email: sales@yahboom.com
QR Code

Copyright © 2017-2026 Yahboom. All Rights Reserved.

EN/中文