>
section 3 of 1810 min read

3. Serial Communication, in Detail

Almost no embedded system stands alone. A sensor speaks I2C to the MCU, the MCU speaks SPI to a flash chip, a debug console runs over UART, automotive ECUs gossip over CAN, a USB port lets a tech reflash firmware. These four buses (UART, SPI, I2C, CAN) plus USB and Ethernet make up 95 % of all embedded comms.

3.1 UART

UART (Universal Asynchronous Receiver/Transmitter) is the simplest serial protocol. Two wires per direction (TX, RX) plus a shared ground. There is no clock line. Both ends agree on a baud rate in advance and sample the line at the agreed cadence.

A UART frame:

plaintext
   idle   start   D0  D1  D2  D3  D4  D5  D6  D7  parity  stop   idle
  -----+\_____ /XX\/XX\/XX\/XX\/XX\/XX\/XX\/XX\/XX\/-----+-----
       |          \data, LSB first/        |              |
       |                                                  |
       +-------------- one frame ------------------------+

WaveDrom of a 0x55 transmission, 8-N-1:

The receiver oversamples (typically 16x) so it can re-center on the start-bit edge and sample each data bit at its midpoint. Even with modest clock mismatch, the system is robust because the line returns to idle (high) between frames.

Baud-rate divisor. Given a UART peripheral clocked at fpclkf_\text{pclk}, you derive the divisor:

BRR=fpclk16baud\text{BRR} = \frac{f_\text{pclk}}{16 \cdot \text{baud}}

For 115200 baud on a 42 MHz APB1 with a 16x oversampler:

BRR=42,000,0001611520022.79\text{BRR} = \frac{42{,}000{,}000}{16 \cdot 115200} \approx 22.79

That non-integer is why UART hardware uses fractional dividers (mantissa + fraction). With an integer-only divider you would have to round to 23, getting actual baud 42e6 / (16 * 23) = 114130, off by 0.93 %. UART tolerates about 2 % per side, so you get away with it, but cheap RC oscillators eat into that budget. This is why crystal oscillators are recommended for UART above 115200 baud.

Parity. Optional eighth or ninth bit set so the total number of 1s is even (even parity) or odd (odd parity). Catches single-bit errors. Modern fast UARTs (USB-CDC virtual ports, BLE serial) skip parity and rely on framing.

Flow control. Hardware flow uses RTS/CTS pins to pause sender when receiver is full. Software flow uses XON/XOFF bytes. Without flow control, fast senders overrun slow receivers.

RS-232. UART at +/-12 V using a level-shifter (MAX232 family). What "old-school" serial ports used. Range a few meters, single-ended.

RS-485. UART over a differential pair, multi-drop, half-duplex, distances up to 1.2 km, dozens of nodes. Backbone of building automation (Modbus over RS-485) and some industrial protocols. The differential signaling rejects common-mode noise picked up over long cable runs.

Real-world. GPS modules speak NMEA-0183 over UART. Most ESP32 shells expose UART debug. A million Linux SBCs have their console on /dev/ttyS0. This is also where "open the BMC console at 115200" attacks start.

3.2 SPI

SPI (Serial Peripheral Interface) is the workhorse for fast communication with a known set of devices. Four wires:

  • SCLK. Clock from master.
  • MOSI (Master Out, Slave In). Data from master to slave.
  • MISO (Master In, Slave Out). Data from slave to master.
  • CS (chip select, active low). Master tells slave "your turn."

SPI is full-duplex: every clock cycle, master shifts a bit out and in simultaneously. Speeds easily 10-50 MHz, sometimes 100+. No addressing on the bus: each slave gets a dedicated CS line.

Modes (CPOL/CPHA). Two bits decide the clock polarity and phase, giving four modes 0-3. CPOL = 0 means clock idles low; CPOL = 1, idles high. CPHA = 0 means data is sampled on the first edge after CS asserts; CPHA = 1, on the second. SPI flash and most sensors use Mode 0. SD cards default to Mode 0. Some Maxim sensors use Mode 3.

plaintext
Mode 0 (CPOL=0, CPHA=0): idle low, sample on rising
   CS  ‾\___________________________/‾
   CLK ___/‾\_/‾\_/‾\_/‾\_/‾\_/‾\___
   MOSI    7 6 5 4 3 2 1 0           (latch on rising edge)

Daisy chain. Some SPI peripherals (LED drivers, ADC arrays) chain MOSI->slave->MOSI->slave so a single shift register can clock data into many devices. Drop one CS, shift N*8 bits, raise CS, all devices update at once. Used for LED matrix displays and multi-channel DACs.

Real-world. SD cards (in SPI mode), W25Q SPI flash, MAX31855 thermocouple ICs, MPU9250 IMU, NRF24L01 radios. SPI flash is the boot store on most modern SoCs (read at boot, copy code to internal RAM, jump). That is why SPI flash sniffing is an attack surface: a logic analyzer on the boot clock recovers the firmware unencrypted.

3.3 I2C

I2C (Inter-Integrated Circuit) is Philips's two-wire bus, ubiquitous on every PCB with more than two chips on it. Two open-drain lines:

  • SDA (data).
  • SCL (clock).

Both pulled up by external resistors (typical 4.7 kohm at 100 kHz, 2.2 kohm at 400 kHz). Any device can pull either low; nobody can drive high. That open-drain rule is what makes multi-master and clock-stretching possible.

Frame.

plaintext
   START   ADDR(7) R/W  ACK  DATA(8) ACK  ...  STOP
   _____         ____         ___________
SDA      \______/    \___/   X    DATA   X___/
SCL  ‾‾\__/‾\__/‾\__/‾\__/‾\__/‾\__/‾\__/‾‾

A START is SDA falling while SCL is high. A STOP is SDA rising while SCL is high. Between START and STOP, SDA only changes while SCL is low; that is what lets the receiver use SDA-while-SCL-high as the protocol's edge events.

Addressing. 7-bit (most common), so up to 128 devices, of which 16 addresses are reserved (0x00-0x07 and 0x78-0x7F). 10-bit extended mode exists but is rare.

ACK/NACK. After every byte the receiver pulls SDA low for the ninth clock to acknowledge. NACK (leaving SDA high) means "stop talking" or "address not present."

Clock stretching. A slow slave can hold SCL low to delay the master. Most masters tolerate this; some hardware bugs do not (early Raspberry Pi I2C had a clock-stretching bug).

Multi-master arbitration. Two masters might try to start simultaneously. Because the bus is open-drain, a master that wants to drive SDA high but sees the line still low knows someone else is driving low and silently backs off. The "winner" continues unaware. This bit-by-bit arbitration is elegant but slow.

Speeds: Standard 100 kHz, Fast 400 kHz, Fast+ 1 MHz, High-speed 3.4 MHz.

WaveDrom of an addressing phase:

Real-world. EEPROMs (24LC256), accelerometers (MPU6050), barometers (BMP280), OLED displays (SSD1306), real-time clocks (DS3231), TPM chips, system management buses (SMBus on PC motherboards).

3.4 CAN bus

CAN (Controller Area Network) is the bus that turned the modern car into a network. Bosch invented it in 1986 to reduce wiring weight; today every car has one or several CAN buses connecting up to 100 ECUs.

CAN is a differential two-wire bus (CAN_H, CAN_L) with no master. Every node can transmit any time the bus is idle. Frames carry an 11-bit (CAN 2.0A) or 29-bit (CAN 2.0B / extended) identifier instead of an address. Lower numerical ID = higher priority.

Dominant vs recessive. A "0" bit is dominant: someone actively pulls CAN_H high and CAN_L low (creating a 2 V differential). A "1" bit is recessive: bus floats (zero differential). If two nodes transmit simultaneously, dominant overrides recessive.

Arbitration. Each transmitter listens to the bus while it transmits. If it sends a recessive (1) but reads back a dominant (0), it has lost arbitration to a higher-priority frame and stops; the winner continues unaware. Lossless, deterministic, no collisions wasted.

plaintext
           Bit position:   ID10 ID9 ID8 ID7 ...
   Node A (ID 0x123):       0    0   1   ... loses here, stops
   Node B (ID 0x122):       0    0   0   ... wins, continues
   Bus result:              0    0   0   ... reflects winner

Speed: 1 Mbit/s up to 40 m, slower over longer wire. CAN-FD bumps to 5+ Mbit/s by raising the data-phase clock after arbitration.

Real-world. Engine, transmission, ABS, airbags, body control, instrument cluster all on CAN. The OBD-II port on every car is a CAN interface; that is how scan tools read codes. Hacker tools (CANalyze, Macchina M2, OpenXC) plug there to inject frames. The 2015 Jeep Cherokee remote attack used a cellular foothold to bridge to the CAN bus and steer the car.

3.5 USB basics

USB is the consumer everything-bus. From the embedded perspective:

  • Host (PC, hub) initiates transfers; device responds. A device cannot just push.
  • Speeds: Low (1.5 Mbps), Full (12), High (480), SuperSpeed (5 Gbps and up).
  • Devices expose endpoints, which are typed pipes:
    • Control (default endpoint 0): enumeration, configuration.
    • Bulk: large reliable transfers, no timing guarantee. Mass storage, USB-CDC.
    • Interrupt: small periodic transfers with bounded latency. HID (mouse/keyboard).
    • Isochronous: guaranteed bandwidth, no retransmit. Audio, video.

For the firmware writer, a typical decision is: do I expose USB-CDC (a virtual UART on the host) or HID (no driver needed) or DFU (for firmware update)? Most STM32 USB stacks (TinyUSB, ST USB middleware, libopencm3) handle the descriptor dance for you.

USB enumeration is a good exploit surface: a malicious USB device can imitate a HID keyboard and inject keystrokes (Rubber Ducky). USB-DFU is itself a programming protocol; if a device exposes DFU and the bootloader is unsigned, anyone with USB access can reflash it.

3.6 Ethernet basics

Ethernet shows up on bigger embedded systems. The MCU contains a MAC (Media Access Controller); the PHY (transceiver) is a separate chip (LAN8720, KSZ8081). They talk over MII or RMII (a 4- or 2-bit parallel bus) plus MDIO for control. From firmware, you usually run lwIP (lightweight IP) on top of the MAC for TCP/IP. Real-time variants (EtherCAT, PROFINET, TSN) layer determinism atop standard Ethernet.

3.7 Wireless

  • Wi-Fi. ESP8266 (single-core 80 MHz, 2.4 GHz only) and ESP32 (dual-core, BT also) dominate the hobbyist and low-end IoT world. They expose simple APIs (ESP-IDF) for TCP, MQTT, HTTPS. Industrial systems use Microchip ATWILC, NXP IW416, Murata modules.
  • BLE. Nordic nRF52 family is the de facto MCU. Cortex-M4F core plus 2.4 GHz radio. Low-power, advertise/connect, GATT services. Used in fitness trackers, smart locks, beacons, hearing aids.
  • Zigbee/Thread. Mesh networking on 802.15.4 radio (also 2.4 GHz). Hundreds of nodes, multi-hop. Smart-home staples: Hue lamps, Aqara sensors. Thread is IPv6-on-mesh, used by Apple HomeKit and Matter.
  • LoRa. Sub-GHz chirp-spread modulation, kilometers of range, kilobits-per-second data. Semtech SX127x family. LoRaWAN is the cloud protocol on top. Asset tracking, smart agriculture, smart meters.
  • Cellular. LTE-M and NB-IoT are the IoT-grade flavors of LTE. Quectel BG95, u-blox SARA. Years of battery life, global coverage, SIM management. Used in fleet tracking, smart parking, alarm systems.

Each radio is an attack surface in itself; section 9 returns to security.