55  UART and RS-232 Serial Communication

Key Concepts
  • UART (Universal Asynchronous Receiver/Transmitter): An asynchronous serial communication interface using a start bit, data bits, optional parity bit, and stop bit(s) to frame each character
  • Baud Rate: The number of symbols per second; for UART, equals the bit rate (one bit per symbol); common rates: 9600, 115200, 460800 bps
  • RS-232: An electrical standard for UART using ±12 V signalling; designed for point-to-point distances up to ~15 m
  • RS-485: A differential signalling standard for UART that supports multi-drop buses (up to 32 devices) over distances up to 1200 m
  • Start/Stop Bits: Frame delimiters in UART: the line idles high; a start bit (low) signals the beginning of a byte; one or two stop bits (high) end the frame
  • Parity Bit: An optional error-detection bit appended to each character; even parity counts total 1s to be even, odd parity to be odd
  • Flow Control: Mechanisms (RTS/CTS hardware flow control, or XON/XOFF software flow control) to prevent buffer overflow when the receiver is slower than the sender

55.1 In 60 Seconds

UART (Universal Asynchronous Receiver/Transmitter) is a point-to-point asynchronous serial protocol using just TX, RX, and GND wires — no clock line required. Both devices must agree on baud rate (commonly 9600 or 115200), data bits, parity, and stop bits. RS-232 defines the electrical standard with ±12V signaling. UART is widely used for GPS modules, serial debugging consoles, industrial equipment, and legacy systems, though its lack of a shared clock makes baud rate mismatch the most common debugging issue.

55.2 Learning Objectives

By the end of this section, you will be able to:

  • Explain RS-232 Protocol: Describe the history, voltage levels, and inverted logic characteristics that distinguish RS-232 from TTL UART
  • Configure UART Parameters: Select and apply baud rate, data bits, parity, and stop bits correctly for a given application
  • Connect Serial Devices: Wire TX, RX, and GND between devices and identify when a level shifter is required
  • Diagnose Serial Faults: Identify root causes of common problems such as baud rate mismatch, clock accuracy failures, and voltage level incompatibility
  • Implement UART Communication: Build and test UART communication code on Arduino/ESP32 using hardware and software serial ports
  • Evaluate Baud Rate Trade-offs: Justify baud rate selection by comparing distance, clock accuracy, throughput, and EMI requirements

UART is one of the oldest and simplest ways for two devices to communicate — it is like two tin cans connected by a string. One device sends data on a wire while the other listens, and vice versa. Many IoT projects use UART to connect a microcontroller to a GPS module, display, or computer for debugging.

“UART is the simplest protocol of all!” said Max the Microcontroller. “Just three wires: TX for sending, RX for receiving, and GND for ground. No clock wire needed — both sides just agree on the speed beforehand.”

“That speed is called the baud rate,” explained Sammy the Sensor. “Common ones are 9600 and 115200. If I talk at 9600 baud and Max listens at 115200, all he hears is garbage! Baud rate mismatch is the number one debugging headache with UART.”

Lila the LED added, “The trick with UART is it only connects TWO devices. No sharing like I2C’s 127-device bus. But that simplicity is an advantage — there is no addressing, no arbitration, no pull-up resistors. Just wire TX to RX, RX to TX, and start talking.”

“I use UART all the time for my serial debug console,” said Bella the Battery. “When something goes wrong in my IoT project, I print messages through UART to a computer screen. GPS modules speak UART, Bluetooth modules speak UART, and most microcontrollers have multiple UART ports. It has been around since the 1960s and it is still going strong because simple works!”

55.3 Prerequisites

Before diving into this chapter, you should be familiar with:

  • Wired Communication Fundamentals: Understanding of synchronous vs asynchronous, point-to-point topology
  • Binary and Digital Logic: Understanding binary numbers and voltage levels (HIGH/LOW)

55.4 Introduction

When your ESP32 sends debug messages to your laptop, when a GPS module transmits location data, when a factory sensor reports temperature to a PLC — they are almost certainly using UART. It is the default “I need to send some bytes” protocol of the embedded world.

UART works by turning a byte into a sequence of voltage pulses on a single wire: a start pulse, eight data pulses (or five to nine), an optional parity check pulse, and a stop pulse. The receiver independently times each pulse using its own clock. No clock wire is needed, which is what makes UART so cheap to implement — just two wires and agreement on timing.

RS-232 is the electrical standard that defined how those voltage pulses should look when connecting a computer (DTE, Data Terminal Equipment) to a modem (DCE, Data Communication Equipment) in the 1960s. It specified +12V and −12V levels, a DB9 connector, and handshaking signals like RTS/CTS. Modern microcontrollers use TTL UART (0V/3.3V or 0V/5V) which carries the same protocol but at logic-level voltages — a MAX232 chip converts between the two when connecting legacy RS-232 equipment.

This chapter covers both: the RS-232 electrical standard and the UART framing protocol that runs on top of it.


55.5 RS-232: Serial Communication Overview

RS-232 (Recommended Standard 232) is one of the oldest communication protocols, introduced in the 1960s to connect computers with modems.

Modern usage:

  • GPS modules
  • Serial debugging/console
  • Industrial equipment
  • Legacy systems

55.5.1 Characteristics

Feature Value
Wires 3 minimum (TX, RX, GND), up to 9 for full implementation
Topology Point-to-point
Sync/Async Asynchronous
Duplex Full-duplex (separate TX/RX wires)
Speed 300 bps - 115,200 bps (typical)
Distance Up to 15 meters
Voltage ±3V to ±25V (typically ±12V)
RS-232 voltage level diagram showing logic 0 represented by positive 3V to 15V and logic 1 by negative 3V to 15V, with a transition region between negative 3V and positive 3V providing noise immunity
Figure 55.1: RS-232 signal voltage levels showing ±3V to ±15V logic levels

55.6 Voltage Levels

RS-232 uses inverted voltage logic:

Logic Level Voltage Range Binary Value
Logic 1 (Mark) -3V to -25V 1
Logic 0 (Space) +3V to +25V 0
RS-232 connection diagram showing Data Terminal Equipment DTE connected to Data Communication Equipment DCE with transmit, receive, and handshake signal lines
Figure 55.2: RS-232 connection diagram showing DTE and DCE devices
RS-232 DB9 connector pinout diagram showing pin assignments: pin 2 RX, pin 3 TX, pin 5 GND, pins 4/6/7/8 for handshaking signals RTS/CTS/DSR/DTR
Figure 55.3: RS-232 cable wiring showing pin connections for DB9 connector

Important: This is opposite of TTL logic (0V = 0, +5V = 1)!

Most modern devices use TTL UART, requiring level shifter (MAX232) for true RS-232.


55.7 RS-232 Connectors

DB9 Connector Pinout:

  5  4  3  2  1
   9  8  7  6

Pin 2: RX (Receive Data)
Pin 3: TX (Transmit Data)
Pin 5: GND (Ground)

Common signals:

Pin Signal Direction Purpose
1 DCD Input Data Carrier Detect
2 RX Input Receive Data
3 TX Output Transmit Data
4 DTR Output Data Terminal Ready
5 GND - Signal Ground
6 DSR Input Data Set Ready
7 RTS Output Request To Send
8 CTS Input Clear To Send
9 RI Input Ring Indicator

Minimum connection: TX, RX, GND (3 wires)


55.8 Data Frame Structure

Start  Data Bits (5-9)      Parity  Stop
 Bit   D0 D1 D2 D3 D4 D5 D6 D7  Bit   Bit(s)
  |    <- Transmitted LSB first ->  |     |
 [0] [1][0][1][1][0][0][1][0]   [0]   [1]

Idle: High (Mark = 1)

Frame components:

  1. Start bit: 0 (space) - signals beginning of byte
  2. Data bits: 5-9 bits (usually 8), LSB first
  3. Parity bit: Optional error checking (even, odd, or none)
  4. Stop bit(s): 1 or 2 bits (mark = 1) - signals end of byte

Common configurations:

  • 8N1: 8 data bits, No parity, 1 stop bit (most common)
  • 8E1: 8 data bits, Even parity, 1 stop bit
  • 7E1: 7 data bits, Even parity, 1 stop bit (legacy)


55.9 Baud Rate

Baud rate = Speed of communication in bits per second

Common baud rates:

  • 9600 bps (common for low-speed sensors)
  • 115200 bps (common for debugging, GPS modules)

Both devices must use the SAME baud rate!

UART’s baud rate tolerance comes from the cumulative timing error over one data frame.

For 8N1 format (1 start + 8 data + 1 stop = 10 bits), the receiver resyncs on the falling edge of the start bit, then samples each subsequent bit at 1.5, 2.5, 3.5 … 9.5 bit periods. At the last sample (9.5 bit periods), the cumulative clock drift must stay within ±50% of one bit period for the sample to land inside the correct bit:

\[t_{error} = 9.5 \times T_{bit} \times \epsilon_{combined}\]

\[9.5 \times T_{bit} \times \epsilon_{combined} < 0.5 \times T_{bit}\]

\[\epsilon_{combined} < \frac{0.5}{9.5} \approx 5.26\%\]

The UART specification conservatively sets the practical limit at 3.75% combined (i.e., each device’s clock error should be ≤1.875%) to provide margin against jitter, cable delays, and rise-time imperfections. The 5% figure is the theoretical maximum; in practice, exceeding 3.75% leads to intermittent failures.

For example, at 115200 baud with 2% combined clock error: - Bit period: \(T_{bit} = \frac{1}{115200} = 8.68\) μs - Cumulative error at 9.5 bits: \(9.5 \times 8.68 \times 0.02 = 1.65\) μs - Error as fraction of bit: \(\frac{1.65}{8.68} = 19\%\) of one bit period

This 19% is well below the 50% theoretical limit, so communication works. At 921600 baud with the same 2% clock error, the percentage of a bit period drifted is identical (19%) because clock error is proportional. What makes high baud rates risky is the absolute net margin: at 115200 baud, the available sampling window is ±4.34 μs and the 19% drift consumes 1.65 μs of it, leaving 2.69 μs net margin. At 921600 baud, the sampling window is only ±0.543 μs and the same 19% drift (0.206 μs) leaves only 0.34 μs net margin — a margin that any cable capacitance, connector contact resistance, or EMI pulse can erase.


55.10 Arduino/ESP32 Example

// UART Serial Communication Example

void setup() {
  // Initialize Serial (USB UART)
  Serial.begin(115200);

  // Initialize Serial2 (Hardware UART on ESP32)
  // RX = GPIO 16, TX = GPIO 17
  Serial2.begin(9600, SERIAL_8N1, 16, 17);

  Serial.println("UART Communication Ready");
}

void loop() {
  // Send data to Serial2
  Serial2.println("Hello from ESP32!");

  // Check if data available from Serial2
  if (Serial2.available()) {
    String received = Serial2.readStringUntil('\n');
    Serial.print("Received: ");
    Serial.println(received);
  }

  delay(1000);
}

Wiring:

ESP32 TX (GPIO 17) -> Device RX
ESP32 RX (GPIO 16) -> Device TX
ESP32 GND          -> Device GND

Common use cases:

  • GPS module communication
  • Bluetooth module (HC-05/HC-06)
  • Serial debugging
  • Industrial sensor protocols (Modbus RTU)

Practical Tips

Choosing Baud Rate:

  • GPS modules: 9600 bps (NMEA standard)
  • Debug console: 115200 bps (fast enough, widely compatible)
  • Bluetooth modules: 9600 bps (HC-05/HC-06 factory default; reconfigurable to 38400–115200)
  • Industrial sensors: 19200 bps (Modbus RTU common)

Common Errors:

  • Garbled data: Baud rate mismatch (check both TX and RX are set identically)
  • Works at 9600, fails at 115200: Low-cost RC oscillator with high error percentage (~10%); 9600 “working” is deceptive — lower baud does not fix a clock problem. Add an external crystal
  • Intermittent errors: Voltage level mismatch (3.3V TX driving 5V input has <0.3V noise margin; add level shifter)

Optimization:

  • Use 8N1 for maximum efficiency (80% = 8/10 bits vs 73% = 8/11 bits for 8E1)
  • Higher baud rates = lower latency but need better crystal accuracy
  • Cable length limits speed: <15m @ 115200 bps, longer cables need lower rates

55.11 Python Serial Communication

Python’s pyserial library is the standard tool for UART communication from a PC or Raspberry Pi.

55.11.1 Reading GPS NMEA Data

# pip install pyserial
import serial
import time

# Open serial port (GPS module connected via USB-UART adapter)
# Common ports: /dev/ttyUSB0 (Linux), /dev/tty.usbserial (macOS), COM3 (Windows)
gps = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,        # GPS NMEA standard baud rate
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    timeout=2.0           # Read timeout in seconds
)

print(f"Connected to {gps.name} at {gps.baudrate} baud")

# Read NMEA sentences
while True:
    line = gps.readline().decode('ascii', errors='replace').strip()
    if line.startswith('$GPGGA'):
        # $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,...
        parts = line.split(',')
        time_utc = parts[1]      # 123519 = 12:35:19 UTC
        lat = parts[2]           # 4807.038 = 48 deg 07.038 min
        lon = parts[4]           # 01131.000 = 11 deg 31.000 min
        satellites = parts[7]    # Number of satellites
        print(f"Time: {time_utc}, Lat: {lat}, Lon: {lon}, Sats: {satellites}")

# What to observe:
# - GPS outputs one NMEA sentence per second at 9600 baud
# - $GPGGA contains position fix data
# - If you see garbage characters: baud rate mismatch (try 4800 or 115200)
# - readline() blocks until \n received or timeout expires

55.11.2 ESP32-to-PC Bridge (Bidirectional)

import serial
import threading

# Connect to ESP32 via USB
esp = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)

def reader():
    """Background thread: print everything ESP32 sends."""
    while True:
        data = esp.readline()
        if data:
            print(f"ESP32: {data.decode().strip()}")

# Start reader in background
threading.Thread(target=reader, daemon=True).start()

# Send commands to ESP32
while True:
    cmd = input("Send to ESP32: ")
    esp.write(f"{cmd}\n".encode())
    # ESP32 receives on Serial.read() and can respond

55.11.3 Modbus RTU: Industrial Sensor Reading

# pip install pymodbus
from pymodbus.client import ModbusSerialClient

# Modbus RTU runs over UART at 19200 baud (common for industrial sensors)
client = ModbusSerialClient(
    port='/dev/ttyUSB0',
    baudrate=19200,
    parity='E',           # Even parity (Modbus standard)
    stopbits=1,
    bytesize=8,
    timeout=1
)

client.connect()

# Read temperature from Modbus device at address 1, register 0
result = client.read_holding_registers(address=0, count=1, slave=1)
if not result.isError():
    raw = result.registers[0]
    temperature = raw / 10.0  # Many sensors use 0.1 degree resolution
    print(f"Temperature: {temperature}C")
else:
    print(f"Read error: {result}")

client.close()

# What to observe:
# - Modbus RTU uses 8E1 (even parity), not 8N1
# - Device address (slave=1) must match sensor's DIP switch setting
# - Register addresses vary by manufacturer -- check the datasheet
# - If timeout: check wiring (TX/RX swap is the #1 mistake)

55.11.4 Throughput Calculation

Try It: UART Throughput Calculator

55.12 Knowledge Check: UART Clock Accuracy

Scenario: A UART connection works correctly at 9600 baud but fails completely at 115200 baud with garbled data. Both devices use 8N1 configuration.

Think about:

  1. Why would higher baud rates fail when lower baud rates work?
  2. What is the relationship between clock accuracy and bit timing?

Key Insight: UART is asynchronous (no clock signal), so TX and RX must independently time each bit using their internal clocks.

The Math:

  • At 9600 baud (104 μs/bit), 2% combined clock error = 2.08 μs/bit × 9.5 bits = 19.8 μs total drift = 19% of one bit period — within the 50% theoretical limit
  • At 115200 baud (8.68 μs/bit), 2% error gives the same 19% drift fraction — also within tolerance. However, devices with 10% RC oscillator error give 9.5 × 8.68 × 0.10 = 8.25 μs error = 95% of a bit period — completely unreliable

Solution: Use precision crystals (<±0.5% per device, <±1% combined) or internal RC oscillators with factory calibration to UART-standard frequencies. This is why UART has standardized baud rates (9600, 115200, etc.) — they align with common crystal frequencies (16 MHz, 8 MHz) whose integer dividers produce these exact rates.

Scenario: An ESP32 communicates with an Arduino via UART at 115200 baud. The ESP32 runs at 3.3V logic, Arduino at 5V. Direct connection works sometimes but has intermittent errors.

Think about:

  1. What happens when a 3.3V signal is sent to a 5V input?
  2. What happens when a 5V signal is sent to a 3.3V input?

Key Insight: The problem is voltage level incompatibility.

  • ESP32 TX outputs 3.3V HIGH, but Arduino (5V ATmega328P) Vih min = 0.6 × Vcc = 3.0V per the datasheet. At 3.3V the signal nominally clears the threshold, but the noise margin is only 0.3V — any cable capacitance, ground offset, or load brings it below 3.0V
  • At high baud rates (115200), fast transitions into a capacitive input further degrade the received voltage, causing intermittent bit errors

Solution:

  1. Use bi-directional level shifter (TXS0108E) for safe conversion in both directions
  2. Voltage divider (2 kΩ + 1 kΩ) on Arduino TX → ESP32 RX only (drops 5V to 3.3V; the ESP32 → Arduino direction is marginal but not destructive)

Direction matters: Arduino TX at 5V driving ESP32 RX can permanently damage the ESP32 (absolute max is 3.6V). Never directly connect 5V TX to a 3.3V-rated RX pin without protection.


55.13 Quiz: UART and RS-232


55.14 Real-World Deployment: Industrial UART at Scale

55.14.1 Modbus RTU in Oil and Gas: Chevron’s Richmond Refinery

Chevron’s Richmond, California refinery operates 4,200 Modbus RTU devices connected via RS-485 (electrically based on RS-232 signaling but with differential voltage for multi-drop networks). The Modbus RTU protocol — the most widely deployed UART-based industrial protocol — runs at 19200 baud with 8E1 framing across 380 instrument loops, each up to 1,200 meters.

Why UART/Modbus persists in a Wi-Fi world:

Factor UART/Modbus RTU Modern alternatives (Ethernet/IP, Wi-Fi)
Installation cost per point $45 (2-wire RS-485) $180 (Ethernet) / $250 (industrial Wi-Fi)
Latency determinism 3.5 character times (1.8 ms at 19200) guaranteed 1-50 ms variable (Ethernet CSMA/CD)
EMI immunity Differential RS-485 rejects common-mode noise up to +12V Ethernet susceptible to ground loops, requires shielding
Cable distance 1,200 m at 19200 baud (RS-485) 100 m (Ethernet), 50 m typical (Wi-Fi in metal structures)
Intrinsic safety certification IS-rated RS-485 transceivers available since 1990s Limited certified Ethernet options for Zone 1/2
Mean time between failure 25+ years (no active components in cable) 7-10 years (switches, access points)

Chevron’s instrumentation engineering team evaluated replacing Modbus with Ethernet/IP in 2019. The projected cost for the Richmond refinery alone was $18.7M including new cable runs, Ethernet switches rated for Class I Division 2, and recertification of all safety instrumented systems. They opted instead to add Modbus-to-OPC-UA gateways at each control room ($340K total), preserving the existing 4,200-device wired infrastructure while enabling modern IT integration.

Baud rate selection matters more than you think:

The refinery originally standardized on 9600 baud in 1998. When a new gas chromatograph requiring 200-byte readings was added in 2012, the poll cycle for its 32-device loop exceeded the 1-second requirement:

At 9600 baud, 8E1 (11 bits/byte):
  Query frame: 8 bytes x 11 bits = 88 bits / 9600 = 9.2 ms
  Response frame: 200 bytes x 11 bits = 2200 bits / 9600 = 229 ms
  Inter-frame gap: 3.5 char = 4.0 ms
  Total per device: 242 ms
  32 devices: 7,744 ms = 7.7 seconds (exceeds 1-second target)

Upgrade to 19200 baud:
  Total per device: 121 ms
  32 devices: 3,872 ms = 3.9 seconds (still too slow)

Solution: Split into two 16-device loops at 19200 baud:
  16 devices: 1,936 ms per loop (both loops polled in parallel)
  Effective cycle: 1.9 seconds

The key lesson: at 19200 baud with 8E1 framing, the raw byte rate is 19200/11 = 1,745 bytes/second — already 9% lower than the 1,920 B/s you would get with 8N1. On top of that, Modbus requires a 3.5-character silent gap between every frame (an additional ~4 ms pause at 19200 baud). For short messages this gap can represent 20-30% of total transaction time. Both overheads compound and are often overlooked in capacity planning.

55.14.2 Cost Decision Framework: When to Use UART vs. Modern Protocols

Criteria Stay with UART/RS-485 Migrate to Ethernet/Wi-Fi
Device count per loop < 32 (Modbus limit) > 32 or growing rapidly
Data per device < 250 bytes/poll > 250 bytes or streaming
Cable runs Already installed, < 1.2 km New installation, < 100 m
Update rate needed > 500 ms acceptable < 10 ms required
Environment Explosive atmosphere (Zone 1/2) Office/clean room
Budget < $50/point available > $150/point available
IT integration needed Gateway bridge acceptable Direct IP connectivity required

55.15 Baud Rate Selection Guide for IoT Applications

Baud rate selection is more nuanced than “pick the fastest.” Higher baud rates reduce transfer time but increase sensitivity to clock accuracy, cable quality, and electromagnetic interference. For IoT devices, the trade-offs are quantifiable.

55.15.1 Common Baud Rates and Their Sweet Spots

Baud Rate Effective Byte Rate (8N1) Max Reliable Distance (RS-485) Typical Use Case
9,600 960 bytes/sec 1,200 m Modbus RTU sensors, building automation
19,200 1,920 bytes/sec 1,000 m Industrial telemetry, flow meters
38,400 3,840 bytes/sec 500 m Barcode scanners, weigh scales
57,600 5,760 bytes/sec 300 m GPS modules (NMEA output)
115,200 11,520 bytes/sec 100 m Debug consoles, MCU-to-MCU
460,800 46,080 bytes/sec 15 m High-speed logging, camera triggers
921,600 92,160 bytes/sec 5 m Same-board communication only

55.15.2 Clock Accuracy Requirements

UART has no clock line — both sides must generate the baud rate independently. If their clocks drift apart, bits are sampled at wrong times and data is corrupted.

Tolerance rule: The combined clock error of transmitter and receiver must be less than 3.75% for reliable communication (the sampling point must fall within the middle 50% of each bit period).

Example: ESP32 (crystal oscillator, ±0.01% error)
         + ATmega328P internal RC oscillator (±10% rated spec, ~2-4% typical at room temp)
         = 10.01% combined (spec) or ~4% combined (typical at room temp)
         Spec case → WILL FAIL at any baud rate!
         Typical room-temp case → marginal (fails in heat)

Fix: Use external 16 MHz crystal on ATmega328P (±0.005%)
     Combined error: 0.015% → works reliably at all baud rates

Why 9,600 baud appears forgiving: At 9,600 baud, each bit period is 104 microseconds — there is 3.9 μs of absolute timing margin before a 3.75% error corrupts a sample. A marginal clock at ±5% error (above spec) still leaves only 0.4 μs of margin violation, so communication may limp along. At 921,600 baud, each bit period is 1.09 microseconds — the same 3.75% margin is just 41 ns absolute. Any connector delay, cable capacitance, or thermal drift consumes that margin instantly, causing immediate failure. The percentage tolerance is the same at all baud rates; the forgiving behavior at 9,600 comes from having more absolute nanoseconds of margin, not from any fundamentally different tolerance rule.

Design recommendation: For IoT devices connected by cables longer than 30 cm, stick to 115,200 baud or below. For same-board communication (MCU to radio module), higher rates are safe. Always verify with an oscilloscope if using baud rates above 115,200 with cables.

Try It: UART Clock Error Calculator

A factory needs to monitor 25 temperature sensors distributed across 800 meters using Modbus RTU over RS-485.

Given:

  • 25 sensors, each reporting 4 registers (temperature, humidity, pressure, status)
  • Polling interval: 2 seconds per sensor
  • Baud rate options: 9,600 or 19,200
  • RS-485 bus length: 800 meters

Step 1: Calculate time-on-air per sensor query at 9,600 baud

Modbus RTU at 9,600 baud, 8E1 (11 bits/byte):
Query frame: 8 bytes × 11 bits = 88 bits / 9,600 = 9.2 ms
Response frame: 13 bytes × 11 bits = 143 bits / 9,600 = 14.9 ms
Inter-frame gap: 3.5 char × (11/9600) = 4.0 ms
Total per sensor: 9.2 + 14.9 + 4.0 = 28.1 ms

Step 2: Calculate total poll cycle time

25 sensors × 28.1 ms = 702.5 ms per complete cycle

Target: Poll all sensors every 2 seconds
Actual cycle: 0.7 seconds ✓ (well within target)

Step 3: Evaluate faster baud rate (19,200)

At 19,200 baud:
Total per sensor: 14.1 ms (half of 9,600)
25 sensors × 14.1 ms = 352.5 ms per cycle

Step 4: Check distance limitations

At 9,600 baud: Maximum 1,200 m (800 m deployment ✓)
At 19,200 baud: Maximum 1,000 m (800 m deployment ✓)

Step 5: Factor in retries for reliability

Assume 5% packet loss requiring retries:
9,600 baud: 702.5 ms × 1.05 = 737 ms (still <2 sec target ✓)
19,200 baud: 352.5 ms × 1.05 = 370 ms (faster, but...)

Recommendation: Use 9,600 baud because: - Meets 2-second polling requirement with margin (737 ms < 2,000 ms) - More reliable at 800 m distance (lower bit error rate) - Better EMI immunity in industrial environment - Standard rate with widest device compatibility

If requirements change to 1-second polling:

  • 9,600 baud: 737 ms cycle ✓ (still meets target)
  • Only use 19,200 if polling needs to be <500 ms

Key Insight: Don’t choose the fastest baud rate by default. Match baud rate to actual requirements, considering distance, reliability, and environmental factors.

Try It: Modbus RTU Poll Cycle Planner
Application Type Typical Data Size Update Rate Distance Recommended Baud Reasoning
GPS NMEA 80 bytes/sentence 1-5 Hz <3 m 9,600 Industry standard, minimal latency requirement
Modbus RTU sensors 20-200 bytes 0.5-2 Hz 10-1,200 m 9,600 Reliability over speed, long cable runs
Industrial flow meters 50-100 bytes 2-10 Hz 100-800 m 19,200 Faster updates, moderate distance
Debug console Variable On-demand <2 m 115,200 Fast log output, short cable, no reliability concern
Bluetooth module (HC-05) 20-500 bytes 10-100 Hz <1 m 9,600-115,200 Factory default is 9600; reconfigurable via AT commands
MCU-to-MCU (same board) Variable 100+ Hz <30 cm 230,400-921,600 Minimal noise, very short traces
Barcode scanner 10-100 bytes 1-10 scans/min 1-3 m 38,400 Fast enough for human interaction
Legacy SCADA (1990s) 10-50 bytes 0.1-1 Hz 500-1,200 m 9,600 Compatibility with old equipment

Decision Factors:

  1. Distance Priority: >500 m → use 9,600 baud maximum (RS-485)
  2. Throughput Priority: Calculate minimum baud = (bytes × 11 bits/byte × updates/sec) / 0.7 (70% utilization max)
  3. Reliability Priority: EMI-heavy industrial → use lowest baud that meets throughput
  4. Compatibility Priority: Match the slowest device in the chain

Example Calculation (Industrial Temperature Logger):

Requirements:
- 100 bytes per reading (timestamp + 20 sensor values)
- 10 readings/second required
- 250 meter cable run in factory (near motors, VFDs)

Minimum baud calculation:
Throughput = 100 bytes × 11 bits × 10 Hz = 11,000 bps
At 70% utilization: 11,000 / 0.7 = 15,714 bps minimum
Next standard baud: 19,200 bps ✓

Distance check:
19,200 baud at 250 m: within 1,000 m limit ✓

EMI consideration:
Factory environment suggests 9,600 for reliability

Conclusion: Use 19,200 baud (meets throughput + distance) OR
           Use 38,400 with shielded cable (2× margin for EMI immunity)

Trade-off Table:

Factor Choose Lower Baud (9,600) Choose Higher Baud (115,200)
Cable length >200 m <50 m
Environment Noisy (motors, RF) Clean (lab, office)
Clock accuracy Low-cost RC oscillator Crystal oscillator
Throughput need <1 KB/sec >10 KB/sec
Existing infrastructure Legacy 9,600 devices Modern MCU-to-MCU
Common Mistake: Using Maximum Baud Rate Without Considering Clock Tolerance

The Error: “My ESP32 and Arduino both support 115,200 baud, so I’ll use that for maximum speed!”

What Actually Happens:

ESP32: Crystal oscillator, ±0.01% accuracy
Arduino Uno (ATmega328P): Internal RC oscillator, ±10% accuracy (factory)

Combined error: 10.01%

UART tolerance: ±3.75% for reliable communication

Result: 10.01% >> 3.75% → Communication FAILS at 115,200 baud

Symptoms:

  • Works sometimes, fails randomly
  • Errors increase with ambient temperature changes (RC oscillator drifts)
  • Longer messages fail more often (bit errors accumulate)
  • Developer blames “bad wiring” or “faulty hardware”

Real-World Example (Failed Smart Meter Project, 2021):

100 smart meters using ATmega328P (internal RC) communicating with Raspberry Pi gateway at 115,200 baud over RS-485:

Deployment metrics:
- Day 1-30: 12% packet error rate (acceptable with retries)
- Summer (temperature +30°C): 38% error rate (RC drift with heat)
- 40% of meters eventually became unreachable (clock drift >10%)
- $45,000 to replace all meters with crystal-equipped versions

The Fix They Should Have Used:

Option 1: Lower baud rate (no hardware change)

At 9,600 baud, bit period = 104 μs:
- 10% combined clock error × 9.5 bit-periods = 95% of one bit period
- The receiver samples the 9th data bit 95% late → samples adjacent bit
Still fails! Clock error percentage is what matters, not baud rate.
Lower baud rate does NOT fix a clock accuracy problem.

Option 2: Add external crystal to Arduino (correct solution)

16 MHz crystal: ±0.005% accuracy
ESP32 + Arduino crystal: 0.01% + 0.005% = 0.015% combined
✓ Well within 3.75% UART tolerance
✓ Works reliably at 115,200 baud
Cost: $0.50 per crystal + 15 min per unit = $30/unit labor
vs $450/unit to replace entire meter

Option 3: Use 9,600 baud + protocol-level checksums

Even with 10% rated spec clock error (typical units run ~2-4% at room temp):
- Typical units at room temp (~3% actual): 9.5 × 3% = 28.5% of bit period drift
  → within 50% limit, marginal; errors occur occasionally, retransmission helps
- Hot summer (+30°C shifts RC oscillator +3-4%): total ~6-7% actual error
  → 9.5 × 6.5% = 61.75% of bit period → exceeds 50% → sampling outside the bit → fails
- Add CRC-16 to every message (Modbus-style) to detect and retransmit errors

Result: 9,600 baud with retries at room temp = effective ~7,000-8,000 bps
        (unreliable in summer; not a production-quality solution)

Baud Rate vs Clock Accuracy Table:

The maximum combined clock error for reliable UART is 3.75% at any baud rate — this is a fixed spec, not baud-rate dependent. What changes with higher baud rates is that non-clock factors (cable propagation delay, capacitive loading, rise-time distortion) consume an increasing fraction of the shrinking bit period:

Baud Rate Bit Period 3.75% Margin Practical Limit (inc. cable effects) Works With
9,600 104 μs 3.9 μs 3.75% (clock dominates) RC oscillator (calibrated)
19,200 52 μs 1.95 μs 3.75% (clock dominates) RC oscillator (tight calibration) OR crystal
38,400 26 μs 0.975 μs 3.75% Crystal required
115,200 8.68 μs 0.325 μs 3.75% (cable delay eats margin fast) Crystal + cable <15 m
460,800 2.17 μs 0.081 μs 3.75% (10 ns cable delay = 0.5%) Crystal + PCB traces only
921,600 1.09 μs 0.041 μs 3.75% (any connector adds ~1%) Crystal + same-board only

Key Insight: UART clock tolerance is always 3.75% combined — but at high baud rates, the absolute time margin shrinks so much that connector resistance, cable capacitance, and PCB trace inductance each consume a measurable fraction of it. Use a crystal at any baud rate, and stay on-board for rates above 460,800.

Common Pitfalls

A sender at 115200 bps and a receiver at 9600 bps produces garbled data. Fix: always explicitly configure both devices to the same baud rate; never assume the default matches.

RS-232 uses ±12 V; a 3.3 V microcontroller GPIO tolerates 0–5 V. Connecting them directly will permanently damage the microcontroller. Fix: always use a level-shifter (e.g., MAX3232) between RS-232 and 3.3 V TTL-level UART.

Sending continuous high-rate data over UART without flow control causes receiver buffer overflow and data loss. Fix: enable RTS/CTS flow control for streaming applications, or implement software buffering with XON/XOFF.

55.16 Summary

This chapter covered UART and RS-232 serial communication:

  • RS-232 is a legacy point-to-point protocol using inverted voltage levels (±3V to ±25V)
  • UART is the modern TTL-level implementation (0V/3.3V or 0V/5V)
  • Data frames consist of start bit, data bits (usually 8), optional parity, and stop bit(s)
  • 8N1 (8 data bits, No parity, 1 stop bit) is the most common configuration
  • Baud rate must match between sender and receiver; clock accuracy <3.75% combined error required
  • Distance limits baud rate — 9,600 baud reaches 1,200 m, 115,200 reaches 100 m on RS-485
  • Common issues include baud rate mismatch, clock accuracy at high speeds, and voltage level incompatibility

55.17 What’s Next

Topic Chapter Description
Wired Communication Fundamentals Wired Comm Fundamentals Synchronous vs asynchronous, signal lines, and why wired protocols persist in IoT
I2C Protocol I2C Protocol Two-wire multi-device bus enabling up to 127 sensors and displays to share just SDA and SCL
SPI Protocol SPI Protocol Four-wire full-duplex interface for SD cards, displays, and fast ADCs
Wired Communication Overview Wired Communication Side-by-side comparison of UART, I2C, SPI, and Ethernet for IoT projects
Physical Layer and Ethernet Physical Layer — Wired Ethernet How Ethernet frames map onto physical cabling, MAC addressing, and collision domains
Network Fundamentals Networking Fundamentals IP addressing, switching, and routing concepts that sit above the physical serial layer