Reference

Communication Protocols Compared: I2C vs SPI vs UART vs CAN

The first time you wire a sensor to a microcontroller, you accidentally pick the wrong protocol and waste two hours wondering why nothing works. Each of these protocols has a specific shape of problem it solves well and a specific shape of problem it solves badly. This article is the reference card we wish we had on day one.

The protocols at a glance

flowchart LR subgraph I2C [I2C - 2 wires] M1[Master] ---|SDA| Bus1[SDA + SCL] M1 ---|SCL| Bus1 Bus1 --- S1[Slave 1 addr 0x40] Bus1 --- S2[Slave 2 addr 0x76] end subgraph SPI [SPI - 4 wires + N CS] M2[Master] ---|MOSI MISO SCK| SS1[Slave 1] M2 ---|CS1| SS1 M2 ---|CS2| SS2[Slave 2] end subgraph UART [UART - 2 wires point-to-point] D1[Device A] ---|TX-RX RX-TX| D2[Device B] end subgraph CAN [CAN - 2 wires differential] N1[Node 1] --- BusC[CAN-H + CAN-L] N2[Node 2] --- BusC N3[Node 3] --- BusC end

Four common embedded protocols. Notice the topology differences: I2C and CAN are buses with multiple devices; SPI is master-slave with dedicated chip-select per slave; UART is strictly point-to-point.

I2C (Inter-Integrated Circuit)

Two wires: SDA (data) and SCL (clock). Both pulled up to VCC through 4.7 kOhm resistors. Multiple devices share the bus, each with a unique 7-bit address. The master initiates every transaction; slaves only respond.

  • Speed: 100 kHz standard, 400 kHz fast mode, 1 MHz fast-mode plus, 3.4 MHz high speed (rare in practice).
  • Devices per bus: theoretically 128 (7-bit address), practically 10 because of bus loading and physical addressing collisions.
  • Wires: 2 + power + ground.
  • Used by: BME280, MPU6050, OLED displays, RTCs, EEPROMs, expanders — the dominant sensor protocol.

The big advantages: minimal wiring, multiple devices on the same lines. The drawbacks: slow compared to SPI, more sensitive to noise, vulnerable to bus lock-up if a device misbehaves.

Common gotcha: missing pull-up resistors. The SDA and SCL lines float without them, and bus communication fails. Most breakout boards include 10 kOhm pull-ups; some do not. If your bus does not work, add 4.7 kOhm pull-ups to 3.3V (or 5V depending on your logic level).

SPI (Serial Peripheral Interface)

Four wires: MOSI (Master Out, Slave In), MISO (Master In, Slave Out), SCK (clock), and CS (Chip Select, one per slave). Synchronous, full-duplex, and dramatically faster than I2C.

  • Speed: commonly 1–50 MHz; some chips support 100+ MHz.
  • Devices per bus: as many as you have CS pins.
  • Wires: 3 shared + 1 CS per device + power + ground.
  • Used by: SD cards, displays (ILI9341, Sharp Memory LCD), high-speed ADCs, flash memory, ethernet controllers.

SPI has four modes (combinations of clock polarity and clock phase). Master and slave must agree. The wrong mode produces garbled data with no error indication. Datasheets specify which mode each device requires.

The big advantage: speed. The drawbacks: more pins than I2C, requires a CS pin per device, no built-in error detection.

UART (Universal Asynchronous Receiver/Transmitter)

Two wires: TX (transmit) and RX (receive). Asynchronous — no shared clock, both ends agree on baud rate in advance. Strictly point-to-point: one UART peripheral talks to one other.

  • Speed: common rates 9600, 38400, 115200, up to 921600 baud. Higher rates require careful clock matching.
  • Devices: exactly two.
  • Wires: 2 + power + ground.
  • Used by: GPS modules, cellular modems, fingerprint sensors, ESP32 to MCU communication, debug consoles.

UART's claim to fame is its simplicity: no clock to sync, no addresses, just bits arriving at a known rate. The drawbacks: only two devices per UART peripheral, no error detection beyond an optional parity bit, baud rate mismatch produces nonsense.

RS-232 and RS-485 are voltage-level standards layered over UART. RS-232 swings ±12V (mostly for legacy PC serial ports). RS-485 is differential, multi-drop, and goes 1.2 km. UART itself is just the framing.

CAN (Controller Area Network)

Two wires (CAN-H and CAN-L) carrying a differential signal. Multi-master, multi-drop, with built-in arbitration: any node can transmit, and if two transmit at once, lower-ID frames win without collision damage.

  • Speed: 125 kbps to 1 Mbps for classical CAN; 5 Mbps for CAN-FD.
  • Devices per bus: typically 32, up to 110 with proper termination.
  • Wires: 2 differential + power + ground (typically a 4-wire bundle including 12V).
  • Used by: automotive (every modern car has multiple CAN buses), industrial machinery, marine equipment, agricultural machinery, some medical devices.

CAN's killer features: differential signaling for noise immunity in electrically hostile environments (engine bays, factory floors), built-in error detection with automatic retransmission, deterministic priorities based on message ID. The drawbacks: rarely used outside automotive/industrial, requires a transceiver IC (the chip cannot drive the differential signal directly), more complex than UART/I2C/SPI.

For automotive work, CAN is unavoidable; OBD-II diagnostic ports speak it. For most consumer or hobby projects, you do not need CAN.

Side-by-side

Aspect              I2C         SPI         UART        CAN
------------------  ----------  ----------  ----------  ----------
Wires (data)        2           3+CS        2           2 diff
Max speed           1 MHz       50+ MHz     1 Mbps      1 Mbps
Devices per bus     ~10         many CS     2           32+
Clock               shared      shared      none        embedded
Full-duplex         no          yes         yes         no
Error detection     ack only    none        parity opt  CRC + ARQ
Typical use         sensors     displays    modems      automotive
Complexity          medium      low         lowest      high
Noise tolerance     low         low         medium      high

Decision flow

flowchart TD Start([Picking a protocol]) --> Q1{Multiple devices
on same bus?} Q1 -->|No, point-to-point| Q2{Speed needed?} Q1 -->|Yes| Q3{Speed needed?} Q2 -->|Low to medium| UART[UART] Q2 -->|High| SPI1[SPI dedicated] Q3 -->|Low to medium| Q4{Noisy environment?} Q3 -->|High| SPI2[SPI with multiple CS] Q4 -->|No, normal| I2C[I2C] Q4 -->|Automotive industrial| CAN[CAN] style I2C fill:#dbeafe,stroke:#1e40af,color:#0c1e3b style SPI1 fill:#fef3c7,stroke:#92400e,color:#451a03 style SPI2 fill:#fef3c7,stroke:#92400e,color:#451a03 style UART fill:#e0e7ff,stroke:#3730a3,color:#1e1b4b style CAN fill:#fce7f3,stroke:#9d174d,color:#500724

Quick decision flow. Most projects pick I2C for sensors, SPI for displays, UART for modems and GPS, and CAN only when the application demands it.

Other protocols worth knowing

  • 1-Wire — one data wire (plus optional power). Used by DS18B20 temperature probes. Simpler than I2C, slower, but only one wire.
  • I2S — for digital audio. Three wires (clock, word select, data). Used by audio codecs and MEMS microphones.
  • USB — the desktop standard, increasingly common on microcontrollers (ESP32-S2/S3, STM32 USB peripherals). Vastly more complex than the above; usually accessed through a vendor stack.
  • SDIO — for SD cards and Wi-Fi modules at higher rates than SPI allows.
  • RS-485 — differential UART for industrial, used by Modbus.
  • LIN — cheaper, slower automotive bus alongside CAN.
  • Modbus RTU — protocol layered over RS-485, the dominant industrial PLC protocol.

Frequently Asked Questions

Why does my I2C device show address 0x76 in code but 0xEC on the datasheet?

The 7-bit address gets shifted left for the read/write bit on the wire. 0x76 << 1 = 0xEC. Some libraries expect the 7-bit form, others the shifted form. Cause of much confusion. Read your library's documentation to know which form it wants.

Why does my SPI display show garbage?

Almost always wrong SPI mode. Check the datasheet for required CPOL and CPHA, set them in your library config. The second most common cause is wrong clock speed — some displays cannot tolerate the high speeds the MCU defaults to.

Can I bit-bang these protocols if I run out of hardware peripherals?

Yes for I2C, UART, and SPI; not really for CAN. Bit-banging is software emulation of the protocol on regular GPIO. Slower (typically 10–30% of hardware peripheral speed), more CPU load, but works when hardware peripherals are exhausted. SoftwareSerial library on Arduino does UART bit-banging.

How long can each bus be in physical wires?

I2C: roughly 1 meter at 100 kHz with proper pull-ups; less at faster speeds. SPI: under 30 cm reliably; longer needs care. UART: depends on voltage levels — TTL UART under 1 m, RS-232 up to 15 m, RS-485 up to 1.2 km. CAN: 40 m at 1 Mbps, kilometers at low speeds.

What about CAN-FD?

CAN-FD (Flexible Data) is the modern variant: same physical layer as CAN, but supports up to 64 bytes per frame (vs 8) and higher speeds (up to 5 Mbps in data phase). Required for many modern vehicles. If your project involves automotive after about 2018, expect CAN-FD.

Share your thoughts

Worked with this in production and have a story to share, or disagree with a tradeoff? Email us at support@mybytenest.com — we read everything.