Communication Protocols: I2C, SPI, and UART
Summary
Modern sensors and displays rarely connect through a single wire — they use standard communication protocols that let many devices share just a few wires. This chapter introduces the most common protocols you will encounter: I2C (two wires, up to 127 devices), SPI (four wires, very fast), UART (serial communication), 1-Wire (temperature sensors), and I2S (digital audio). You will learn to use MicroPython's machine.I2C, machine.SPI, machine.UART, and machine.I2S classes, and you will use an I2C scanner to discover which devices are connected to your bus — a skill that will save you hours of debugging.
Concepts Covered
This chapter covers the following 19 concepts from the learning graph:
- I2C Protocol
- I2C Bus SDA and SCL
- I2C Address
- I2C Scanner
- machine.I2C Class
- I2C.scan() Method
- I2C.writeto() Method
- I2C.readfrom() Method
- SPI Protocol
- SPI Bus Pins (MOSI MISO SCK CS)
- machine.SPI Class
- SPI.write() Method
- SPI.read() Method
- UART Protocol
- machine.UART Class
- 1-Wire Protocol
- I2S Protocol
- machine.I2S Class
- Bus Frequency Setting
Prerequisites
This chapter builds on concepts from:
Welcome to Chapter 8
Time to learn how your Pico talks to other chips! I2C, SPI, and UART are the languages of electronics. Once you know these three protocols, you can connect hundreds of different sensors, displays, and modules to your Pico. Let's build the vocabulary!
Why Communication Protocols?
Sensors, displays, and peripheral chips generate data as electrical signals. To transfer that data to a microcontroller reliably, both sides must agree on a set of rules: which wire carries data, which carries timing, how fast data travels, and how to signal the start and end of a message. These rules are a communication protocol.
Instead of a different wiring scheme for every sensor, a small number of standard protocols cover almost every device you will connect. The Pico supports four: I2C, SPI, UART, and 1-Wire, plus I2S for audio.
I2C — Two Wires, Many Devices
I2C (pronounced "I-squared-C", short for Inter-Integrated Circuit) is the most common protocol for connecting sensors and displays to a microcontroller. It uses exactly two wires:
- SDA (Serial Data) — carries the data bits.
- SCL (Serial Clock) — carries the timing signal that keeps both sides in sync.
The I2C bus is the pair of SDA and SCL wires that can carry traffic for many devices at once. Every device on the bus has a unique I2C address — a 7-bit number (0 to 127) that acts like a street address. The Pico calls out one address, and only the device with that address responds. Up to 127 different devices can share the same two wires.
On the Pico, the default I2C pins are: - I2C bus 0: SDA = GP0, SCL = GP1 - I2C bus 1: SDA = GP2, SCL = GP3 (or other pin pairs — check the pinout)
The machine.I2C Class
Create an I2C object by specifying the bus ID and pin numbers:
1 2 3 4 | |
The freq argument sets the bus frequency — how fast bits are sent. Standard I2C is 100 kHz; fast mode is 400 kHz. Most sensors support 400 kHz. Some slow sensors need 100 kHz.
I2C.scan() — The I2C Scanner
Before you can use a device, you need to know its address. The I2C.scan() method sends a probe to every possible address and returns a list of addresses that responded.
1 2 3 | |
Running the scanner is always the first step when connecting a new I2C device. A common address for OLED displays is 0x3C. The BME280 temperature sensor is typically 0x76.
Monty's Tip
Run the I2C scanner whenever a sensor does not respond. Wrong address is one of the top three causes of I2C failures. The other two are: wrong pin assignment (SDA and SCL swapped) and missing pull-up resistors (I2C lines need 4.7 kΩ pull-ups to 3.3 V; many breakout boards include them).
I2C.writeto() and I2C.readfrom()
The two fundamental I2C operations are:
I2C.writeto(address, data)— sends bytes to a device.I2C.readfrom(address, count)— reads a specified number of bytes from a device.
1 2 3 4 5 6 | |
In practice you rarely call these directly — driver libraries (like ssd1306.py) wrap them for you.
SPI — Four Wires, Maximum Speed
SPI (Serial Peripheral Interface) is faster than I2C but requires more wires. It is common for displays and SD card modules that need high data throughput. SPI uses four wires:
- MOSI (Master Out Slave In) — data from Pico to the device.
- MISO (Master In Slave Out) — data from the device to the Pico.
- SCK (Serial Clock) — timing signal.
- CS (Chip Select, sometimes called SS) — one wire per device; pulled LOW to select that device.
Because CS wires are separate per device, SPI does not use addresses. You select a device by pulling its CS line LOW before sending data.
The machine.SPI Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
UART — Serial Communication
UART (Universal Asynchronous Receiver/Transmitter) is the oldest and simplest protocol. It uses just two wires — TX (transmit) and RX (receive) — and is used for GPS modules, Bluetooth adapters, and the USB-serial connection from your Pico to Thonny.
UART is asynchronous — there is no shared clock wire. Both sides must agree on the same baud rate (speed in bits per second) in advance.
1 2 3 4 5 6 7 8 | |
Common baud rates: 9600, 115200. GPS modules typically use 9600; Bluetooth adapters often use 115200.
1-Wire — One Wire for Temperature Sensors
The 1-Wire protocol is unusual: it carries both data and power on a single wire (plus GND). It is almost exclusively used for the DS18B20 waterproof temperature sensor, which is common in robotics and weather projects.
MicroPython includes a onewire module and a ds18x20 module for these sensors. You will use 1-Wire in Chapter 9.
I2S — Digital Audio
I2S (Inter-IC Sound) is a protocol for transferring digital audio data between chips. It is used to connect the Pico to digital microphones (MEMS mics) and audio DACs (digital-to-analog converters) for playing WAV files.
1 2 3 4 5 6 | |
You will use I2S in Chapter 18 (Sound and Audio).
Diagram: Protocol Comparison Explorer
Communication Protocol Comparison MicroSim
Type: diagram
sim-id: protocol-comparison
Library: p5.js
Status: Specified
Bloom Level: Understand (L2) Bloom Verb: compare Learning Objective: Students can select the appropriate protocol (I2C, SPI, UART, 1-Wire) for a given sensor or peripheral based on its wire count, speed, and use case.
Canvas layout: - Top: protocol selector tabs: I2C | SPI | UART | 1-Wire | I2S - Center: animated waveform showing the selected protocol's bus signals - Bottom: summary panel: wire count, typical speed, max devices, best for
Visual elements: - I2C: two waveforms (SDA, SCL); address byte highlighted in purple; data bytes in blue - SPI: four waveforms (SCK, MOSI, MISO, CS); CS pulse visible - UART: two waveforms (TX, RX); start/stop bits highlighted - 1-Wire: single waveform with presence pulse animation
Interactive controls: - Tab buttons to switch protocol - A "Send data" button triggers a one-shot waveform animation - Bottom panel updates automatically with: Wires, Speed, Addressing, Best use
Instructional Rationale: Side-by-side animated waveforms with a clear summary panel give students a mental model to match protocols to real devices before they wire anything.
Implementation: p5.js. Each protocol stored as an array of timed state changes; draw() animates the waveform; createButton() for tab switching.
Choosing the Right Protocol
Before the summary table, here is how to reason about protocol selection:
- If the device has two wires labeled SDA and SCL — use I2C. Check the address in the datasheet.
- If the device has MOSI, MISO, SCK, and CS — use SPI. It will be faster than I2C.
- If the device has TX and RX — use UART. Match the baud rate exactly.
- If the device has a single data wire and is a temperature sensor — use 1-Wire.
- If the device sends or receives PCM audio — use I2S.
| Protocol | Wires | Speed | Devices | Common Uses |
|---|---|---|---|---|
| I2C | 2 (SDA, SCL) | 100–400 kHz | Up to 127 | OLED, sensors, RTCs |
| SPI | 4 (MOSI, MISO, SCK, CS) | 1–80 MHz | 1 per CS | Displays, SD cards, ADCs |
| UART | 2 (TX, RX) | 1,200–115,200 baud | 1:1 only | GPS, Bluetooth, debug |
| 1-Wire | 1 + GND | ~16 kbps | Many | DS18B20 temperature |
| I2S | 3 (SCK, WS, SD) | Audio rates | 1 | Microphones, DACs |
I2C or SPI: Which Should I Choose?
Choose I2C when you want simplicity and you have multiple sensors (they share two wires). Choose SPI when you need speed — especially for displays that must update quickly. Many devices (like the BME280 sensor) support both; the breakout board determines which connector you use.
Key Takeaways
- I2C uses SDA + SCL (2 wires) with unique device addresses; up to 127 devices per bus.
I2C.scan()lists all responding addresses — run it first when connecting any new I2C device.- SPI uses MOSI + MISO + SCK + CS (4 wires); faster than I2C; one CS per device.
- UART uses TX + RX (2 wires); match baud rates exactly; point-to-point only.
- 1-Wire uses one data wire; specialized for temperature sensors like DS18B20.
- I2S carries digital audio; used for microphones and audio playback chips.
- Bus frequency controls speed; I2C runs at 100–400 kHz; SPI can reach MHz speeds.
Quick Check: Which I2C method discovers all connected device addresses? (Click to reveal)
i2c.scan() — it probes all 127 possible addresses and returns a list of the ones that acknowledge.
You Speak the Language of Electronics!
I2C, SPI, and UART are the three handshakes that unlock almost every sensor and display on the market. In Chapter 9 you will use these protocols for real — reading temperature, humidity, and distance from physical sensors. The world is about to tell your Pico many interesting things!