TL;DR:
UART is two wires, no clock, and both sides must agree on a baud rate. I2C is two shared wires with an address per device. SPI is four-plus wires and fast, with one chip select per device.
A shared clock (I2C, SPI) lets the receiver know exactly when to sample. UART instead relies on both sides agreeing on timing.
Pick by what the part supports first, then by speed, wire count and how many parts share the bus.
A small robot has an inertial measurement unit (IMU) that reports turning and acceleration, a GPS receiver that reports position, and a microcontroller that wants both. The parts are on different chips and need to exchange bytes. There are a few standard ways to do that, and each part you buy will support one or two of them.
Wires, speed and addressing
Feature | UART | I2C | SPI |
|---|---|---|---|
Wires | TX and RX (plus ground), crossed between two devices | SDA (data) and SCL (clock), shared by all devices, plus ground | SCK (clock), MOSI (out), MISO (in), plus one chip-select line per device, plus ground |
Clock | None. Both sides agree on a baud rate | Shared clock from the controller | Shared clock from the controller |
Devices | Two, point to point | Many on the same two wires | Many, each needing its own chip-select pin |
Addressing | None needed | Each device has an address | Chip-select selects the device |
Typical speed | Often 9600 to 115200 baud for hobby parts | 100 kHz standard, 400 kHz fast, higher modes exist | Often several MHz, the fastest of the three |
UART sends bytes one bit at a time with a start bit and a stop bit around each byte, usually 8 data bits, so 10 bits on the wire per byte. With no clock line, both ends must be set to the same baud rate (bits per second) or they read gibberish.
I2C lets one controller talk to many devices over two wires. Each device has a 7-bit address, and every transfer starts with the controller sending that address. The lines are open-drain, which means devices only pull them low, so each line needs a pull-up resistor to the supply to return high. Many breakout boards already include them.
SPI uses a clock from the controller and separate lines for data out and data in, so it can send and receive at once. To choose a device, the controller pulls that device's chip-select line low. That costs one pin per device but gives the highest speed and simple logic.
Worked example: an IMU on I2C and a GPS on UART
Connect an IMU to the I2C pins and a GPS module to a UART. A common IMU address is 0x68, but read your own part's documentation. Power both at the voltage they are rated for.
To read the IMU, the controller sends a start condition, then the address with a write bit, then the number of the first register it wants, then a repeated start, then the address again with a read bit.
The IMU answers with bytes, one after another, and the controller acknowledges each. Say you read 14 bytes: accelerometer, temperature and gyroscope values.
Count the clock cycles. Each byte on I2C takes 9 clocks (8 data bits and 1 acknowledge). The address, register and address again are 3 bytes:
3 × 9 = 27. The 14 data bytes:14 × 9 = 126. Total:27 + 126 = 153clocks, ignoring the start and stop conditions.At the fast 400 kHz clock:
153 / 400000 ≈ 0.38 msper read. At 100 kHz it would be 1.53 ms. Either is comfortably quick enough to read the IMU at 100 times a second.
Now the GPS. Many modules send text lines called NMEA sentences continuously at 9600 baud. Each byte is 10 bits on the wire, so the link carries 9600 / 10 = 960 bytes per second. A typical sentence is around 80 bytes, so one sentence takes about 80 / 960 ≈ 0.083 s. That limit is why GPS data is slow to arrive and why you read it by listening for a full line instead of asking.
The two links fit the parts. The IMU needs a quick register read on request, and addressable I2C does that. The GPS just streams text, and a simple UART does that. Notice the GPS's data rate is determined by the baud rate set at both ends, not by the controller's own clock.
Choosing one
Use what the part offers. Many sensors support only one, so the choice is often made for you.
Lots of slow parts, few pins: I2C. Two pins serve many devices.
Fast data (displays, memory cards, high-rate sensors): SPI.
Two devices streaming text or commands, or a computer-to-microcontroller link: UART.
Long cables: I2C and SPI are meant for short runs on a board. For longer runs use differential links such as RS-485 or CAN, which are separate standards.
Where it goes wrong
Address clash. Two I2C parts with the same address cannot share a bus unless one can change its address.
Missing pull-ups. I2C does not work, or works only sometimes.
TX to TX. UART lines must cross: TX of one goes to RX of the other.
Wrong baud rate. Garbled text almost always means a mismatch.
Voltage mismatch. A 5 V device driving a 3.3 V pin can damage it. Use level shifting.
Floating chip-select. A missing pull-up on SPI chip-select can make a device wake at random.
Safety: Power the board off while you change wiring, check the supply voltage of every part before connecting, and keep to low-voltage supplies. Never connect any of these pins to mains power.
How real robots use it
A typical robot controller uses all three at once: I2C for small sensors, SPI for faster parts like memory or radios, and UART for the link to a bigger computer. How much those parts draw from the battery matters too, which is the topic of power budgets.
Try it yourself
Connect two microcontroller boards by UART (TX to RX, RX to TX, shared ground) and send a word from one to the other at 9600 baud. Change one side to 115200 and watch the garbage appear. Then run an I2C scanner sketch on a board with a sensor attached and read off its address.
Frequently asked questions
Why does I2C need pull-up resistors?
Devices only pull the line low. Something has to pull it back high when nobody is driving, and that is the resistor.
Can I use more than one device on a UART?
Not directly. UART is point to point. To talk to several, you use extra UARTs or a different bus.
