Weekly Robotics logo
Weekly Robotics Beginner-friendly tutorials, every week
I2C and SPI: The Two Most Common Communication Protocols
Communication

I2C and SPI: The Two Most Common Communication Protocols

After CAN bus, I2C and SPI are the protocols you'll use most often to connect sensors and peripherals to your microcontroller. Here's how they work and when to use each.

In robotics, you’ll constantly need to connect sensors, displays, motor drivers, and other peripherals to your microcontroller. Two protocols handle the vast majority of these connections: I2C and SPI. Understanding both will make you much more effective at building hardware.

I2C (Inter-Integrated Circuit)

I2C uses just two wires: SDA (Serial Data) and SCL (Serial Clock). This makes it ideal for connecting many devices with minimal wiring.

Every I2C device has a unique 7-bit address (0x00–0x7F). The master (usually your Arduino) addresses a specific device before communicating with it. Up to 112 devices can share the same two wires (128 addresses minus 16 reserved).

Master (Arduino) SDA SCL IMU 0x68 OLED 0x3C EEPROM 0x50
I2C: every device shares the same two wires. The master picks one by sending its address first.

I2C on Arduino:

  • SDA → Arduino pin A4 (Uno) or pin 20 (Mega)
  • SCL → Arduino pin A5 (Uno) or pin 21 (Mega)
#include <Wire.h>

void setup() {
  Wire.begin(); // Initialize as I2C master
  Serial.begin(9600);
}

void loop() {
  // Read from MPU-6050 IMU (address 0x68)
  Wire.beginTransmission(0x68);
  Wire.write(0x3B); // Starting register for accelerometer data
  Wire.endTransmission(false);
  Wire.requestFrom(0x68, 6, true); // Request 6 bytes
  
  int16_t ax = Wire.read() << 8 | Wire.read();
  int16_t ay = Wire.read() << 8 | Wire.read();
  int16_t az = Wire.read() << 8 | Wire.read();
  
  Serial.print("Accel X: "); Serial.print(ax / 16384.0);
  Serial.print(" Y: "); Serial.print(ay / 16384.0);
  Serial.print(" Z: "); Serial.println(az / 16384.0);
  
  delay(100);
}

I2C limitations: relatively slow (100 kHz standard, 400 kHz fast mode), and the bus can only be as long as a few meters reliably.

SPI (Serial Peripheral Interface)

SPI uses four wires: MOSI (Master Out Slave In), MISO (Master In Slave Out), SCK (Clock), and CS/SS (Chip Select). It’s faster than I2C (up to 80 MHz on some specialized high-end peripherals/MCUs) and full-duplex (can send and receive simultaneously). Note that an Arduino Uno is limited to about 8 MHz (F_CPU/2); that 80 MHz figure only applies to specialized high-end devices.

The trade-off: each SPI device needs its own CS wire. With many devices, this adds up.

Master (Arduino) MOSI MISO SCK SD card ADC CS1 CS2
SPI: MOSI, MISO and SCK are shared, but each device gets its own chip-select (CS) line so the master can talk to one at a time.

SPI on Arduino Uno:

  • MOSI → Pin 11
  • MISO → Pin 12
  • SCK → Pin 13
  • CS → Any digital pin (you choose)
#include <SPI.h>

const int CS_PIN = 10;

void setup() {
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // Deselect device
  SPI.begin();
  Serial.begin(9600);
}

byte readRegister(byte reg) {
  digitalWrite(CS_PIN, LOW);  // Select device
  SPI.transfer(reg | 0x80);   // Read command (bit 7 = 1)
  byte value = SPI.transfer(0x00); // Read response
  digitalWrite(CS_PIN, HIGH); // Deselect device
  return value;
}

void loop() {
  byte whoAmI = readRegister(0x0F); // WHO_AM_I register
  Serial.print("Device ID: 0x");
  Serial.println(whoAmI, HEX);
  delay(1000);
}

I2C vs SPI: When to Use Which

FeatureI2CSPI
Wires needed2 (shared)4 + 1 per device
SpeedUp to 400 kHz (standard)Up to 80 MHz
Multiple devicesEasy (address-based)One CS wire per device
Full duplexNoYes
Common usesSensors, displays, EEPROMsSD cards, ADCs, high-speed sensors

Use I2C when you have many low-speed sensors and want to minimize wiring. Use SPI when you need high data rates (SD cards, display modules, high-speed ADCs).

Many devices support both protocols — check the datasheet.