Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home IoT & Smart Home

ESP32 Interrupt-Driven UART: High-Speed Serial Communication

ESP32 Interrupt-Driven UART: High-Speed Serial Communication

March 11, 2026 /Posted byJayesh Jain / 0

When building high-speed IoT systems in India, one of the most critical skills is mastering ESP32 interrupt UART serial communication. Polling-based serial reading — where your code constantly checks if data is available — works fine for hobby experiments but breaks down the moment you need to handle fast sensor data, GPS streams, or GSM module responses without dropping bytes or freezing the CPU. Interrupt-driven UART solves this by letting the hardware notify your firmware asynchronously when data arrives, freeing the processor to do real work in between. This tutorial covers ESP32 UART architecture, ISR-based reception, ring buffers, and DMA transfers at a depth rarely found in hobbyist guides.

Table of Contents

  1. ESP32 UART Hardware Overview
  2. Why Interrupt-Driven UART Beats Polling
  3. ESP-IDF UART Interrupt Setup
  4. Using Ring Buffers for Safe ISR Data Transfer
  5. Arduino Framework: HardwareSerial and Custom ISRs
  6. Multi-UART: Using All Three UART Ports
  7. DMA-Driven UART for Maximum Throughput
  8. Frequently Asked Questions

ESP32 UART Hardware Overview

The ESP32 silicon includes three independent hardware UART controllers: UART0, UART1, and UART2. Each UART has its own hardware TX/RX FIFO buffers (128 bytes each by default), baud rate generator, parity checker, and interrupt controller. This is fundamentally different from software-emulated serial (bit-banging) used on AVR-based Arduinos — ESP32 UART is fully hardware-backed, meaning no CPU cycles are wasted generating or sampling individual bits.

Default UART Pin Assignments

UART Port Default TX GPIO Default RX GPIO Common Use
UART0 GPIO 1 GPIO 3 USB debug / programming
UART1 GPIO 10 GPIO 9 Flash (remap for app use)
UART2 GPIO 17 GPIO 16 Sensor / GSM / GPS

The GPIO matrix on ESP32 allows routing any UART TX/RX to almost any GPIO pin, giving you tremendous flexibility. UART0 is occupied by the USB-to-serial chip on DevKit boards, so for application code use UART1 or UART2 with remapped GPIOs.

Ai Thinker NodeMCU-32S-ESP32 Development Board

Ai Thinker NodeMCU-32S-ESP32 Development Board – IPEX Version

This ESP32 development board exposes all three UART ports, giving you room to run UART2 for your sensor while UART0 stays free for debug output — essential for interrupt-driven UART projects.

View on Zbotic

Why Interrupt-Driven UART Beats Polling

In a polling-based design, your main loop constantly calls Serial.available() or uart_read_bytes() and checks if data is ready. This approach has three fatal flaws for production IoT firmware:

  1. CPU waste: At 115200 baud, a byte arrives every ~87 µs. Between bytes the CPU is spinning in a busy-wait loop, burning power and blocking other tasks.
  2. Latency variability: If a WiFi event handler, RTOS task scheduler, or sensor read delays the poll loop by even 1–2 ms, you risk overrunning the 128-byte hardware FIFO and losing data permanently.
  3. Responsiveness: Real-time systems (motor controllers, GPS parsers, GSM AT command handlers) need deterministic byte-level response times that polling cannot guarantee.

With interrupt-driven UART, the UART hardware asserts an interrupt line the moment data arrives in the RX FIFO (or when the FIFO reaches a threshold). The CPU services the ISR (Interrupt Service Routine) immediately, copies bytes into a software ring buffer, and returns to whatever it was doing — typically in under 5 µs. The main application reads from the ring buffer at leisure without any risk of data loss.

ESP-IDF UART Interrupt Setup

The ESP-IDF UART driver internally uses interrupts and an event-driven task model. Here is the canonical pattern for interrupt-driven UART RX in ESP-IDF:

#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#define UART_NUM       UART_NUM_2
#define TX_PIN         17
#define RX_PIN         16
#define BUF_SIZE       1024
#define UART_BAUD      115200

static QueueHandle_t uart_queue;

void uart_event_task(void *pvParameters) {
    uart_event_t event;
    uint8_t buf[BUF_SIZE];
    for (;;) {
        if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) {
            if (event.type == UART_DATA) {
                int len = uart_read_bytes(UART_NUM, buf, event.size, pdMS_TO_TICKS(100));
                // Process buf[0..len-1] here
            }
        }
    }
}

void app_main(void) {
    uart_config_t cfg = {
        .baud_rate = UART_BAUD,
        .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, &cfg);
    uart_set_pin(UART_NUM, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart_queue, 0);
    xTaskCreate(uart_event_task, "uart_task", 4096, NULL, 12, NULL);
}

The uart_driver_install call with the queue handle is what activates interrupt-driven mode. The ESP-IDF UART driver installs a hardware ISR that fills an internal ring buffer and posts UART_DATA events to your queue. Your task wakes up only when data is available — zero polling overhead.

Using Ring Buffers for Safe ISR Data Transfer

A ring buffer (also called a circular buffer) is the canonical data structure for transferring bytes from an ISR to application code safely. The ISR writes to the buffer; the application reads from it. Because the ISR and app code run in different contexts, the buffer implementation must be lock-free or use critical sections.

Key Ring Buffer Rules for ESP32 ISRs

  • Never use heap allocation inside an ISR (malloc, new). Allocate buffers statically or at init time.
  • Never call blocking functions from an ISR: no printf, no vTaskDelay, no mutex locks.
  • Use volatile for shared variables accessed from both ISR and main code: volatile uint16_t head, tail;
  • Use portYIELD_FROM_ISR() at the end of your ISR if you have posted to a FreeRTOS queue inside it, to trigger an immediate context switch to the waiting task.
  • Keep ISRs short: Copy bytes from FIFO to buffer, update index, yield — done. Never parse or process data inside the ISR.

Arduino Framework: HardwareSerial and Custom ISRs

If you’re using the Arduino framework for ESP32 (via Arduino IDE or PlatformIO), the HardwareSerial class already uses interrupt-driven buffering internally. A 256-byte ring buffer receives bytes via the UART RX interrupt automatically. However, the default buffer size may be insufficient for high-baud-rate sensors like GPS modules streaming NMEA at 9600–38400 baud.

Increasing the Arduino UART Buffer Size

In your project’s platformio.ini or Arduino IDE board settings, set:

build_flags = -DRXBUFFERSIZE=2048

Or in Arduino IDE, edit the HardwareSerial.h file and change SERIAL_RX_BUFFER_SIZE. Alternatively, use the Serial2.setRxBufferSize(2048) method available in ESP32 Arduino core 2.x.

Using Serial2 with Custom Pins

// In setup():
Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX=16, TX=17
Serial2.setRxBufferSize(2048);

// In loop():
while (Serial2.available()) {
    char c = Serial2.read();
    // Process character
}

This is interrupt-driven behind the scenes — Serial2.available() just reads the ring buffer head/tail pointers without any hardware polling.

30Pin ESP32 Expansion Board

30Pin ESP32 Expansion Board with Type-C and Micro USB

The clearly labelled GPIO breakout on this expansion board makes it easy to route UART2 TX/RX pins to external sensors or GSM modules without jumper wire confusion.

View on Zbotic

Multi-UART: Using All Three UART Ports

ESP32’s three UART ports let you run multiple serial devices simultaneously — something the single-UART Arduino Uno famously cannot do without software serial hacks. A typical real-world ESP32 serial topology:

  • UART0 (GPIO 1/3): USB debug serial — Serial Monitor output during development.
  • UART1 (remapped to GPIO 4/5): GSM/4G LTE modem (AT commands) — interrupt-driven, 115200 baud.
  • UART2 (GPIO 16/17): GPS module (NMEA stream) — interrupt-driven, 9600 baud.

To remap UART1 away from the flash pins (GPIO 9/10, which you must NOT use on most modules), call:

Serial1.begin(115200, SERIAL_8N1, 4, 5); // RX=GPIO4, TX=GPIO5

With all three UARTs running interrupt-driven, your loop() can run application logic — MQTT publish, HTTP POST, display updates — while serial data from GPS and GSM accumulates safely in their respective ring buffers.

DMA-Driven UART for Maximum Throughput

For extremely high baud rates (921600 baud and above) or when processing thousands of bytes per second, even interrupt-driven reception creates overhead because each byte triggers a separate ISR invocation. DMA (Direct Memory Access) solves this by letting the UART hardware write directly to a RAM buffer without any CPU involvement — the CPU is only notified once the buffer is full or a timeout occurs.

ESP-IDF UART driver supports DMA in ESP32-S3 targets. For original ESP32, the uart_driver_install API with a sufficiently large RX buffer achieves near-DMA efficiency because the internal ringbuf is fed via a combined FIFO-threshold + RX-timeout interrupt strategy — bytes are batched, not individually interrupted.

For the highest performance on standard ESP32, set a high FIFO threshold:

uart_intr_config_t intr_cfg = {
    .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA,
    .rxfifo_full_thresh = 120,    // Interrupt when FIFO has 120 bytes
    .rx_timeout_thresh = 10,      // Interrupt after 10-char idle time
};
uart_intr_config(UART_NUM_2, &intr_cfg);

This reduces interrupt frequency by ~120x compared to per-byte interrupts, dramatically cutting ISR overhead at high baud rates.

DHT11 Temperature and Humidity Sensor

DHT11 Digital Temperature and Humidity Sensor Module

Pair your interrupt-driven UART ESP32 project with the DHT11 for environmental monitoring — combine UART-based GSM reporting with local temperature/humidity sensing in one build.

View on Zbotic

Frequently Asked Questions

Can I use UART1 on ESP32 for my sensor since UART0 is used by USB?

Yes, but GPIO 9 and 10 (default UART1 pins) are connected to the SPI flash on most ESP32 modules — using them will crash the chip. Always remap UART1 to safe GPIOs like 4/5 or 25/26 using the custom pin argument in Serial1.begin(baud, config, rx_pin, tx_pin).

How do I detect the end of a UART message in an ISR?

ESP32 UART has a built-in RX idle timeout interrupt (RXFIFO_TOUT). Configure it via uart_intr_config with a timeout of 3–10 character periods. When the line goes idle (no new bytes) for that duration, an interrupt fires signalling end-of-packet. This is the standard way to implement message framing without explicit delimiters.

My ISR-based UART is dropping bytes at 921600 baud. What’s wrong?

At 921600 baud, a byte arrives every ~10.8 µs — faster than most ISR round-trips if WiFi or BT interrupt handlers are also running. Solutions: disable WiFi while receiving (if not needed simultaneously), increase FIFO threshold to batch bytes per interrupt, or switch to ESP32-S3 which supports true UART DMA.

Is interrupt-driven UART safe to use with FreeRTOS on ESP32?

Yes — and it’s the recommended pattern. Use xQueueSendFromISR() and portYIELD_FROM_ISR() to communicate from ISR to tasks. Never call any non-ISR-safe FreeRTOS API (those without FromISR suffix) from inside an interrupt handler.

Get ESP32 Boards and Sensor Modules at Zbotic

Build your interrupt-driven UART project with ESP32 development boards, sensors, and expansion boards available at Zbotic.in — India’s trusted electronics components store with fast shipping nationwide.

Shop ESP32 IoT Components at Zbotic

Tags: embedded systems, ESP32, interrupt, serial communication, UART
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
ESP32 Deep Sleep: Ultra-Low Po...
blog esp32 deep sleep ultra low power iot sensor node build 595469
blog esp32 lvgl display build a rich ui on cheap tft screen 595472
ESP32 LVGL Display: Build a Ri...

Related posts

Svg%3E
Read more

IoT Home Insurance Sensor Kit: Leak, Smoke, and Motion

April 1, 2026 0
Table of Contents IoT and Home Insurance Water Leak Detection Smoke and Fire Detection Motion and Intrusion Sensing Building the... Continue reading
Svg%3E
Read more

IoT Pet Tracker: GPS Collar with Geofencing Alerts

April 1, 2026 0
Table of Contents Introduction and Overview Hardware Components Required GPS Module Integration with ESP32 Cloud Platform Setup Real-Time Tracking Dashboard... Continue reading
Svg%3E
Read more

IoT Aquaponics Controller: Fish and Plant Automation

April 1, 2026 0
Table of Contents The Water Monitoring Challenge in India Sensor Technologies for Water Building the Sensor Node Data Transmission and... Continue reading
Svg%3E
Read more

IoT Composting Monitor: Temperature and Moisture Tracking

April 1, 2026 0
Table of Contents Why Temperature Monitoring Matters Sensor Selection Guide Hardware Assembly and Wiring Firmware Development Cloud Data Logging Alert... Continue reading
Svg%3E
Read more

IoT Beehive Monitor: Weight, Temperature, and Humidity

April 1, 2026 0
Table of Contents Why Monitor Beehives Weight Measurement System Temperature and Humidity Sensing Building the Monitor Data Analysis for Bee... Continue reading

Add comment Cancel reply

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

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now