Loading

What Is a Communication Protocol?

Imagine two people who grew up in different states — one speaks only Malayalam, the other only Tamil. They want to have a conversation. For that to work, they need to agree on a common language, agree on who speaks first, and agree on how to signal "I am finished talking." Without that agreement, they just make noise at each other.

Electronics face the same problem. A microcontroller needs to talk to a GPS module. The GPS module needs to send data to the microcontroller. Both sides need to agree on: which wire carries data, how fast data flows, who initiates the conversation, and how to mark the start and end of each message. That agreement is a communication protocol.

Key Idea

A communication protocol is a set of rules that two or more electronic devices follow to reliably exchange data. UART, I2C, and SPI are three of the most common protocols in embedded systems. Each makes different trade-offs between simplicity, speed, and the number of devices supported.

UART — The Direct Phone Call

UART stands for Universal Asynchronous Receiver-Transmitter. Think of it as a direct phone call between two people — just two wires, just two devices, and they talk directly to each other.

How It Works

  • TX (Transmit) on device A connects to RX (Receive) on device B — and vice versa
  • Both devices must agree on the same baud rate — the speed of data transfer (typically 9600 or 115200 bits per second)
  • No shared clock signal — both sides keep their own timing (that is the "asynchronous" part)
  • Only point-to-point — one sender, one receiver. You cannot add a third device on the same two wires.

Common Uses

  • GPS modules (NEO-6M, NEO-8M) — send NMEA sentences over UART
  • Bluetooth HC-05 module — acts as a wireless UART bridge to your phone
  • Serial Monitor in Arduino IDE — your Arduino talks to your laptop over UART via USB
  • SIM800L GSM module — send SMS or make calls via UART AT commands
TX to RX — Always Cross Them

The most common UART wiring mistake: connecting TX to TX and RX to RX. TX (transmit) on device A must connect to RX (receive) on device B. If both are "talking" on the same wire, nobody hears anything. Cross the wires: A_TX → B_RX and A_RX → B_TX.

Arduino — Reading GPS data over UART (SoftwareSerial)
#include <SoftwareSerial.h>

// GPS TX → Arduino pin 4 (our RX)
// GPS RX → Arduino pin 3 (our TX)
SoftwareSerial gpsSerial(4, 3);  // (RX, TX)

void setup() {
  Serial.begin(9600);       // To Serial Monitor on laptop
  gpsSerial.begin(9600);    // GPS baud rate (check your module's datasheet)
  Serial.println("GPS reader started...");
}

void loop() {
  // Forward any data from GPS to Serial Monitor
  while (gpsSerial.available()) {
    char c = gpsSerial.read();
    Serial.write(c);  // You will see NMEA sentences like $GPGGA,...
  }
}

I2C — The Conference Call

I2C (Inter-Integrated Circuit, pronounced "I squared C") is like a conference call — one person hosts (the master) and can call any of the other attendees (slaves) by name. All participants share the same two wires but each has a unique address so messages reach the right person.

How It Works

  • SDA (Serial Data) — the wire carrying actual data, bidirectional
  • SCL (Serial Clock) — the master drives this clock so all devices stay in sync
  • Each device has a 7-bit address (like a phone extension number) — up to 127 unique devices on one bus
  • Master initiates all communication — it calls a device by address, the device responds
  • Pull-up resistors (typically 4.7 kΩ) required on both SDA and SCL lines

Common Uses

  • OLED displays (SSD1306) — address 0x3C or 0x3D
  • MPU-6050 IMU (gyroscope + accelerometer) — address 0x68 or 0x69
  • BMP280 pressure/temperature sensor — address 0x76 or 0x77
  • RTC modules (DS3231) — address 0x68
Arduino — Reading MPU-6050 over I2C
#include <Wire.h>  // Built-in I2C library

const int MPU_ADDR = 0x68;  // MPU-6050 default I2C address

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

  // Wake up MPU-6050 (it starts in sleep mode)
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x6B);          // Power management register
  Wire.write(0);             // Set to 0 to wake it up
  Wire.endTransmission(true);

  Serial.println("MPU-6050 ready");
}

void loop() {
  // Request 6 bytes of accelerometer data starting at register 0x3B
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_ADDR, 6, true);

  // Each axis is 16-bit (2 bytes, big-endian)
  int16_t ax = (Wire.read() << 8) | Wire.read();
  int16_t ay = (Wire.read() << 8) | Wire.read();
  int16_t az = (Wire.read() << 8) | Wire.read();

  // Raw values ÷ 16384 = g-force (default ±2g range)
  Serial.print("AX: "); Serial.print(ax / 16384.0, 2);
  Serial.print("g  AY: "); Serial.print(ay / 16384.0, 2);
  Serial.print("g  AZ: "); Serial.println(az / 16384.0, 2);

  delay(200);
}

SPI — The High-Speed Dedicated Line

SPI (Serial Peripheral Interface) is the fastest of the three. Think of it as a dedicated high-speed express lane — less traffic management overhead, no address system, just raw speed. The trade-off: each device needs its own dedicated chip select (CS) wire, so adding many devices gets complicated.

How It Works

  • MOSI — Master Out, Slave In (master sends data)
  • MISO — Master In, Slave Out (slave sends data back)
  • SCK — Serial Clock (master drives it)
  • CS/SS — Chip Select (one per device — pulled LOW to select that device)
  • Full-duplex — master and slave send simultaneously on different wires
  • No addressing — you select a device by pulling its CS pin LOW

Common Uses

  • SD card modules — need high speed for file writing
  • TFT colour displays (ILI9341) — huge amounts of pixel data to transfer quickly
  • NRF24L01 radio modules — 2.4 GHz wireless communication
  • MAX7219 LED matrix drivers
Arduino — Writing to SD card over SPI
#include <SPI.h>
#include <SD.h>

const int CS_PIN = 10;  // SD module CS → Arduino pin 10

void setup() {
  Serial.begin(9600);

  if (!SD.begin(CS_PIN)) {
    Serial.println("SD card init failed! Check wiring.");
    while (true);  // Stop here
  }
  Serial.println("SD card ready.");

  // Open a file and write to it
  File dataFile = SD.open("sensor.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println("Timestamp,Temperature");
    dataFile.println("2025-01-01 10:00:00,31.5");
    dataFile.println("2025-01-01 10:00:10,31.7");
    dataFile.close();  // Always close the file!
    Serial.println("Data written to sensor.txt");
  } else {
    Serial.println("Failed to open file.");
  }
}

void loop() {
  // Nothing here — data written once in setup()
}

How to Choose: Quick Decision Guide

In practice, the choice is often made for you by the sensor you are using. But when you do have a choice, or when you are debugging a protocol mismatch, this table and the decision logic below will help.

Protocol Wires Speed Max Devices Best For
UART2 (TX, RX)Up to 1 Mbps2 (point-to-point)GPS, Bluetooth, GSM, debug output
I2C2 (SDA, SCL)100 kbps – 400 kbps127Multiple sensors, displays, IMUs
SPI4+ (MOSI, MISO, SCK, CS)Up to 80 MbpsLimited by CS pinsSD cards, colour displays, radio

Decision Logic in Plain English

  • Do you need maximum speed (SD card, display)? → Use SPI
  • Do you need multiple sensors on just two wires? → Use I2C
  • Are you connecting two devices directly (GPS, Bluetooth module)? → Use UART
  • Not sure? Check your sensor's datasheet — the protocol is always specified
Try This

Pick up any sensor module you own — BMP280, DHT11, MPU-6050, HC-SR04, NEO-6M. Look it up online and identify which protocol it uses. Check the datasheet (or the Pinout diagram on the product page). Once you can identify the protocol from the pinout, you understand it.

← Previous
ESP32 vs Arduino
Next →
PWM Explained