Skip to content
Embedded

I2C vs SPI vs UART: Choosing the Right Serial Protocol for Embedded Hardware

30 June 20266 min read0 views
I2C vs SPI vs UART: Choosing the Right Serial Protocol for Embedded Hardware
A practical comparison of the three most common embedded serial communication protocols. Learn which to choose for sensors, displays, and microcontroller peripherals.

Why Protocol Selection Matters

Every sensor, display, memory chip, or co-processor you connect to a microcontroller uses one of a handful of serial communication protocols. Choosing the wrong one increases wiring complexity, limits data throughput, or creates timing conflicts. Understanding the trade-offs between I2C, SPI, and UART saves hours of debugging.

Protocol Comparison Overview

| Property | I2C | SPI | UART | | :--- | :--- | :--- | :--- | | Wires | 2 (SDA + SCL) | 4+ (MOSI, MISO, CLK, CS per device) | 2 (TX + RX) | | Max Speed | 400 kHz (Fast) / 3.4 MHz (HS) | 10–80 MHz | Up to 115200 baud (typical) | | Multi-device | Yes, software addressing (127 devices) | Yes, one CS pin per device | No (point-to-point) | | Duplex | Half-duplex | Full-duplex | Full-duplex | | Hardware Complexity | Low | Medium | Very low |

When to Use I2C

I2C uses only 2 wires and supports up to 127 devices on the same bus via 7-bit addresses. It is ideal when:

  • You have limited GPIO pins available
  • Multiple sensors of the same type need addressing (e.g. four BME280 sensors on one bus — though address conflicts require external multiplexers)
  • Throughput requirements are below 400kHz (temperature, pressure, accelerometer readings)
// ESP32 I2C read example
#include <Wire.h>
#define BME280_ADDR 0x76

Wire.begin(SDA_PIN, SCL_PIN);
Wire.beginTransmission(BME280_ADDR);
Wire.write(0xF3); // Status register
Wire.endTransmission();
Wire.requestFrom(BME280_ADDR, 1);
uint8_t status = Wire.read();

When to Use SPI

SPI is the fastest option but requires more wires — one Chip Select (CS) line per peripheral. Use SPI when:

  • You need high throughput (TFT displays, SD card readers, ADCs)
  • Full-duplex simultaneous read/write is required
  • Low-level protocol timing matters (e.g. SPI Mode 0 vs Mode 3)

When to Use UART

UART is the simplest point-to-point protocol. Use it when:

  • Communicating with one dedicated peripheral (GPS module, GSM modem, fingerprint scanner)
  • Debugging via serial monitor
  • Bridging two microcontrollers across longer cable runs

Practical Rule of Thumb

  • Many slow sensors on few pins → I2C
  • High-speed single peripherals (displays, SD cards) → SPI
  • One external module with its own protocol → UART

Designing custom hardware with multiple peripherals? Let's talk →

Frequently Asked Questions

Q:Can I use both I2C and SPI on the same ESP32?

Yes. The ESP32 supports two independent I2C buses and three SPI buses simultaneously. You can mix protocols freely across different GPIO pins.

Q:What causes I2C bus lockup and how do I fix it?

I2C lockup usually happens when a slave device holds SDA low during a failed transaction. Send 9 clock pulses on SCL manually, then issue a STOP condition. Many I2C libraries have a bus recovery function for this.

Working on something similar?

Let's collaborate to design custom PCB schematics, write deterministic FreeRTOS threads, or configure secure Next.js databases.

Let's talk →
FyraAsk anything