The No B.S Guide to Embedded Communication Protocols
As computation accelerates and intelligent systems become more complex, hardware is once again taking center stage. Behind every optimized model and high throughput system lies a network of components that must communicate reliably and efficiently.
Embedded communication protocols form that foundation. This article breaks down how UART, I²C, SPI, and USB enable devices to exchange data revealing the mechanisms that quietly support modern computing.
Broccoli note : This article is split into eight sections and built around four hands-on projects. Three focus on individual communication protocols UART, I²C, and SPI and the final project combines SPI and I²C to build a simple weather station. I assume basic familiarity with the Arduino IDE (uploading code, installing libraries, and using the Serial Monitor), and I’ve linked resources if you need a refresher. Even if you’ve never worked with hardware before, you can still follow along concepts are introduced from the ground up, including clocks, bits, serial vs. parallel communication, and common terminology, before being used. Each project is independent, so you don’t need to build everything pick one, gather only the required components, and start there. By the end, you’ll not only understand how devices communicate, but you’ll also be able to build working electronics of your own, including a complete weather station.
Getting started with Arduino IDE : Getting started with Arduino ide
SECTION 1: Talking Without Words What Are Communication Protocols?
Imagine two people trying to have a conversation in different languages. Without a shared language, communication breaks down completely. The same problem exists in electronics. Microcontrollers, sensors, displays, and modules all need to exchange data but without a shared set of rules, the signal would be nothing but noise. That shared set of rules is called a communication protocol.
What Is a Communication Protocol in Embedded Systems?
A communication protocol is a standardized agreement that defines:
- How data is formatted (what pattern of signals = 0 or 1)
- How fast data is sent (speed / baud rate)
- Who talks when (which device sends, which listens)
- How errors are detected (so corruption doesn't go unnoticed)
- How devices are identified (especially when sharing a wire)
Why Do We Need Them?
Without protocols, every hardware manufacturer would invent their own way of communicating. Protocols solve this by providing universally agreed upon standards, which means:
- A GPS module from one brand talks to an Arduino from another
- Engineers worldwide share the same knowledge base
- Debugging is predictable you know what to expect on the wire
- Libraries and community support already exist
Where Are They Used?
They are literally everywhere in modern electronics:
- Your smartphone - display talks to processor over SPI; sensors use I2C
- Your laptop - USB connects keyboard and mouse; UART used internally for debugging
- IoT devices - temperature, humidity, gas sensors communicate over I2C or SPI.
- Vehicles - the CAN bus connects every ECU in your car
- Wearables - smartwatches read gyroscopes over I2C
- Industrial gear - factory machines use Modbus or RS-485
SECTION 2: The Language of Hardware Bits, Clocks & Signals
Before diving into specific protocols, you need to understand the building blocks. These concepts will make everything else will click.
What Is a Bit?
At the most fundamental level, digital electronics understand only two states: ON (1) and OFF (0). A bit is a single unit of this binary information a voltage that is either HIGH (typically 3.3V or 5V) or LOW (0V). Eight bits grouped together form a byte, which can represent any value from 0 to 255. When you send a sensor reading, a character, or a command you're sending a sequence of bits. Think of a bit like a light switch: on or off.A byte is like 8 switches in a row 256 possible combinations.
Serial vs. Parallel Communication
- Serial - bits are sent one at a time over a single wire. Fewer wires, works well over distance. UART, SPI, and I2C are all serial.
- Parallel - multiple bits sent simultaneously over multiple wires. Faster but needs more connections and is more prone to interference. Used inside processors.
Almost all protocols covered here are serial and that's perfectly fine. Modern serial protocols are fast enough for the vast majority of embedded applications.
What Is a Clock Signal and Why Does It Matter?
A clock signal is a regular repeating pulse like a metronome that tells both devices when to read or send the next bit.Without it, one device might send a bit while the other isn't ready yet.
There are two types:
- Synchronous - both devices share a dedicated clock wire. The receiver reads data on each clock pulse. SPI and I2C are synchronous.
- Asynchronous - no shared clock wire. Both sides agree on a speed (baud rate) in advance and use their own internal clocks. UART is asynchronous.
NOTE: If you've ever seen garbled text in Arduino's Serial Monitor, it's almost always a baud rate mismatch the classic asynchronous clock problem!
Key Terms You'll See Everywhere
- Baud rate - signal changes per second; roughly equals bps
- TX / RX - Transmit / Receive. Always cross-connect:
- TX of device A -- RX of device B
- Master - the device that initiates communication
- Slave - the device that responds
- Full duplex - both devices can send and receive simultaneously
- Half duplex - only one device transmits at a time
- SDA - Serial Data line (I2C)
- SCL - Serial Clock line (I2C)
- MOSI - Master Out Slave In (SPI)
- MISO - Master In Slave Out (SPI)
- CS / SS - Chip Select / Slave Select (SPI)
SECTION 3: UART The Grandfather of Serial Communication
What Is UART?
UART stands for Universal Asynchronous Receiver-Transmitter. One of the oldest and simplest serial protocols and still everywhere. UART has no shared clock. Both devices agree on a baud rate beforehand and each manages its own timing.
Where Will You Find UART?
- Arduino Serial Monitor - Serial.println() uses UART over USB
- GPS modules - Neo-6M sends NMEA sentences at 9600
- Bluetooth modules - HC-05, HC-06 talk over UART
- GSM/LTE modules -SIM800L uses UART AT commands
- ESP8266 / ESP32 -AT command firmware runs over UART
- Debug consoles - almost every processor has a UART for boot and debug output
How a UART Frame Works
Each UART transmission is wrapped in a frame:
- Start bit (1 bit, always LOW) - signals start of a byte
- Data bits (usually 8 bits) - actual data, LSB first
- Parity bit (optional) - simple error check
- Stop bit (1 bit, always HIGH) - marks end of frame
Common shorthand: "9600 8N1" = 9600 baud, 8 data bits, No parity, 1 stop bit. This is the default for most hobbyist modules.
Project 1 : Using NEO-6M GPS Module with Arduino
Objective:
The objective of this project is to establish UART communication between an Arduino and a GPS module and to verify that the GPS is transmitting valid positioning data by receiving and displaying standard NMEA sentences.
This project helps confirm that the GPS module is powered correctly, communicating at the correct baud rate, and actively sending navigation data over UART.
Parts Required
- Arduino uno R3
- NEO-6M GPS Module
- Few jumper wires
- A bread board *optional
Arduino Code
#include <SoftwareSerial.h>
SoftwareSerial gps(4, 3); // RX, TX gps rx arduino tx and vice versa
String gpsLine = "";
void setup() {
Serial.begin(9600);
gps.begin(9600);
Serial.println("GPS Line Reader Started.");
}
void loop() {
while (gps.available()) {
char c = gps.read();
if (c == '\n') { // End of one NMEA sentence
if (gpsLine.startsWith("$GPRMC") || gpsLine.startsWith("$GPGGA")) {
Serial.println(gpsLine); // Print only useful sentences
}
gpsLine = ""; // Clear for next line
}
else {
gpsLine += c; // Build the sentence
}
}
}So what does this code do?
This code establishes UART serial communication between an Arduino and a GPS module using software based serial pins. The Arduino continuously listens for data sent by the GPS module and reads it byte by byte. Incoming data is grouped into complete NMEA sentences, and only the important GPS messages are displayed on the Serial Monitor. The program acts as a UART data bridge, allowing the user to verify that the GPS module is transmitting valid data at the correct baud rate and that the Arduino is receiving it correctly.
Line by line working
#include <SoftwareSerial.h>
This library allows the Arduino to create an additional UART interface using digital pins. It is commonly used when the hardware UART is already occupied or unavailable.
SoftwareSerial gps(4, 3);
This line defines the UART connection by specifying the receive (RX) and transmit (TX) pins. In UART communication, RX and TX must be correctly connected between devices for data transfer to work.
Serial.begin(9600);
Starts serial communication with your computer so you can see output in the Serial Monitor
gps.begin(9600);
Starts UART communication with the GPS module at 9600 baud, which is the standard baud rate for most GPS modules.
gps.available();
This function checks whether new data has arrived on the UART receive buffer. Reading data without this check can result in invalid or missed data.
gps.read();
This reads incoming data one byte at a time from the UART buffer. UART communication is inherently byte based, and higher level messages are built by combining these bytes.
Wiring:
- GPS TX - Arduino pin 4
- GPS RX - Arduino pin 3
- GPS GND - Arduino GND
- GPS VCC - 3.3V or 5V (check your module)
RULE: TX always connects to RX. Never TX to TX!
Expected Output On the Serial Monitor, you’ll see raw NMEA sentences, like: this
$GPRMC,134559.00,A,.... $GPGGA,134559.00,....
Some Deatils about Project 1 Optional read
Why the GPS Outputs These Strange Lines ??
When a GPS module is powered on, it does not send latitude and longitude in a simple readable format. Instead, it continuously sends standardized data messages known as NMEA sentences over UART.
These messages are defined by an international standard so that any GPS receiver can work with any GPS software or microcontroller. The GPS sends these messages repeatedly, usually several times per second. That is why, as soon as UART communication starts, you see continuous lines of data being transmitted.
What Do $GPGGA and $GPRMC Mean?
Each NMEA sentence starts with a message identifier, which tells us what type of data the line contains. $GP This prefix means the data comes from the GPS system (Global Positioning System).
$GPGGA Fix and Location Data , GPGGA sentences contain core positioning information, such as:
- Time of the fix
- Latitude and longitude
- GPS fix status (whether the GPS has a lock)
- Number of satellites in use
- Altitude
$GPRMC -Recommended Minimum Data, GPRMC is one of the most useful GPS sentences. It contains:
- Time and date
- Latitude and longitude
- Speed over ground
- Direction of movement
- Status (valid or invalid fix)
Why the Code Filters These Sentences
The GPS module sends many different types of NMEA sentences. To keep the output clean and meaningful, the code only prints:
This makes the UART output easier to understand while still proving that the GPS is working correctly. This makes the UART output easier to understand while still proving that the GPS is working correctly.
congratulations you successfully made your first project��
SECTION 4: SPI Speed When You Need It
# What Is SPI?
SPI (Serial Peripheral Interface) is a synchronous, full duplex protocol developed by Motorola. It uses a shared clock line, making it faster and more reliable than UART. It supports multiple devices but requires more wires.
Key characteristics:
- Wires : 4 minimum MOSI, MISO, SCK, and one CS per device
- Speed : 1 Mbps to 80+ Mbps Devices : One master; multiple slaves (one CS wire each)
- Clock : Shared clock (SCK) from master synchronous
- Duplex : Full master and slave exchange data at same time
Where Will You Find SPI?
- TFT/LCD displays - ILI9341, ST7735 need speed for graphics
- SD card modules - SD cards use SPI in compatibility mode
- SPI Flash chips - W25Q32 for config and program storage
- RF modules - nRF24L01, LoRa RFM95 for wireless
- ADC/DAC chips - MCP3008, MCP4921 for analog conversion
- IMUs - MPU-9250 or BME280 in SPI mode for fast sampling
How SPI Works
The master controls the clock (SCK). To talk to a specific slave, the master pulls that slave's CS pin LOW. Data flows simultaneously in both directions: MOSI (master-slave) and MISO (slave-master) on the same clock edge.
NOTE: SPI has four modes (0–3) based on clock polarity and phase. If your device won't communicate, check its datasheet for the correct mode. Most hobbyist sensors use Mode 0.
Project 2 Using BMP280 Sensor with Arduino
Objective:
The objective of this project is to interface a BMP280 sensor with an Arduino Uno using the SPI communication protocol. The project demonstrates how to configure SPI and read temperature and pressure data from the sensor.
Parts Required :
- Arduino uno
- BMP-280 sensor
- jumper wires
- bread board *optional
Arduino Code
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#define BMP_CS 10
Adafruit_BMP280 bmp(BMP_CS); // SPI constructor
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("BMP280 SPI test");
if (!bmp.begin()) {
Serial.println("BMP280 not detected");
while (1);
}
Serial.println("BMP280 detected over SPI");
}
void loop() {
Serial.print("Temp: ");
Serial.print(bmp.readTemperature());
Serial.println(" °C");
Serial.print("Pressure: ");
Serial.print(bmp.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.println("---------------------");
delay(2000);
}What Does This Code Do?
This code enables the Arduino Uno to communicate with a BMP280 sensor using SPI. In the setup() function, serial communication is initialized and the BMP280 sensor is started using SPI. If the sensor is not detected, the program stops execution. In the loop() function, the Arduino reads temperature and pressure values from the BMP280 sensor and displays them on the serial monitor at regular intervals.
Key Lines in SPI to Work
#include <SPI.h>
This line enables hardware SPI support on the Arduino. Without this, the Arduino literally doesn’t know how to talk SPI.
#define BMP_CS 10
This line defines the Chip Select (CS) pin for the BMP280 sensor. In SPI communication, multiple devices can share the same data and clock lines, so the CS pin is used to select which device is currently active. When pin 10 is driven LOW, the BMP280 is enabled for communication.
Note : The SPI library automatically drives the CS pin LOW when communicating with the BMP280 and drives it HIGH when communication ends.
Adafruit_BMP280 bmp(BMP_CS);
Initializes the BMP280 sensor in hardware SPI mode and assigns the CS pin.
if (!bmp.begin()) {Starts SPI communication with the BMP280 sensor and checks whether the sensor is connected properly.
Adafruit_BMP280 bmp(BMP_CS);
if (!bmp.begin()) {
Serial.println("BMP280 not detected");
while (1);
}The core functionality of this project lies in initializing the sensor over SPI and reading data from it.This initializes the BMP280 sensor using SPI and ensures that communication is established before proceeding.
Wiring:
- SCK (SPI Clock) - Pin 13
- SDO (MISO) - Pin 12
- SDA (MOSI) - Pin 11
- CS (Chip Select) - Pin 10
- VCC -3.3V
- GND - GND
Expected Result
After uploading the code and opening the Serial Monitor at 9600 baud, the Arduino successfully communicates with the BMP280 sensor over SPI and displays temperature and pressure readings at regular intervals.
- Temperature values in degrees Celsius (°C)
- Pressure values in hectopascals (hPa)
- A separator line printed after each reading cycle
BMP280 SPI test BMP280 detected over SPI Temp: 29.53 °C Pressure: 967.62 hPa --------------------- Temp: 29.53 °C Pressure: 967.65 hPa --------------------- Temp: 29.52 °C Pressure: 967.63 hPa ---------------------
congratulations you successfully made your Second project��
SECTION 5: I2C One Bus to Rule Them All
What Is I2C?
I2C (Inter-Integrated Circuit, "I-squared-C") was developed by Philips in 1982. Its superpower: connect up to 127 devices using just two wires. Each device has a unique address, so the master can select exactly who it wants to talk to.
Key characteristics:
- Wires : 2 SDA (data) and SCL (clock)
- Speed : 100 kbps (standard), 400 kbps (fast), 1 Mbps (fast+), 3.4 Mbps (high-speed)
- Devices : Up to 127 on a single bus
- Clock : Shared (SCL) from master synchronous
- Requires : Pull-up resistors on SDA and SCL (usually 4.7kΩ)
Where Will You Find I2C?
- Temp/humidity sensors -DHT12, SHT31, BME280
- OLED displays -SSD1306 128x64 (most use I2C)
- IMUs -MPU-6050 accelerometer/gyroscope
- Real-Time Clocks -DS3231, PCF8523
- EEPROM chips -24C32 for non volatile storage
- I/O expanders - PCF8574 adds 8 GPIO on 2 wires
- Light/colour/pressure - almost all use I2C by default
I2C Addressing
What is an I²C Address and Why Is It Important?
When you use I²C communication, multiple sensors can share the same two wires: SDA (data) and SCL (clock). Since all devices are connected to the same bus, the Arduino needs a way to know which sensor it is talking to at any given moment. This is where the I²C address comes in. An I²C address is a unique number assigned to each device on the I²C bus. You can think of it like a house number on a street. Even though all houses share the same road, each one has a different address so deliveries go to the right place. In the same way, the Arduino sends data along the I²C bus and includes the address of the sensor it wants to communicate with.
One of the most common mistakes beginners make (I made it to lol) is assuming the sensor address without checking it. In Many tutorials mention a “default” address, but in real life, the address can change depending on how the sensor is wired. For example, some sensors have an address select pin that switches between two different addresses. If the wrong address is used in the code, the sensor will simply not respond and nothing will show up on the Serial Monitor.
This can be confusing because the wiring may be correct and the sensor may be powered properly, but the code still does not work. In most cases, the issue is not the sensor or the Arduino, but an incorrect I²C address.
Can You Change an I²C Address? it depends on the sensor.
I²C addresses are usually fixed in hardware, but many sensors give you limited control over them. You typically cannot choose any address you want only the options the manufacturer allows.
#include <Wire.h>
void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("I2C Scanner Started...");
}
void loop() {
byte error, address;
int devices = 0;
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
devices++;
}
}
if (devices == 0) {
Serial.println("No I2C devices found");
} else {
Serial.println("Scan complete");
}
delay(3000);
}This code will help you to find I2C adress of your device
How this code works
- Arduino scans all possible I²C addresses (1–126)
- It sends a request to each address
- If a device responds with an ACK, the address is printed
- Runs repeatedly every 3 seconds
Expected Output
I2C Scanner Started... I2C device found at address 0x68 Scan complete
THIS IS YOUR ADDRESS = 0x68 use it in your code each device has its own unique address
Every device has a 7 bit address (0x00 to 0x7F). Some devices let you change the last 1–3 bits via hardware pins (A0, A1, A2) so you can put multiple copies of the same device
TIP: Not sure what address your sensor uses? Run the I2C Scanner sketch first. It finds everything on the bus and prints the address for you.
Note: : I2C pull up resistors are mandatory. Without them the bus won't work. Most breakout boards include them but if you're using raw chips, add 4.7kΩ from SDA and SCL to VCC.
Project 3 : Using MPU650 Sensor with Arduino
Objective : To establish I²C communication between an Arduino and the MPU6500 sensor and demonstrate its basic functionality by reading raw accelerometer and gyroscope data.
Parts required :
- Arduino Uno
- MPU 650
- Jumper wires
- bread board *optional
Arduino code
#include <Wire.h>
#define MPU_ADDR 0x68
void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("MPU6500 I2C Test");
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6B);
Wire.write(0x00);
Wire.endTransmission();
}
void loop() {
int16_t ax, ay, az;
int16_t gx, gy, gz;
// Start reading from ACCEL_XOUT_H register
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 14);
ax = Wire.read() << 8 | Wire.read();
ay = Wire.read() << 8 | Wire.read();
az = Wire.read() << 8 | Wire.read();
Wire.read(); Wire.read(); // Skip temperature
gx = Wire.read() << 8 | Wire.read();
gy = Wire.read() << 8 | Wire.read();
gz = Wire.read() << 8 | Wire.read();
Serial.print("Accel (X Y Z): ");
Serial.print(ax); Serial.print(" ");
Serial.print(ay); Serial.print(" ");
Serial.println(az);
Serial.print("Gyro (X Y Z): ");
Serial.print(gx); Serial.print(" ");
Serial.print(gy); Serial.print(" ");
Serial.println(gz);
Serial.println("-------------------------");
delay(1000);
}What does this code do ?
The Arduino communicates with the MPU6500 using the I²C protocol, where the Arduino acts as the master and the MPU6500 acts as the slave device. Communication occurs over the SDA and SCL lines using the sensor’s I²C address. At startup, the MPU6500 is taken out of sleep mode. The Arduino then continuously reads accelerometer and gyroscope registers over I²C and displays the raw sensor values on the Serial Monitor. Changes in these values correspond to physical movement and rotation of the sensor.
Key I²C Code Lines Explained
#include <Wire.h>
Enables I²C communication on the Arduino using the Wire library.
#define MPU_ADDR 0x68
Defines the I²C address of the MPU6500 sensor used for all data transactions.
Wire.begin();
Initializes the Arduino as the I²C master and prepares the I²C bus for communication.
Wire.beginTransmission(MPU_ADDR); Wire.write(0x6B); Wire.write(0x00); Wire.endTransmission();
Writes to the power management register to wake the MPU6500 from sleep mode, allowing sensor data to be read.
Wire.beginTransmission(MPU_ADDR); Wire.write(0x3B); Wire.endTransmission(false);
Sets the starting register address for reading accelerometer and gyroscope data while keeping the I²C connection active.
Wiring:
- SDA - A4
- SCL - A5
- VCC - 3.3V
- GND - GND
- AD0 - GND (I²C address = 0x68)
Expected Output
When you move or rotate the sensor, values will change:
Accel (X Y Z): 24 -1420 16652 Gyro (X Y Z): 133 419 -26 ------------------------- Accel (X Y Z): 104 -1504 16580 Gyro (X Y Z): 157 396 -20 ------------------------- Accel (X Y Z): 20 -1440 16592 Gyro (X Y Z): 135 423 -28 ------------------------- Accel (X Y Z): 20 -1448 16576 Gyro (X Y Z): 139 376 -35 -------------------------
congratulations you successfully made your Third project��
SECTION 6: USB The Protocol Hiding in Plain Sight
What Is USB?
USB (Universal Serial Bus) is the dominant protocol for connecting peripherals to computers and for device to device embedded communication. You use it every day: phone charging, keyboards, flash drives, Arduino programming. All USB. USB is significantly more complex than UART, SPI, or I2C under the hood. It involves device enumeration, descriptor tables, and differential signaling. But for embedded work, libraries handle all of that complexity.
Key characteristics:
- Wires : 4 for USB 2.0 - VBUS, GND, D+, D-
- Speed : USB 2.0 = 480 Mbps | USB 3.2 = 20 Gbps
- Devices : Up to 127 via hubs
- Structure: Host/Device the host (PC) controls the bus
- Power : USB 2.0 delivers 500mA; USB-C PD up to 240W
NOTE: USB C is a connector shape, not a protocol. It can carry USB 2.0, USB 3.x, Thunderbolt, or DisplayPort depending on what's inside the device.
Where Will You Find USB in Embedded Systems?
- Arduino Leonardo / Micro / Uno R4 act as HID or serial
- Raspberry Pi Pico - native USB, emulates keyboard, storage, serial
- ESP32-S2 / S3 - native USB OTG
- STM32 microcontrollers - USB Full Speed built-in
- USB-to-UART bridges (CH340, FTDI) on nearly every Arduino clone convert USB to UART
- Data loggers - appear as USB mass storage
SECTION 7: Choosing the Right Protocol for Your Project
The decision comes down to five things: number of devices, speed required, wire count, distance, and PC connectivity.
Quick Comparison
Use UART when
- You're connecting exactly two devices
- You need a debug channel (Serial Monitor)
- Your module only offers UART (GPS, Bluetooth, GSM)
- You need longer distance with RS-232 or RS-485 drivers
- Simplicity matters no library needed, built into every board
Use SPI when
- You need the fastest possible transfer (displays, SD cards)
- Full-duplex matters send and receive at the same time
- You have a small number of peripherals
- Your device offers both SPI and I2C always pick SPI for speed
Use I2C when
- You need many sensors but want minimal wires
- Speed is not critical (under 400 kbps is fine for most sensors)
- You're chaining many identical sensors with different addresses
- Board space matters 2 wires for the entire bus is a big win
Use USB when
- You need to communicate with a PC or smartphone
- You want plug-and-play with no custom drivers
- You need to deliver meaningful power through the connection
- You want the device to appear as a keyboard, drive, or audio card
Project: Using SPI and I²C Together
Objective :
This project demonstrates how two different communication protocols can be used together in a single embedded system. SPI is used to read sensor data, while I²C is used to display that data, showing how multiple protocols can coexist on the same microcontroller.
Parts required:
- Arduino uno
- BMP280
- OLED Display
- few jumper wires
- bread board
Overview:
An Arduino Uno reads temperature and pressure data from a BMP280 sensor using SPI. The measured values are then displayed on a 0.96-inch OLED screen using I²C. Each device communicates over its own bus, allowing both protocols to run simultaneously without interference.
How It Works:
The Arduino continuously reads data from the sensor via SPI and sends the processed values to the OLED display over I²C. This simple data flow demonstrates how different protocols can be combined in practical applications.
Wiring Diagram
BMP280 Pin Arduino Uno Pin
- VCC - 3.3V
- GND - GND
- CS - D10
- SDI - D11
- SDO - D12
OLED Pin Arduino Uno Pin
- VCC 5V
- GND GND
- SCL A5
- SDA A4
Arduino code
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define BMP_CS 10
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_BMP280 bmp(BMP_CS);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(9600);
Wire.begin();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
while (1);
}
if (!bmp.begin()) {
while (1);
}
}
void loop() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print("Temp: ");
display.print(bmp.readTemperature());
display.println(" C");
display.setCursor(0, 20);
display.print("Press: ");
display.print(bmp.readPressure() / 100.0F);
display.println(" hPa");
display.display();
delay(2000);
}congratulations you successfully made your final project🥳
SECTION 8: Summary Your Protocol Cheat Sheet
UART
2 wires | Asynchronous | Point-to-point only | up to ~1 Mbps Best for: debugging, GPS, Bluetooth, GSM modules Remember: TX to RX, always match baud rate on both sides
SPI
4+ wires | Synchronous | Full duplex | up to 80+ Mbps Best for: displays, SD cards, RF modules, fast sensors Remember: one CS wire per device; check SPI mode in datasheet
I2C
2 wires | Synchronous | Up to 127 devices | 100k–1 MbpsBest for: sensors, OLEDs, RTCs, EEPROMs, slow peripherals Remember: pull-up resistors required; use scanner for address .
What to Learn Next
- CAN Bus automotive and industrial, robust multi-master bus
- RS-485 industrial UART variant, long distance, multi-drop
- 1-Wire single wire protocol; used in DS18B20 temp sensors
- I2S I2C's cousin for audio; microphones and DAC chips
- Ethernet for devices that need to join a full TCP/IP network
FINAL THOUGHT:
The best way to solidify this knowledge: pick one protocol, grab a sensor or module that uses it, wire it up, get data on your Serial Monitor, and dig into what's happening electrically with a logic analyzer. Reading is good. Building is better.
And remember ..
The best embedded engineers aren't the ones who memorized the most protocols. They're the ones who know which tool to reach for and why.