Hardware Communications Protocols
Why this matters
Every embedded device is a small network of chips talking to each other, and almost none of that traffic is protected. The designers assumed the conversation was private because it happens on copper a few millimetres long, inside a plastic case. Attach a logic analyzer and it is not private any more.
This page is the map. It covers what the four buses you will meet actually are, how to tell them apart on an unfamiliar board, and which one to reach for when you want a particular kind of access. Each has its own page with the detail:
- UART — the bootloader console, and the first thing to look for
- I2C — sensors, EEPROMs, and anything that needs two wires
- SPI — flash, displays, radios, and anything that needs speed
- JTAG and SWD — the debug port, which is a different category of thing
The four buses at a glance
| UART | I2C | SPI | JTAG/SWD | |
|---|---|---|---|---|
| Wires | 2 (+GND) | 2 | 4 + 1 CS per device | 4–5 / 2 |
| Clock | None — both ends agree a rate | Shared, driven by controller | Shared, driven by controller | Shared |
| Addressing | None — point to point | 7-bit, in band | Out of band, via chip select | Out of band, via TAP |
| Typical speed | 9.6–115.2 kbaud | 100 kHz – 3.4 MHz | 1–100+ MHz | 1–10+ MHz |
| What you get | Whatever firmware prints | Sensor and config traffic | Firmware, display, radio data | The silicon itself |
| Usual target | Console, GPS, modems | EEPROM, RTC, sensors, codecs | NOR flash, displays | The CPU |
The last row is the one that matters for planning an assessment. These are not four flavours of the same thing — they give you four different levels of access, and they are worth attacking roughly in that order because the effort rises as you go right.
Telling them apart on a scope
Before you know what a bus is, you can often tell from its shape. Put a logic analyzer on the candidate pins, power-cycle the board, and look:
Two wires, one idling high, bursts of activity with no separate clock. UART. The idle-high line is TX; there is no clock to find because there is not one. See UART.
Two wires, one clearly a clock, activity in bursts of nine bits. I2C. The nine-bit grouping is the giveaway: eight data bits plus an acknowledge. Look for the START condition — SDA falling while SCL is high, which is illegal for a data bit and therefore unmistakable. See I2C.
Three or four wires, one a clock, one going low around each burst. SPI. The line that frames the burst is chip select. Data on the other two moves in both directions simultaneously. See SPI.
Four or five closely-grouped pins, often on an unpopulated header, silent until you drive them. JTAG or SWD. Unlike the others, a debug port does not usually talk unprompted — you have to initiate. That silence is why it gets missed. See JTAG and SWD.
Choosing what to attack
The buses differ in what they can give you, and the difference is not subtle.
UART gives you what the firmware chose to say. That is a limitation and also a gift, because firmware engineers are chatty. A boot log frequently includes the kernel version, the partition table, the hardware inventory, and sometimes a root shell. Cheap to attempt, high payoff, always try it first.
I2C and SPI give you what the firmware says to its own peripherals. It did not intend for you to read this. That is where configuration secrets, sensor calibration, and — critically — the entire firmware image live, because bulk storage sits on SPI.
JTAG gives you the silicon, independent of what the firmware wants. Halt the core mid-instruction, read any address, single-step, dump internal flash. When it is available and unlocked, everything else becomes unnecessary.
A rough decision order:
- Is there a UART? Read the boot log. It often tells you where everything else is.
- Is there an external flash chip? Its contents are the whole firmware. See Dumping flash.
- Is there a debug port, and is it locked? If unlocked, you are done.
- Is there interesting I2C traffic? Usually configuration rather than code, but EEPROMs hold secrets.
Voltage, and the mistake everybody makes once
Modern embedded I/O runs at 3.3 V or 1.8 V. Older and simpler devices run at 5 V. Nothing on the board tells you which, and the pins look identical.
Connecting a 5 V adapter to a 1.8 V pin will, at best, do nothing, and at worst destroy the pin, the pad, or the part. There is no undo.
⚠️ Measure before you connect. Find ground with a multimeter in continuity mode, referenced against a shield can or the negative terminal of a large electrolytic capacitor. Then measure the idle voltage on the line you intend to probe. Thirty seconds of measurement against a destroyed board is not a close decision.
Tools with a voltage-select jumper — the Tigard, most Bus Pirate models — have one specifically because this mistake is so common that the hardware is designed around it.
Worked example: identifying an unknown bus from a capture
You have a four-channel capture from an unlabelled header and no idea what it is. Work it structurally rather than guessing.
CH0 ‾‾‾‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾‾‾‾ regular, symmetric
CH1 ‾‾‾\__/‾‾‾‾‾\___/‾‾‾‾‾‾‾‾\_/‾‾‾‾‾‾‾‾‾‾‾ irregular, data-like
CH2 ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ idle high, never moves
CH3 ‾‾‾‾\_______________________________/‾‾ one long low, framing the rest
Reason it through:
- CH0 is a clock. Regular, symmetric, and it only runs while something else is happening. That rules out UART immediately.
- CH3 frames the whole burst. One transition down at the start, one up at the end. That is a chip select, which means SPI rather than I2C — I2C has no such line.
- CH1 is data. With CS on CH3 and clock on CH0, CH1 and CH2 are MOSI and MISO.
- CH2 never moves. Either nothing replied, or you have MISO on a device that was only written to.
Count the clock edges between the CS transitions. If it is a multiple of eight, you have whole bytes and the decode will line up. If it is 9, 18, 27 — go back and reconsider I2C, because that nine-bit grouping is the acknowledge.
Now set the decoder to SPI, assign the channels, and try mode 0 first; if the output is garbage but the framing looks right, work through the four SPI modes.
Passive first, always
There is an enormous difference between listening to a bus and driving it, and the difference is measured in destroyed hardware.
A bus already has a controller. If you attach a second one and both drive the same line in opposite directions, you have connected the supply rails together through two output transistors. The mild outcome is a corrupted transaction; the severe one is a dead driver on a chip you cannot replace.
⚠️ Sniff before you interact. Capture the bus with a logic analyzer, which is high-impedance and cannot drive anything, until you understand what is on it. Only then consider becoming a controller — and when you do, hold the original controller in reset first.
This is why the Tools of the Trade page distinguishes analyzers from interfaces. A Saleae or Bitscope can only listen. A Bus Pirate can talk, which makes it more useful and more dangerous.
Key takeaways
- The four buses give four different levels of access; attack them roughly in order of effort, which is also order of payoff per unit work.
- You can classify a bus from its waveform before decoding anything: no clock means UART, nine-bit groups mean I2C, a framing line means SPI, silence until driven means a debug port.
- SPI is where firmware lives, which makes it the highest-value bus on most boards.
- Measure the voltage domain before connecting. This is the single most common way students destroy hardware in this course.
- Listening is safe; driving is not. Capture first.
References
- NXP UM10204, I2C-bus specification and user manual (Rev. 7.0, 2021) — https://www.pololu.com/file/0J435/UM10204.pdf
- JEDEC JESD216F, Serial Flash Discoverable Parameters — https://www.jedec.org/standards-documents/docs/jesd216b
- IEEE 1149.1, Standard Test Access Port and Boundary-Scan Architecture — https://standards.ieee.org/ieee/1149.1/4484/
- ARM Debug Interface Architecture Specification (ADIv5) — https://developer.arm.com/documentation/ihi0031/latest/
- sigrok, supported protocol decoders — https://sigrok.org/wiki/Protocol_decoders
- Tigard multi-protocol interface — https://github.com/tigard-tools/tigard
- OWASP IoT Security Testing Guide — https://owasp.org/www-project-iot-security-testing-guide/
Related course pages: Schedule · Tools of the Trade · UART · I2C · SPI · JTAG and SWD · Dumping flash
🛠️ Maintenance note: the bus standards on this page are stable — I2C and SPI predate most of the students — so the content that ages is the tooling. Re-check the logic-analyzer software names and the Tigard/Bus Pirate model lineup each offering, and note that UM10204 Rev 7.0 replaced the master/slave terminology with controller/target, which older tutorials and decoder UIs still use.