Serial Protocols
Protocol Comparison
| Property | Serial (UART) | SPI | I2C | PWM |
|---|---|---|---|---|
| Wires | 2 (TX, RX) | 4+ (SCK, COPI, CIPO, CS×n) | 2 (SDA, SCL) | 1 |
| Clock | No (async) | Yes (sync) | Yes (sync) | N/A |
| Max devices | 2 | Many (1 CS per device) | 1008 | 1 |
| Speed | Slow | Fast | Medium | N/A |
| Complexity | Low | Medium | Medium | Low |
| Python library | pyserial | — | smbus2 | gpiozero |
Synchronous vs Asynchronous
- Synchronous (SPI, I2C): shared clock wire — devices stay in sync
- Asynchronous (UART): no clock — both sides must agree on baud rate
UART / Serial
- Point-to-point only (2 devices)
- Raspberry Pi GPIO14 = TX, GPIO15 = RX
- Device path:
/dev/ttyS0or/dev/serial0 - No clock wire — agree on baud rate in advance
import serial
# Context manager pattern (recommended)
with serial.Serial('/dev/serial0', 9600) as ser:
while True:
line = ser.readline() # reads until '\n'
print(f'Line: {line}')
if line == b'hello\n':
break
ser.write(b'there\n')MicroPython (Pico side)
from machine import UART, Pin
uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
uart0.write('hello\n')SPI (Serial Peripheral Interface)
- Has clock wire (SCK) — synchronous
- Full-duplex (simultaneous bidirectional data)
- CS (Chip Select) = active LOW — controller pulls CS low to select peripheral
- One CS pin needed per peripheral device
| Signal | Meaning |
|---|---|
| SCK | Clock (generated by controller) |
| COPI | Controller-Out Peripheral-In (data to peripheral) |
| CIPO | Controller-In Peripheral-Out (data from peripheral) |
| CS | Chip Select (active LOW, one per peripheral) |
Old names (deprecated): MOSI/MISO/SS
I2C (Inter-Integrated Circuit)
- Only 2 wires: SDA (data) + SCL (clock)
- Up to 1008 peripheral devices on same bus
- Each device has unique hexadecimal address
- Slower than SPI
Detect I2C devices
i2cdetect -y 1
# Shows grid of hex addresses; UU = in use by kernel driverPython with smbus2
from smbus2 import SMBus
with SMBus(1) as bus:
b = bus.read_byte_data(80, 0) # read 1 byte from addr 80, register 0
print(b)
bus.write_byte_data(80, 0, 45) # write byte 45 to addr 80, register 0In practice: use device-specific libraries (e.g.,
grove_temperature_humidity_aht20)
I2C Devices in Course
- AHT20 temperature & humidity sensor
- reTerminal accelerometer
- reTerminal light sensor
- reTerminal LCD driver
PWM (Pulse Width Modulation)
Digital signal that simulates analog by varying duty cycle.
- Signal always HIGH (3.3V) or LOW (GND) — never in between
- Duty cycle = % of time HIGH in one period
- 0% = always off, 50% = half, 100% = always on
Applications
- LED dimming: fast on/off tricks the eye
- Servo motors: pulse width = arm position
from gpiozero import PWMLED, Servo
from time import sleep
# LED brightness (0=off, 1=full)
led = PWMLED(17)
led.value = 0 # off
led.value = 0.5 # half brightness
led.value = 1 # full brightness
# Servo
servo = Servo(17)
servo.min() # minimum position
servo.mid() # middle position
servo.max() # maximum positionSee Also
- UART Serial concept, SPI concept, I2C concept, PWM concept
- GPIO and reTerminal topic
- serial-protocols source