I2C vs SPI vs UART: Choosing the Right Communication Protocol
Free Daily Electronics Newsletter
Tutorials, news, and one component explained simply — every day.

Difficulty: Intermediate — You’ve got your Arduino talking to one sensor and everything works great. Now you want to add a display, an SD card, and another sensor — and suddenly you’re staring at three different protocol options wondering which one to pick. Sound familiar?
Here’s the thing: I2C, SPI, and UART aren’t competing standards. They’re different tools for different jobs, and once you understand what each one is good at, the choice almost makes itself.
UART: The Simple Two-Wire Workhorse
UART (Universal Asynchronous Receiver/Transmitter) is the oldest and simplest of the three. It uses just two wires — TX (transmit) and RX (receive) — to send data between exactly two devices. No clock signal, no addressing, no bus sharing. Just two devices having a direct conversation.
UART uses just two wires (TX and RX) for direct communication between two devices.
The key word is asynchronous. Both devices need to agree on a speed (baud rate) beforehand — typically 9600 or 115200 bits per second. If they disagree, you get garbage data. It’s like two people trying to talk at different speeds.
When to use UART:
- GPS modules (they almost always use UART)
- Bluetooth modules like the HC-05
- Debugging — the Serial Monitor in the Arduino IDE uses UART
- Any time you’re connecting exactly two devices with a simple data stream
UART’s limitations: Only two devices can talk. If you need a third, you need another UART port. Most Arduinos only have one hardware UART (though you can use SoftwareSerial for more).
I2C: The Multi-Device Network
I2C (Inter-Integrated Circuit, pronounced “I-squared-C”) solves UART’s biggest limitation. It uses just two wires — SDA (data) and SCL (clock) — but can connect up to 127 devices on the same bus. Each device has a unique 7-bit address, and the master (your Arduino) calls each one by its address when it wants to talk.
I2C connects multiple devices on a shared two-wire bus. Each device has a unique address.
Think of it like a classroom. The teacher (master) asks a question to a specific student (address 0x48), and only that student responds. Everyone else stays quiet. This makes I2C perfect for connecting multiple sensors, displays, and memory chips with minimal wiring.
When to use I2C:
- OLED displays (most use address 0x3C)
- Temperature/humidity sensors like the BME280 or SHT31
- Real-time clocks (DS3231)
- EEPROM memory chips
- Any time you need multiple devices and have limited pins
I2C’s limitations: It’s relatively slow (100-400 kHz typically), and the shared bus means only one device can talk at a time. Long wire runs can cause problems — keep I2C cables under 1 meter for reliable operation.
One gotcha: I2C requires pull-up resistors on both SDA and SCL lines. Many breakout boards include these already, but if you’re connecting multiple boards, you might end up with too many pull-ups in parallel — which can cause communication failures.
SPI: The Speed Demon
SPI (Serial Peripheral Interface) is the fastest of the three, capable of clock speeds up to 50 MHz or more. It uses four wires: MOSI (Master Out Slave In), MISO (Master In Slave Out), SCK (clock), and CS (Chip Select). Each additional device needs its own CS line.
SPI uses separate chip select (CS) lines to choose which device to talk to. More wires, but much faster.
SPI is full-duplex — data can flow in both directions simultaneously. There’s no addressing scheme; instead, the master pulls the CS line low for whichever device it wants to talk to. This makes the protocol simpler and faster, but it costs an extra pin per device.
When to use SPI:
- SD cards and microSD modules
- TFT displays and fast graphical screens
- High-speed ADCs and DACs
- Radio modules like the nRF24L01
- Any time speed matters more than pin count
SPI’s limitations: Every device needs its own CS pin, so connecting many devices eats through your available pins quickly. There’s also no built-in error checking or acknowledgment — you’re trusting that the data arrived correctly.
The Decision Cheat Sheet
Here’s how to choose in practice:
- Only two devices talking? → UART (simplest)
- Multiple slow sensors on limited pins? → I2C (most flexible)
- Need speed (displays, SD cards, radio)? → SPI (fastest)
- Long distance between boards? → UART (most tolerant of wire length)
In many real projects, you’ll use all three. A typical Arduino sensor project might use I2C for a temperature sensor, SPI for an SD card to log data, and UART for a Bluetooth module to send it to your phone. The LED cube project uses SPI-style shift registers for speed, while the fingerprint door lock uses UART for the sensor module.
Common Pitfalls and How to Avoid Them
I2C address conflicts: Two devices with the same address can’t share a bus. Check datasheets before buying. Some sensors have configurable addresses (usually via a solder jumper).
UART baud rate mismatch: If your Serial Monitor shows garbage characters, the baud rate is wrong. Double-check both sides.
SPI mode confusion: SPI has four modes (0-3) based on clock polarity and phase. Most devices use Mode 0, but always check the datasheet. Using the wrong mode gives corrupted data.
Missing pull-ups on I2C: If your I2C device isn’t responding, the first thing to check is whether you have pull-up resistors. Use a logic analyzer or oscilloscope to verify the signals look clean.
Recommended Tools & Parts
Components to experiment with all three protocols (affiliate links):
- Arduino Uno R3
- USB Logic Analyzer
- I2C OLED Display
- BME280 I2C Sensor Module
- SD Card Module (SPI)
- nRF24L01 Radio Module (SPI)
- HC-05 Bluetooth Module (UART)
Free Daily Electronics Newsletter
Tutorials, news, and one component explained simply — every day.
