4  Sensor Communication Protocols

I2C, SPI, and Serial Interfaces for IoT Sensors

sensing
protocols
i2c
spi
interfaces
Author

IoT Textbook

Published

March 22, 2026

Keywords

I2C, SPI, UART, serial communication, sensor interface, ESP32, Arduino, bus protocol

In 60 Seconds

Sensor communication protocols (I2C, SPI, UART) are the “languages” sensors and microcontrollers use to exchange data. I2C uses just two wires and supports dozens of sensors on one bus, while SPI trades extra wires for much faster data transfer. Choosing the right protocol depends on your speed needs, available GPIO pins, and how many sensors you are connecting.

Key Concepts
  • I2C (Inter-Integrated Circuit): A two-wire serial protocol (SDA data, SCL clock) supporting multiple devices on one bus using 7-bit addresses; standard speed 100 kHz, fast mode 400 kHz, fast-plus 1 MHz
  • SPI (Serial Peripheral Interface): A four-wire full-duplex protocol (MOSI, MISO, SCK, CS) offering higher speed than I2C (up to tens of MHz) but requiring a dedicated chip-select line per device
  • UART (Universal Asynchronous Receiver/Transmitter): An asynchronous two-wire protocol (TX, RX) using agreed baud rates; simple and universal but limited to point-to-point connections with no inherent bus topology
  • 1-Wire: A single-wire protocol (plus ground) supporting multiple addressable devices; commonly used by DS18B20 temperature sensors; slow but minimal wiring
  • Pull-Up Resistors on I2C: I2C SDA and SCL lines use open-drain signaling and require external pull-up resistors (typically 4.7 kohm at 100 kHz) to define the HIGH state between transactions
  • Address Conflicts: Two I2C devices with the same address on the same bus cause data corruption. Check all sensor addresses before designing a multi-sensor board; some sensors offer address-select pins
  • Clock Stretching: An I2C feature where a slow slave holds SCL low to pause the master while preparing data; not all masters support it — check the microcontroller documentation before relying on this feature
  • Logic Level Compatibility: Mixing 3.3 V and 5 V devices on the same I2C or SPI bus can damage 3.3 V inputs. Use bidirectional level shifters or verified series-resistor approaches when mixing voltage domains

4.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Differentiate between I2C, SPI, and UART communication protocols based on wiring, speed, and topology
  • Configure an I2C bus with correct pull-up resistors and address assignments for multi-sensor applications
  • Implement SPI communication with proper mode selection for high-speed sensor data transfer
  • Diagnose common protocol issues including address conflicts, missing pull-ups, and SPI mode mismatches
  • Evaluate and justify the appropriate protocol for specific sensor requirements and constraints

4.2 Introduction

Sensor communication protocols are the foundation of IoT data acquisition. Every sensor reading must travel from the physical sensor to your microcontroller through a defined communication interface. Understanding these protocols enables you to design reliable, efficient sensor networks.

Think of communication protocols like different languages. Just as people need to speak the same language to understand each other, sensors and microcontrollers need to “speak” the same protocol to exchange data. The most common “languages” are I2C (pronounced “eye-squared-see” or “eye-two-see”), SPI (“spy”), and UART (“you-art”). Each has different rules about how many wires to use, how fast to talk, and how to address specific devices.

4.3 I2C Communication Protocol

15 min | Intermediate | P06.C09.U01a

I2C (Inter-Integrated Circuit) is a two-wire synchronous protocol perfect for connecting multiple sensors using minimal GPIO pins. It uses:

  • SDA (Serial Data): Bidirectional data line
  • SCL (Serial Clock): Clock signal from master
  • 7-bit addressing: Up to 112 usable device addresses (128 total, 16 reserved)
  • Pull-up resistors: Required on both lines

4.3.1 I2C Protocol Sequence

I2C protocol sequence diagram showing master-slave communication: START condition, 7-bit address with write bit, register address, repeated START, read data byte, NACK, and STOP condition with key protocol elements explained.
Figure 4.1: I2C Protocol Sequence: Reading Temperature from BMP280 Sensor
Geometric diagram of I2C bus architecture showing master microcontroller, multiple slave devices with unique addresses, shared SDA and SCL lines, pull-up resistors, and bidirectional data flow for multi-device sensor networks
Figure 4.2: The I2C bus enables multiple sensors to share just two wires (SDA and SCL) by using unique addresses for each device. Pull-up resistors maintain logic high levels, while open-drain drivers allow any device to pull lines low for signaling. This architecture simplifies wiring for multi-sensor IoT systems.

4.3.2 Key I2C Protocol Elements

  • START Condition: SDA goes LOW while SCL is HIGH (signals transaction start)
  • STOP Condition: SDA goes HIGH while SCL is HIGH (signals transaction end)
  • ACK (Acknowledge): Receiver pulls SDA LOW during 9th clock pulse (data received successfully)
  • NACK (Not Acknowledge): Receiver leaves SDA HIGH (last byte or error)
  • Repeated START: START without preceding STOP (direction change from write to read)

4.3.3 I2C Implementation Example

Reading sensor data over I2C follows a two-phase pattern: first write the register address you want to read, then request the data bytes. The endTransmission(false) sends a Repeated START instead of a STOP, keeping the bus locked for the subsequent read.

#include <Wire.h>

#define I2C_SDA 21
#define I2C_SCL 22
#define BMP280_ADDR 0x76
#define BMP280_CHIP_ID_REG 0xD0

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA, I2C_SCL);

  // Read chip ID register to verify communication
  uint8_t chipId;
  readI2CRegister(BMP280_ADDR, BMP280_CHIP_ID_REG, &chipId, 1);
  Serial.print("BMP280 Chip ID: 0x");
  Serial.println(chipId, HEX);  // Should print 0x58 for BMP280
}

void readI2CRegister(uint8_t addr, uint8_t reg, uint8_t* data, uint8_t len) {
  Wire.beginTransmission(addr);
  Wire.write(reg);                // Phase 1: Write register address
  Wire.endTransmission(false);    // Repeated START (no STOP)

  Wire.requestFrom(addr, len);    // Phase 2: Read data bytes
  for(int i = 0; i < len; i++) {
    if(Wire.available()) {
      data[i] = Wire.read();
    }
  }
}

void loop() { }

Estimate total bus capacitance to determine if your I2C bus will work reliably. The I2C specification limits bus capacitance to 400 pF for standard mode and fast mode.

This simulation demonstrates how I2C bus scanning works. Add devices to the bus, then click “Scan Bus” to see the master controller query each address and detect ACK/NACK responses.

4.4 SPI Communication Protocol

15 min | Intermediate | P06.C09.U01b

SPI (Serial Peripheral Interface) is a synchronous, full-duplex communication protocol using four lines: MISO (Master In Slave Out), MOSI (Master Out Slave In), SCK (Serial Clock), and CS (Chip Select). Unlike I2C, SPI supports simultaneous bidirectional data transfer.

SPI protocol diagram showing four-wire full-duplex communication between master ESP32 and slave SD card with MOSI, MISO, SCK, and CS signal lines and bidirectional data flow.
Figure 4.3: SPI Protocol Communication: Full-Duplex SD Card Read Transaction

4.4.1 SPI vs I2C Comparison

Feature I2C SPI
Wires 2 (SDA, SCL) 4+ (MISO, MOSI, SCK, CS per device)
Speed 100 kHz - 3.4 MHz (standard/fast/high-speed) 1-100 MHz
Topology Multi-master, multi-slave Single master (typical), multi-slave
Addressing 7-bit addresses (112 usable) Hardware CS pins (limited by GPIO)
Data Transfer Half-duplex (sequential) Full-duplex (simultaneous)
Protocol Overhead START, STOP, ACK/NACK Minimal (just CS selection)
Typical Use Multiple sensors, displays High-speed: SD cards, displays, ADCs
GPIO Efficiency High (2 pins for many devices) Low (3 shared + 1 CS per device)

Calculate the effective data throughput for your SPI configuration based on clock speed and transaction overhead.

4.4.2 When to Choose Each Protocol

Choose I2C when:

  • Multiple sensors needed (temperature, pressure, IMU, light)
  • Limited GPIO pins available
  • Moderate data rates sufficient (<50 kB/s)
  • Moderate cable length (limited by 400 pF bus capacitance; typically 0.5-2 meters depending on number of devices and wire type)

Choose SPI when:

  • High-speed data transfer required (>1 MB/s)
  • Large data blocks (SD cards, displays)
  • Real-time requirements (<1 ms latency)
  • Plenty of GPIO pins available
Try It: GPIO Pin Budget Calculator

Plan your wiring by entering how many I2C, SPI, and UART devices you need. The calculator shows total GPIO pin usage and whether your microcontroller has enough pins.

Tradeoff: I2C vs. SPI for Multi-Sensor IoT Nodes

Option A: I2C bus (BME280 + BH1750 + MPU6050): Wire count 2 (SDA, SCL shared), GPIO usage 2 pins total for 3+ sensors, max speed 400kHz (~44kB/s), read latency ~100-200us per sensor at 400kHz (address + register overhead), power during transfer ~1mA, cable length up to 1-3 meters with 4.7k pull-ups

Option B: SPI bus (BME280 + SD card + TFT display): Wire count 3 shared + 1 CS per device = 6 pins for 3 devices, max speed 10-40MHz (1-5MB/s), read latency ~10us per sensor (direct register access), power during transfer ~5-10mA (higher clock), cable length 10-30cm max at high speeds

Decision Factors: For battery-powered environmental monitoring nodes with 3-5 slow sensors, I2C saves pins and power while 400kHz is adequate for 100Hz sensor reads. For data logging to SD card (500kB/s sustained), displays (30fps video), or high-speed ADCs (1MSPS), SPI is mandatory. Hybrid approach: use I2C for slow sensors, SPI for SD/display. Watch for I2C address conflicts - BME280 has only 2 addresses (0x76, 0x77), limiting you to 2 per bus without multiplexer.

4.5 UART Serial Communication

10 min | Intermediate | P06.C09.U01c

UART (Universal Asynchronous Receiver/Transmitter) is the simplest serial protocol, using just two wires for point-to-point communication without a clock signal. Unlike I2C and SPI, UART is asynchronous – both sides must agree on the baud rate (bits per second) beforehand.

4.5.1 UART Key Characteristics

  • TX (Transmit) and RX (Receive): Two unidirectional lines (cross-connected between devices)
  • Asynchronous: No shared clock; both devices must use the same baud rate
  • Point-to-point only: Connects exactly two devices (no bus topology)
  • Common baud rates: 9600, 19200, 38400, 57600, 115200 bps
  • Frame format: Start bit + 8 data bits + optional parity + 1-2 stop bits

4.5.2 When to Use UART

UART is commonly used for GPS modules (NMEA sentences at 9600 bps), Bluetooth modules (HC-05 AT commands), GSM/cellular modems, and debug/logging output. Many sensors provide UART as a simpler alternative to I2C/SPI, though at lower data rates.

// Reading GPS data over UART (Serial2 on ESP32)
#define GPS_RX 16
#define GPS_TX 17

void setup() {
  Serial.begin(115200);           // USB debug output
  Serial2.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX);  // GPS at 9600 baud
}

void loop() {
  while (Serial2.available()) {
    char c = Serial2.read();
    Serial.print(c);  // Forward GPS NMEA sentences to USB
  }
}

Calculate the effective data throughput for a UART connection based on baud rate and frame configuration.

4.5.3 I2C vs SPI vs UART Quick Reference

Feature I2C SPI UART
Wires 2 3 + 1/device 2
Topology Bus (multi-device) Bus (multi-device) Point-to-point
Clock Synchronous (shared) Synchronous (shared) Asynchronous (agreed)
Speed 100 kHz - 3.4 MHz 1-100 MHz 9600 - 921600 bps
Duplex Half-duplex Full-duplex Full-duplex
Best For Many slow sensors High-speed data Simple serial devices
Try It: Protocol Selector for Your Project

Select your project requirements and see which protocol is the best fit. The tool scores each protocol across your priorities and highlights the recommended choice.

4.6 Common Protocol Pitfalls

Pitfall: Missing or Wrong I2C Pull-Up Resistors

The Mistake: Connecting I2C sensors directly to GPIO pins without external pull-up resistors, assuming the microcontroller’s internal pull-ups are sufficient, or using incorrect resistor values that cause unreliable communication.

Why It Happens: Many tutorials skip pull-up resistors because they work in short-range prototyping. Internal pull-ups on ESP32 are weak (45-65 kohm), only suitable for very short wires (<10 cm) at low speeds. Beginners also confuse I2C (open-drain, requires pull-ups) with SPI (push-pull, no pull-ups needed).

The Fix: Always use external pull-up resistors. Common starting values are 4.7 kohm for standard 100 kHz I2C and 2.2 kohm for fast mode 400 kHz. The optimal value depends on bus capacitance: R = t_rise / (0.8473 x C_bus), where t_rise is the maximum rise time from the I2C specification (1000 ns standard mode, 300 ns fast mode). Place resistors near the master (ESP32/Arduino), not at each sensor. See the Worked Example below for a detailed step-by-step pull-up calculation with a real multi-sensor design. Use the interactive calculator to quickly check values for your setup:

Calculate the optimal pull-up resistor value for your I2C bus based on capacitance and speed.

Diagnosing Pull-Up Issues: Use the endTransmission() return code to identify whether communication failures stem from missing pull-ups, wrong addresses, or bus faults:

// Check I2C communication health
Wire.beginTransmission(0x76);
byte error = Wire.endTransmission();
// error = 0: Success
// error = 2: NACK on address (wrong address or missing pull-ups!)
// error = 3: NACK on data (device rejected register/data byte)
// error = 4: Other error (bus stuck, SDA/SCL shorted)

Symptom of missing pull-ups: I2C scanner finds no devices, or communication works intermittently. If you see error code 2 on all addresses, check your pull-up resistors first.

Pitfall: SPI Mode Mismatch Causing Corrupted Data

The Mistake: Using the default SPI mode (Mode 0) when the sensor datasheet specifies a different mode, resulting in consistently wrong readings, all-zeros, or all-ones from the sensor even though communication appears to work.

Why It Happens: SPI has four modes defined by CPOL (clock polarity) and CPHA (clock phase), and unlike I2C addresses which cause obvious failures, wrong SPI mode still clocks data - it just samples at the wrong edge. The MAX31855 thermocouple ADC requires Mode 0, while the ADXL345 accelerometer is typically used in Mode 3 (it also supports Mode 0). Many developers copy-paste SPI initialization code without checking the specific sensor’s timing requirements.

The Fix: Check the sensor datasheet for CPOL and CPHA, then configure SPI explicitly. Mode 0: CPOL=0, CPHA=0 (clock idle low, sample on rising edge). Mode 1: CPOL=0, CPHA=1. Mode 2: CPOL=1, CPHA=0. Mode 3: CPOL=1, CPHA=1 (clock idle high, sample on falling edge):

// WRONG: Default mode may not match sensor
SPI.begin();
SPI.transfer(0x00);

// CORRECT: Explicit mode configuration
SPI.begin();
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));  // 1 MHz, MSB first, Mode 0

// For ADXL345 accelerometer (Mode 3):
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE3));  // 5 MHz, Mode 3

// Always end transaction when done
SPI.endTransaction();

Debug tip: If sensor returns 0x00 or 0xFF consistently, try all four SPI modes - one should produce valid register values.

Try It: SPI Mode Timing Visualizer

Select an SPI mode and see how CPOL and CPHA affect the clock signal and data sampling edges. Understanding which clock edge is used for sampling helps you debug corrupted sensor readings.

Try It: I2C Address Conflict Checker

Enter the sensors in your design to check for I2C address conflicts. The tool identifies collisions and suggests resolution strategies.

4.7 Knowledge Check

Scenario: You’re building an indoor air quality monitor with 5 sensors on a single I2C bus connected to an ESP32:

  1. BME280 (temperature, humidity, pressure) - Address: 0x76
  2. BH1750 (light sensor) - Address: 0x23
  3. CCS811 (CO2 and VOC) - Address: 0x5A
  4. SSD1306 (OLED display) - Address: 0x3C
  5. Second BME280 (outdoor sensor) - Address: 0x77 (SDO pin to VCC)

Design Challenges to Solve:

1. Pull-Up Resistor Calculation

The I2C bus needs pull-up resistors on both SDA and SCL lines. Too weak (high resistance) causes slow rise times and communication errors. Too strong (low resistance) wastes power and may damage devices.

Formula: R_pullup = t_rise / (0.8473 × C_bus)

Where: - t_rise = maximum rise time (1000 ns for standard mode 100 kHz, 300 ns for fast mode 400 kHz) - C_bus = total bus capacitance (trace capacitance + device capacitance)

Step-by-step Calculation:

a) Estimate bus capacitance:

  • PCB trace: ~30 pF per 30cm wire (assume 60cm total) = 60 pF
  • BME280: 10 pF per device × 2 = 20 pF
  • BH1750: 10 pF
  • CCS811: 10 pF
  • SSD1306: 400 pF (worst-case with long flex cable; typical breakout boards add 100-150 pF)
  • Total C_bus: 60 + 20 + 10 + 10 + 400 = 500 pF

b) Choose I2C speed:

  • Standard mode (100 kHz): lower power, simpler
  • Fast mode (400 kHz): faster data, needed if polling sensors frequently
  • Choice: Fast mode 400 kHz (t_rise = 300 ns max)

c) Calculate maximum pull-up resistance for fast mode:

R_max = t_rise / (0.8473 × C_bus)
R_max = 300 ns / (0.8473 × 500 pF)
R_max = 300 × 10^-9 / (0.8473 × 500 × 10^-12)
R_max = 708 Ω (approximately 0.708 kΩ)

d) Calculate minimum pull-up resistance: Minimum limited by maximum sink current (3 mA per I2C spec) and V_OL_max (0.4V):

R_min = (V_CC - V_OL) / I_OL = (3.3V - 0.4V) / 3mA = 0.97 kΩ

e) Evaluate resistor selection for 400 kHz:

  • Maximum resistance from rise time: 708 Ω
  • Minimum resistance from current limit: 0.97 kΩ
  • Problem: No standard resistor value exists between 708 Ω and 0.97 kΩ that satisfies both constraints!
  • Actual rise time with 1.0 kΩ: 1kΩ × 500pF × 0.8473 = 424 ns (exceeds 300 ns limit for 400 kHz)

Conclusion: 400 kHz is not achievable with 500 pF bus capacitance!

Let’s recalculate for 100 kHz standard mode instead:

Revised Calculation for 100 kHz:

t_rise_max = 1000 ns (standard mode specification)
R_max = 1000 ns / (0.8473 × 500 pF) = 2.36 kΩ (maximum resistance allowed)
R_min = (3.3V - 0.4V) / 3mA = 0.97 kΩ (minimum resistance from current limit)

Acceptable range: 0.97 kΩ to 2.36 kΩ

Test with 4.7 kΩ resistors (common value):

t_rise = 4.7kΩ × 500pF × 0.8473 = 1991 ns
Result: FAILS (exceeds 1000 ns maximum for 100 kHz)

Final Decision: Use 2.2 kΩ pull-ups for reliable 100 kHz operation:

t_rise = 2.2kΩ × 500pF × 0.8473 = 932 ns ✓ (safely under 1000 ns limit)
Current draw = 3.3V / 2.2kΩ = 1.5 mA ✓ (within ESP32 limits)

2. Address Conflict Resolution

One BME280 at default address 0x76, second at alternate 0x77:

#include <Adafruit_BME280.h>

Adafruit_BME280 bme_indoor;  // 0x76
Adafruit_BME280 bme_outdoor; // 0x77

void setup() {
    Wire.begin(21, 22); // SDA=21, SCL=22 on ESP32

    // Initialize with explicit addresses
    if (!bme_indoor.begin(0x76)) {
        Serial.println("Could not find indoor BME280");
    }
    if (!bme_outdoor.begin(0x77)) {
        Serial.println("Could not find outdoor BME280");
    }
}

3. Bus Scanning for Debugging

Before writing sensor-specific code, scan the bus to verify all devices are present:

void scanI2CBus() {
    Serial.println("\n--- I2C Bus Scan ---");
    byte count = 0;

    for (byte addr = 1; addr < 127; addr++) {
        Wire.beginTransmission(addr);
        byte error = Wire.endTransmission();

        if (error == 0) {
            Serial.print("Device found at 0x");
            if (addr < 16) Serial.print("0");
            Serial.print(addr, HEX);

            // Identify common sensors
            switch(addr) {
                case 0x23: Serial.print(" (BH1750 Light)"); break;
                case 0x3C: Serial.print(" (SSD1306 OLED)"); break;
                case 0x5A: Serial.print(" (CCS811 Air Quality)"); break;
                case 0x76: Serial.print(" (BME280 #1)"); break;
                case 0x77: Serial.print(" (BME280 #2)"); break;
                default: Serial.print(" (Unknown)"); break;
            }
            Serial.println();
            count++;
        }
    }

    Serial.print("Total devices found: ");
    Serial.println(count);

    if (count != 5) {
        Serial.println("WARNING: Expected 5 devices!");
    }
}

Expected Output:

--- I2C Bus Scan ---
Device found at 0x23 (BH1750 Light)
Device found at 0x3C (SSD1306 OLED)
Device found at 0x5A (CCS811 Air Quality)
Device found at 0x76 (BME280 #1)
Device found at 0x77 (BME280 #2)
Total devices found: 5

4. Power Sequencing for CCS811

The CCS811 air quality sensor has specific power-up requirements:

void initCCS811() {
    pinMode(CCS811_WAKE_PIN, OUTPUT);
    digitalWrite(CCS811_WAKE_PIN, LOW); // Wake sensor

    delay(50); // Wait for sensor wake-up

    if (!ccs811.begin(0x5A)) {
        Serial.println("CCS811 not found!");
        return;
    }

    // Wait for sensor to be ready (mandatory 20 minutes burn-in on first use)
    while (!ccs811.available()) {
        delay(500);
    }

    ccs811.setDriveMode(CCS811_DRIVE_MODE_1SEC); // Read every 1 second
}

5. Complete Reading Sequence

Optimized order to minimize blocking:

void readAllSensors() {
    // Start CCS811 measurement (takes ~50ms)
    ccs811.readData();

    // Read fast sensors while CCS811 processes
    float lux = lightMeter.readLightLevel();

    // Read BME280s (I2C read ~5ms each)
    bme_indoor.takeForcedMeasurement();
    float temp_in = bme_indoor.readTemperature();
    float hum_in = bme_indoor.readHumidity();
    float press_in = bme_indoor.readPressure() / 100.0F;

    bme_outdoor.takeForcedMeasurement();
    float temp_out = bme_outdoor.readTemperature();
    float hum_out = bme_outdoor.readHumidity();

    // CCS811 should be ready now
    if (ccs811.available()) {
        co2 = ccs811.geteCO2();
        tvoc = ccs811.getTVOC();

        // Set environmental data for compensation
        ccs811.setEnvironmentalData(hum_in, temp_in);
    }

    // Update display (non-blocking update)
    updateDisplay(temp_in, hum_in, co2, tvoc, lux);
}

Bill of Materials (BOM):

Component Quantity Cost (unit) Purpose
ESP32 DevKit 1 $8 Microcontroller
BME280 2 $4 each Temp/humidity/pressure
BH1750 1 $2 Light sensor
CCS811 1 $12 Air quality (CO2/VOC)
SSD1306 OLED 1 $5 Display
2.2kΩ resistors 2 $0.01 each I2C pull-ups
Total $35.02 Complete system

Key Lessons:

  1. Bus capacitance matters - high-capacitance devices (OLED) require stronger pull-ups
  2. Scan before coding - verify all addresses are correct before writing application code
  3. Read datasheets carefully - CCS811 has specific initialization and environmental compensation requirements
  4. Optimize read order - start slow sensors first, read fast ones during wait times
  5. Pull-up calculation is critical - incorrect values cause intermittent failures that are hard to debug

4.8 Summary

This chapter covered the three essential communication protocols for sensor interfacing:

  • I2C Protocol: Two-wire synchronous bus with 7-bit addressing, ideal for connecting multiple slow sensors with minimal GPIO usage
  • SPI Protocol: Four-wire synchronous full-duplex interface for high-speed data transfer (SD cards, displays, high-resolution ADCs)
  • UART Protocol: Two-wire asynchronous point-to-point link for simple serial devices (GPS, Bluetooth modules, debug output)
  • Protocol Selection: Choose I2C for multi-sensor setups with limited pins; SPI when speed is critical; UART for simple serial peripherals
  • Common Pitfalls: Pull-up resistors for I2C, SPI mode configuration, address conflict resolution
Key Takeaway

I2C, SPI, and UART are the three core sensor communication protocols in IoT. Use I2C (two wires, 7-bit addressing) when you need to connect many low-speed sensors with minimal wiring. Use SPI (four wires, full-duplex) when high-speed data transfer is essential. Use UART (two wires, asynchronous) for simple point-to-point serial devices. Always add proper pull-up resistors for I2C and verify SPI mode settings from the sensor datasheet to avoid corrupted data.

Sammy the Sensor wanted to tell Max the Microcontroller about the temperature, but they did not speak the same language! Luckily, their friend Lila the LED knew a translator called “I2C” – it only needed two wires, like a tin-can telephone with two strings. “I will talk on the data string, and you keep time on the clock string!” Sammy said.

But when Bella the Battery needed to send a LOT of data really fast (like a whole photo album), she used a different translator called “SPI” – it was like having four walkie-talkies at once! One to send, one to receive, one to keep time, and one to pick who is talking.

“Why not always use SPI?” Max asked. “Because SPI needs more wires,” Lila explained. “If you have 10 sensor friends, I2C lets them ALL share just two wires – each friend just has a different nickname (address). SPI would need a separate wire for each friend!” Now the whole Sensor Squad could chat, whether they needed speed or simplicity!

4.9 What’s Next

Chapter Focus
Sensor Data Processing Filtering techniques, calibration procedures, and data validation pipelines
Sensor Fundamentals and Types Core sensor types, characteristics, and selection criteria
Sensor Circuits and Signals Analog circuit design, signal conditioning, and ADC interfacing
Multi-Sensor Data Fusion Combining readings from multiple sensors for improved accuracy
IoT Networking Core Network protocols that carry sensor data to the cloud