Why Arduino Needs Dedicated Buses
A single Arduino has only a handful of I/O pins, yet a real project may need a temperature sensor, an OLED screen, a real-time clock, and an SD card all at once. Wiring each device with its own dedicated parallel lines would exhaust the pins instantly. I2C (Inter-Integrated Circuit) and SPI (Serial Peripheral Interface) solve this by letting many chips share the same small set of wires, each device addressed or selected in turn. Both are synchronous serial buses, meaning a shared clock line keeps sender and receiver in lockstep so no timing guesswork is required.
Cricket analogy: Like a single set of stumps shared by both batting ends: rather than building a fresh pitch per delivery, one shared strip serves every ball, just as one bus serves every chip.
I2C: Two Wires, Many Devices
I2C uses just two lines: SDA (serial data) and SCL (serial clock), plus a shared ground. On an Arduino Uno these are pins A4 (SDA) and A5 (SCL). Every device on the bus has a unique 7-bit address, so the master (the Arduino) begins a transaction by broadcasting the target address, and only the matching chip responds. Because the lines are open-drain, they need pull-up resistors (commonly 4.7 kΩ) to a positive rail; most breakout boards include these already. I2C is slower than SPI, typically 100 kHz standard or 400 kHz fast mode, but its two-wire simplicity makes it ideal for connecting many low-bandwidth sensors.
Cricket analogy: The umpire calling a specific batter's name before a review is like the master broadcasting a 7-bit address so only the addressed chip answers.
#include <Wire.h>
void setup() {
Wire.begin(); // join the I2C bus as master
Serial.begin(9600);
}
void loop() {
// Request 1 byte from a DS3231 RTC at address 0x68 (seconds register)
Wire.beginTransmission(0x68);
Wire.write(0x00); // point to register 0
Wire.endTransmission();
Wire.requestFrom(0x68, 1);
if (Wire.available()) {
byte seconds = Wire.read();
Serial.print("Seconds (BCD): 0x");
Serial.println(seconds, HEX);
}
delay(1000);
}Stuck bus? Upload the classic I2C scanner sketch: loop addresses 1–126, call Wire.beginTransmission(addr) and Wire.endTransmission(), and print any address that returns 0. It confirms your wiring and reveals each device's real address before you write driver code.
SPI: Four Wires, Full Speed
SPI trades I2C's minimal wiring for raw speed and full-duplex data flow. It uses four signals: SCK (clock), MOSI (master out, slave in), MISO (master in, slave out), and one SS/CS (slave select) line per device. On an Uno these live on pins 13, 11, and 12, with any spare pin acting as a chip select. Instead of addressing by number, the master pulls a device's CS line low to activate it, then shifts bits out on MOSI while simultaneously reading bits back on MISO in the same clock cycles. Clock speeds of several megahertz make SPI the natural choice for SD cards, TFT displays, and high-rate sensors, at the cost of one extra pin per additional device.
Cricket analogy: Full-duplex SPI is like a quick single where both batters run in opposite directions at once, sending and receiving in the same instant.
I2C and SPI use different logic conventions and can conflict on voltage. Many modern breakouts run at 3.3 V; feeding them the Uno's 5 V logic can permanently damage them. Check each device's datasheet and add a level shifter when mixing 5 V and 3.3 V parts, especially on SPI lines like MOSI and SCK.
#include <SPI.h>
const int CS_PIN = 10;
void setup() {
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH); // deselect device
SPI.begin();
}
byte readRegister(byte reg) {
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW); // select device
SPI.transfer(reg | 0x80); // 0x80 = read bit for many sensors
byte value = SPI.transfer(0x00); // send dummy, receive data
digitalWrite(CS_PIN, HIGH); // deselect
SPI.endTransaction();
return value;
}Choosing Between Them
Reach for I2C when pin count matters more than speed: multiple slow sensors, an RTC, and a small OLED can all coexist on the same two wires. Reach for SPI when throughput dominates, such as streaming to an SD card or refreshing a color display, and you can spare a chip-select pin per device. Many projects blend both: an ESP32 might drive a fast SPI display while polling a bank of I2C environmental sensors. Understanding each bus's trade-offs lets you allocate the microcontroller's limited pins deliberately instead of running out halfway through a build.
Cricket analogy: Choosing I2C vs SPI is like picking a spinner for control or a pace bowler for speed: match the tool to what the situation demands.
- I2C uses two wires (SDA, SCL) with 7-bit addressing and open-drain pull-ups, ideal for many slow devices.
- SPI uses four wires (SCK, MOSI, MISO, CS) with one chip-select per device and runs at megahertz speeds full-duplex.
- On an Uno, I2C is on A4/A5 and hardware SPI is on pins 11/12/13 plus a chosen CS pin.
- The Wire library drives I2C; the SPI library drives SPI, using beginTransaction to set speed and mode.
- Watch voltage levels: many breakouts are 3.3 V and need level shifting from the Uno's 5 V logic.
- Use an I2C scanner to discover device addresses and confirm wiring before writing drivers.
- Blend both buses in one project: SPI for high-bandwidth peripherals, I2C for banks of low-rate sensors.
Practice what you learned
1. On an Arduino Uno, which pins carry the I2C SDA and SCL signals?
2. How does an SPI master select which peripheral to communicate with?
3. Why does an I2C bus require pull-up resistors on SDA and SCL?
4. What does 'full-duplex' mean in the context of SPI?
5. Which scenario best favors I2C over SPI?
Was this page helpful?
You May Also Like
Using and Writing Arduino Libraries
How to install and use third-party Arduino libraries, and how to package your own reusable code into a proper library with headers, source files, and examples.
Reading Common Sensors
Learn how to interface Arduino with common sensors — potentiometers, light-dependent resistors, temperature sensors, PIR motion detectors, and ultrasonic rangefinders — using voltage dividers, libraries, and pulse timing.
Serial Communication Basics
Use the Serial library to send data between Arduino and a computer for debugging, monitoring sensors, and receiving commands.
WiFi and ESP32 Basics
An introduction to the ESP32, a WiFi- and Bluetooth-enabled microcontroller programmable through the Arduino IDE, and how to connect it to networks and make HTTP requests.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics