1. Key Points of UART Hardware Resources and Configuration
-
UART Resources of ESP32ESP32 supports 3 UART controllers (UART0, UART1, UART2), but actual availability is limited by pin conflicts:
- UART0: Default for REPL debugging (flashing and serial monitoring), using GPIO1 (TX), GPIO3 (RX)
- UART1: Some development boards connect to external Flash/SD cards (conflicts should be avoided), default GPIO9 (TX), GPIO10 (RX)
- UART2: Recommended for use, can freely map pins
Pin Mapping Flexibility Customizable pin mapping through <span>tx</span> and <span>rx</span> parameters
from machine import UART
uart = UART(2, baudrate=115200, tx=17, rx=16) # Map UART2 to GPIO16 (RX), GPIO17 (TX)

2. Detailed Explanation of Core Functions
1. Constructor
<span>UART(id, baudrate, bits, parity, rx, tx, stop, timeout)</span>
| Parameter | Description | Common Values |
|---|---|---|
<span>id</span> |
Serial port number | 1 (UART1), 2 (UART2) |
<span>baudrate</span> |
Baud rate | 9600, 115200 (default) |
<span>bits</span> |
Data bit length | 8 (default), 7, 9 |
<span>parity</span> |
Parity method | None (default), 0 (even), 1 (odd) |
<span>rx/tx</span> |
Receive/Send GPIO pin number | e.g., 16, 17 |
<span>stop</span> |
Stop bits | 1 (default), 2 |
<span>timeout</span> |
Read timeout (milliseconds) | 10 (default) |
Function Introduction
2. Data Read and Write Methods
| Function | Function Description | Example |
|---|---|---|
<span>uart.read(n)</span> |
Read n bytes, returns<span>bytes</span> object |
<span>data = uart.read(10)</span> |
<span>uart.readline()</span> |
Read a line (ends with<span>\n</span>) |
<span>line = uart.readline()</span> |
<span>uart.readinto(buf)</span> |
Read data into buffer | <span>buf = bytearray(10); uart.readinto(buf)</span> |
<span>uart.write(buf)</span> |
Send byte data | <span>uart.write(b'ABC')</span> |
<span>uart.any()</span> |
Returns the number of bytes available to read | <span>if uart.any(): ...</span> |
<span>uart.deinit()</span> |
Close UART and release resources | <span>uart.deinit()</span> |

<span>uart.any()</span>: Returns an integer that counts the number of characters that can be read without blocking. If no characters are available, it returns 0; if there are characters, it returns a positive number. Even if multiple characters are available, this method may return 1.
3. Test Code Example
1. Testing
from machine import UART, Pin
import time
# Initialize UART communication interface
# Use UART2 (ESP32 supports UART0/1/2)
# Set baud rate to 115200 (bit rate for data transmission speed)
# Specify GPIO16 as the send pin (TX), GPIO17 as the receive pin (RX)
uart = UART(2, baudrate=115200, tx=16, rx=17)
# Loopback test function: send back the received data as is
def echo_test():
print("UART loopback test started. Type something and press Enter to send.")
while True:
# Check if there is data to read
if uart.any():
# Read all available byte data
data = uart.read()
# Print the received data in REPL (for debugging)
print(f"Received: {data}")
# Send the received data back (loopback function)
uart.write(data)
if data.decode() == "end":
break;
# Short delay to avoid high CPU usage
time.sleep(0.1)
# Automatic test function: send preset data and verify reception, short-circuit for send/receive
def auto_test():
# Define test data (byte type)
test_data = b'Hello, UART!\r\n'
# Display the sent test data in REPL
print(f"Sending test data: {test_data}")
# Send test data via UART
uart.write(test_data)
# Wait enough time for data transmission and return (0.5 seconds)
time.sleep(0.5)
# Check if there is response data
if uart.any():
# Read response data
received = uart.read()
# Display the received data in REPL
print(f"Received: {received}")
# Verify if the received data matches the sent data
if received == test_data:
print("Test passed!") # Test success message
else:
print("Test failed!") # Test failure message
else:
print("No response received!") # No response message
# Execute automatic test (run once at program startup)
auto_test()
# Start loopback test (enter infinite loop after automatic test)
echo_test()
Wiring Instructions: Connect GPIO16 and GPIO17 of the ESP32 with Dupont wires.

2. Reading Modbus RTU Devices (Temperature and Humidity Sensor)
from machine import UART
import ustruct # Structure parsing library for parsing binary data
# Initialize UART2 interface, connected to RS485 module
# Baud rate 9600 (matching the sensor)
# TX pin GPIO16 (sending data)
# RX pin GPIO17 (receiving data)
uart = UART(2, baudrate=9600, tx=16, rx=17)
# Modbus RTU protocol request frame
# [Slave address, Function code, Start register address high byte, Start register address low byte, Register count high byte, Register count low byte, CRC check low byte, CRC check high byte]
modbus_cmd = bytes([0x02, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x38])
# Function to read temperature and humidity sensor data
def read_sensor():
uart.write(modbus_cmd) # Send Modbus request
utime.sleep_ms(200) # Wait for sensor response (200 milliseconds)
# Check if enough bytes are received (complete response at least 7 bytes)
if uart.any() >= 7:
res = uart.read(7) # Read 7 bytes of response data
# Parse temperature value (register 0)
# res[3:5] is temperature data (2 bytes) temperature value at index 3-4
# '>h' indicates big-endian signed short (16 bits)
# Dividing by 10.0 because the sensor precision is 0.1°C
temp = ustruct.unpack('>h', res[3:5])[0] / 10.0
# Parse humidity value (register 1)
# res[5:7] is humidity data (2 bytes) humidity value at index 5-6
hum = ustruct.unpack('>h', res[5:7])[0] / 10.0
return temp, hum # Return temperature and humidity tuple
return None # Return None if insufficient data received
# Main loop: read sensor data every 2 seconds
while True:
result = read_sensor() # Call read function
if result: # If successfully read data
# Format output temperature and humidity values
print(f"Temperature: {result[0]}°C, Humidity: {result[1]}%")
utime.sleep(2) # Wait 2 seconds before reading again
4. Common Problem Solutions
-
Incomplete data reception (single character not string)Reason:
<span>read()</span><span> defaults to reading 1 byte.</span><strong><span>Solution</span></strong><span>: Use </span><code><span>read(n)</span><span> to specify length or </span><code><span>readline()</span><span> to read a line:</span>data = uart.readline() # Read a whole line -
UART resource conflict error
E (214129) uart: UART driver already installedSolution: Call
<span>uart.deinit()</span><span> to release resources before reusing.</span> -
End character recognition failureReason: Serial screen requires binary end character.Correct usage:
uart.write(b"n0.val=100\xff\xff\xff") # Correct: binary send uart.write("n0.val=100\xff\xff\xff") # Incorrect: text escape invalid -
Pin conflict suggestions
- Avoid using UART0 (debug occupied)
- Default pins GPIO9/10 of UART1 may connect to Flash, recommended to remap:
uart = UART(1, tx=17, rx=16) # Remap to GPIO16/17
- Flow control: RTS/CTS must be enabled when setting
<span>rtx</span>and<span>cts</span>parameters- Large data volume: Increase
<span>timeout</span>or use<span>readinto()</span><span> to avoid data loss</span>- Multiple devices: RS485 requires enabling pin control for send/receive direction (requires additional GPIO)
References
http://www.micropython.com.cn/en/latet/esp32/quickref.html#uart-serial-bus
https://docs.micropython.org/en/latest/library/machine.UART.html
https://developer.quectel.com/doc/quecpython/API_reference/zh/peripherals/machine.UART.html