Communication protocols¶
Modern embedded projects rarely use a microcontroller in isolation: it almost always has to talk to sensors, displays, other boards, or a PC.
Communication protocols define how bits move between devices and how both sides agree on speed, timing, and meaning of the data.
On Arduino‑class boards, three protocols are especially common:
- Serial (UART): point‑to‑point communication with a computer or another microcontroller.
- I²C (Two‑Wire Interface): short‑range bus with one master and (usually) several slave devices on just two wires.
- SPI (Serial Peripheral Interface): very fast, full‑duplex bus for displays, SD cards, and high‑speed sensors.
In this page we’ll briefly introduce each protocol and show a minimal Arduino example.
Serial (UART)¶
Serial (often called UART or “Serial over USB”) is the simplest protocol to start with:
- Wires: TX, RX, and GND.
- Typical use: debugging via the Serial Monitor, sending simple commands, logging sensor data.
- Data rate: configured as a baud rate (e.g. 9600, 115200).
Most Arduino boards expose a Serial object that is already connected to the USB port.
Example: send sensor data over Seria¶
This example (for an ESP32-S3 DevKit‑style board) reads a value from GPIO 34 and prints it to the Serial Monitor:
// Simple Serial example for ESP32: read from GPIO34 and print to Serial Monitor
const int SENSOR_PIN = 34; // ADC pin on many ESP32-S3 DevKit boards
void setup() {
// Start serial communication at 115200 baud
Serial.begin(115200);
Serial.println("Serial ready!");
}
void loop() {
int sensorValue = analogRead(SENSOR_PIN);
Serial.print("Sensor value: ");
Serial.println(sensorValue);
delay(500);
}
Open the Serial Monitor in the Arduino IDE and make sure the baud rate matches the value in Serial.begin(...).
I²C (Two‑Wire Interface)¶
I²C is a bus protocol that lets one master (your Arduino or ESP32) talk to multiple slave devices on the same two wires:
- Wires:
SDA(data),SCL(clock), plus GND and usually 3.3 V or 5 V power. - Addressing: each device has a 7‑bit address (e.g.
0x27). - Typical use: temperature/pressure sensors, real‑time clocks, small LCDs, I/O expanders.
On Arduino you use the Wire library to work with I²C.
Example: I²C master sending a byte¶
This minimal sketch shows an Arduino acting as I²C master and sending a single byte to a device at address 0x27:
#include <Wire.h>
// I2C address of the peripheral device
const uint8_t DEVICE_ADDRESS = 0x27;
void setup() {
// Initialize I2C as master
Wire.begin(); // on most Arduino boards: SDA/SCL pins; on ESP32: GPIO 21/22 by default
// Optional: also initialize Serial for debugging
Serial.begin(115200);
}
void loop() {
uint8_t valueToSend = 42;
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write(valueToSend); // send a single byte
uint8_t error = Wire.endTransmission(); // actually transmit
if (error == 0) {
Serial.println("I2C write OK");
} else {
Serial.print("I2C error: ");
Serial.println(error);
}
delay(1000);
}
In a real project you would check the datasheet for your specific device to know which bytes/commands to send and in what order.
SPI (Serial Peripheral Interface)¶
SPI is a high‑speed, full‑duplex protocol that uses separate lines for data and clock:
- Wires:
MOSI,MISO,SCK, one chip select (CS) per device, plus GND and power. - Typical use: color displays (TFT/OLED), SD cards, high‑speed sensors, external flash memory.
- Speed: much faster than I²C; megabits per second are common.
On Arduino you use the SPI library.
Example: send a command over SPI¶
This example shows how to send a single byte command to an SPI device:
#include <SPI.h>
// Chip Select pin for the SPI device (GPIO 5 is free on many ESP32 DevKit boards)
const int CS_PIN = 5;
void setup() {
// Configure CS pin
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH); // deselect device
// Initialize SPI for ESP32 VSPI: SCK=18, MISO=19, MOSI=23, CS=CS_PIN
SPI.begin(18, 19, 23, CS_PIN);
// Optional: Serial for debug
Serial.begin(115200);
}
void loop() {
uint8_t command = 0xA5; // example command byte
// Select the device by pulling CS low
digitalWrite(CS_PIN, LOW);
// Transfer one byte; this both sends and receives a byte
uint8_t response = SPI.transfer(command);
// Deselect the device again
digitalWrite(CS_PIN, HIGH);
Serial.print("Sent 0x");
Serial.print(command, HEX);
Serial.print(", received 0x");
Serial.println(response, HEX);
delay(1000);
}
For real peripherals (e.g. displays or SD cards) you usually:
- Configure SPI mode (0–3), clock speed, and bit order to match the datasheet.
- Use a dedicated Arduino library that wraps the low‑level SPI commands.
When to choose which protocol?¶
-
Serial (UART)
Use for debugging, logging to a PC, or simple point‑to‑point links between two boards. -
I²C
Use when you want to connect several low‑speed peripherals (sensors, small displays) using just two wires. -
SPI
Use for fast data transfer (displays, SD cards, high‑speed sensors) where you can afford a few extra wires.
Most real projects mix these protocols: for example Serial for debugging, I²C for sensors, and SPI for a display or SD card, all on the same Arduino‑compatible board.