Protocol Stacks - What Actually Runs on Top of the Physical Bus
Buses carry bits. What gives those bits meaning is the protocol layer above them. Modbus on RS-485, CANopen on CAN, SMBus on I2C. Here's how layers compose in real embedded systems.
Protocol Stacks. What Actually Runs on Top of the Physical Bus
Everything we've covered so far. UART, SPI, I2C, CAN, RS-485. is essentially layer 1 and 2 work. It gets bits from one chip to another, reliably. But bits are just bits. What do they mean? That's the job of the protocol layer above.
This is where most of the real-world complexity lives. A Modbus RTU frame means nothing without the register map. A CAN frame means nothing without knowing the ID-to-signal mapping. The physical bus is just the transport; the protocol on top is the contract.
The layer model for embedded
The OSI model is designed for networking, but the concept translates:
Rendering diagram...
When something breaks, knowing which layer the problem is at cuts debugging time dramatically.
RS-485 + Modbus RTU
The most common industrial stack. Modbus RTU sits directly on top of RS-485, with no additional framing needed. the Modbus frame is transmitted as raw bytes at the agreed baud rate.
Application: "Get flow rate from meter at address 4"
Modbus: [04][03][00 1E][00 01][CRC16]
RS-485: differential voltage on twisted pair
The application only cares about register numbers and engineering values. Modbus handles the framing. RS-485 handles the electrical delivery.
CAN + CANopen
CANopen is a higher-layer protocol built on CAN, widely used in industrial automation, medical devices, and motion control. It defines a standard object dictionary: a table of parameters every device must expose, with standardised indices. Real-time data is exchanged via PDOs (Process Data Objects); configuration and non-time-critical data uses SDOs (Service Data Objects).
Rendering diagram...
CANopen is heavier than raw Modbus, but it standardises things Modbus never tried to: drive profiles, motion sequences, device identification. If you're building a machine with servo drives, CANopen is probably already on the table.
I2C + SMBus
SMBus (System Management Bus) is a subset of I2C originally designed for laptop batteries and ACPI power management. It adds:
- Packet Error Checking (PEC): an extra CRC byte after each transaction
- A fixed minimum timing specification (no indefinite clock stretching)
- Standard commands: ReadByte, WriteWord, BlockRead, etc.
If you're using a battery gas gauge IC (like BQ27546 or MAX17055), it almost certainly speaks SMBus over its I2C pins. The difference in practice: enable PEC in your I2C driver and you're done.
SPI + SD card protocol + FAT32
SD cards export a fairly complex protocol in SPI mode. initialization sequences, read/write commands with R1/R2/R3 response tokens, data blocks with CRC16. On top of that runs FAT32 or exFAT, giving you a file system.
Rendering diagram...
Each layer has no idea what's in the one above it. FAT32 doesn't know it's on SPI. The SD driver doesn't know someone is writing log files. This separation is what lets you swap out any layer independently. replace the SD card with QSPI flash, keep FAT32 on top unchanged.
A real stack: battery management system
Cell voltage ADC ββSPIβββ battery AFE chip (e.g. BQ76940)
BQ76940 ββI2Cβββ microcontroller
microcontroller ββCAN (CANopen)βββ vehicle BMS master
BMS master ββSAE J1939βββ dashboard / instrument cluster
Four different buses, four different protocols, all composing predictably. The dashboard code speaks J1939 and doesn't know or care that there's an I2C sensor somewhere in the stack.
Debugging tip: go bottom-up
The single most common debugging mistake is starting at the application layer when the problem is physical. If your Modbus read returns garbage, check the RS-485 differential voltage with a multimeter before you start examining register maps. If I2C hangs, put a scope on SDA and SCL before reading the sensor library source code.
Physical layer problems masquerade as protocol problems constantly. Terminate your bus, fix your pull-ups, get a clean signal. then debug the protocol.