courses

I2C

Why this matters

I2C is the bus that tells you what a board is made of. Every transaction carries the target’s address in band, so a single capture enumerates the devices on the bus without you tracing a single trace. That is a property SPI does not have, and it makes I2C the friendliest bus on the board to an attacker.

What rides on it is usually configuration rather than code — but “usually” is doing work in that sentence. EEPROMs on I2C hold calibration data, serial numbers, provisioning keys, and occasionally the credentials the device uses to talk to its cloud.

A terminology note. NXP’s UM10204 Rev 7.0 (2021) replaced master/slave with controller/target. This page uses the current terms. Most tools, decoders, and tutorials you will meet still use the old ones, so you need to read both.

The physical layer

Two wires, and the interesting part is how they are driven:

Line Meaning
SCL Serial clock, driven by the controller
SDA Serial data, bidirectional

Both are open drain. A device can pull a line low but never drive it high; pull-up resistors return the line to idle. Three consequences follow, and all three matter:

  1. Any device can hold a line low at any time. This is a feature — it is how acknowledge and clock stretching work — and a failure mode, because one stuck device wedges the whole bus.
  2. Wired-AND arbitration. Two controllers can start simultaneously; the one that transmits a 1 while seeing a 0 on the wire loses and backs off, with no data corrupted.
  3. Rise time is limited by the pull-ups. Weak pull-ups on a long bus give slow rising edges, which is the usual reason an I2C bus that “should work” does not.

Speed modes

Mode Maximum
Standard 100 kbit/s
Fast 400 kbit/s
Fast-mode Plus 1 Mbit/s
High-speed 3.4 Mbit/s

Nearly everything you will meet in this course is 100 or 400 kHz — slow enough that any logic analyzer can capture it comfortably.

The protocol

START and STOP

The framing conditions are deliberately illegal as data, which makes them unmistakable in a capture:

START:  SDA falls while SCL is HIGH
STOP:   SDA rises while SCL is HIGH

During normal data transfer SDA is only allowed to change while SCL is low. So any SDA transition during a high clock is framing, not data. When you are hunting for the start of a transaction in a noisy capture, that is what you look for.

Addressing and the acknowledge

After START, the controller sends one byte: seven address bits then a direction bit.

 bit:   7   6   5   4   3   2   1   0
      [-- 7-bit address --------] [R/W]

R/W is 0 for write, 1 for read. So the byte on the wire is address << 1 | rw — which is why a device at address 0x19 appears as 0x32 on the wire for a write and 0x33 for a read.

This is the single most common decoding error. A student who reports the address as 0x32 has read the wire correctly and the datasheet incorrectly. Many datasheets quote the 8-bit form, which compounds the confusion; when a datasheet says “the device address is 0x94”, it means 7-bit 0x4A.

Note the trap in that example: 0x32 and 0x33 are the same device, once being written to and once being read from. A scan that reports “devices at 0x32 and 0x33” has found one device and counted it twice.

Then the ninth bit: the transmitter releases SDA and the receiver pulls it low to acknowledge.

Ninth bit Meaning
SDA low (ACK) A device at that address exists and is responding
SDA high (NAK) Nothing there, or the device is refusing

That nine-bit grouping is the signature of I2C in a capture. If you count groups of nine rather than eight, you are looking at I2C.

Repeated START

To read a register you must first write the register address, then read — but releasing the bus between the two lets another controller interleave. A repeated START (Sr) issues a new START without an intervening STOP, holding the bus across both phases:

S  addr+W  ACK  reg  ACK  Sr  addr+R  ACK  data  NAK  P

That pattern — write one byte, repeated start, read — is the most common transaction shape on the bus, and recognising it saves you a lot of time.

Note the final NAK from the controller: when reading, the controller NAKs the last byte to tell the target to stop transmitting. A NAK there is normal and correct, not an error.

Clock stretching

A target that needs time can hold SCL low after the acknowledge. The controller must wait for the line to release before continuing. Not all controllers implement this correctly, and it is a classic source of intermittent bus failures — and of captures where the clock period is suddenly irregular.

Reserved addresses

The bottom eight (0x00–0x07) and top eight (0x78–0x7F) addresses are reserved by the specification. 0x00 is the general call address. A bus scan should skip them, and a scanner that reports a device at 0x00 is confused.

That leaves 112 usable addresses, which is why address collisions between peripherals are a real design problem — and why some devices have address-select pins or jumpers.

Sniffing a bus

Attach a logic analyzer to SDA, SCL, and ground. Nothing else. An analyzer is high impedance and cannot disturb the bus, which is exactly what you want before you understand what is on it.

Capture from power-on: the initialisation burst enumerates every device the firmware expects to find, which is more informative than anything that happens later.

Worked example: decoding a capture by hand

A decoded burst from a board’s power-on sequence, on a board carrying an ST LSM303DLHC:

 1  S  0x32  ACK  0x20  ACK  0x27  ACK  P
 2  S  0x3C  ACK  0x00  ACK  0x10  ACK  P
 3  S  0x30  NAK  P
 4  S  0x3C  ACK  0x0A  ACK  Sr  0x3D  ACK  0x48  NAK  P

Reading it:

The NAK on the last byte of line 4 is not an error. On a read, the controller drives the acknowledge bit, and it NAKs the final byte to tell the target to stop sending. A NAK after an address byte means “nobody there”; a NAK after the last data byte of a read means “done”. Confusing the two is the second most common decoding mistake after the address shift.

From four transactions you have: one confirmed device with its identity, one absent device the firmware expects, and two configuration values you can look up. No board tracing required.

Interacting with a bus

Reading is safe. Writing is not, and there is a real risk of bricking: configuration EEPROMs on I2C frequently hold calibration constants that the device cannot regenerate.

To become a controller you need the original one out of the way — hold the SoC in reset — and a tool that can drive the bus. The Bus Pirate and Tigard both do this.

An address scan walks every valid address and records which ACK:

$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- 18 -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
...
40: -- -- -- -- -- -- -- -- -- -- 4a -- -- -- -- --

i2cdetect is from i2c-tools and works when you have Linux on the device itself — which you may, after getting a shell over UART. From outside, the Bus Pirate’s (1) macro in I2C mode does the same scan.

⚠️ A scan is not entirely passive. It writes an address byte to every address on the bus, and a device that interprets a bare write as a command may act on it. Rare, but it happens with some power-management ICs. Sniff first; scan only when you need to.

Where the secrets are

Devices on I2C worth attention in an assessment:

Device type Typical addresses Why you care
EEPROM (24Cxx) 0x50–0x57 Serial numbers, calibration, provisioning data, sometimes keys
RTC (DS1307, PCF8523) 0x68 Battery-backed RAM often used as scratch storage
Sensors Various Rarely security-relevant, useful for fingerprinting
Audio codecs 0x1A, 0x4A Not sensitive; useful as a known-good calibration target
MEMS sensors 0x19, 0x1E, 0x68, 0x6B Not sensitive either, but they ACK reliably — the easiest way to prove your wiring and decode settings are right before chasing something that matters
Power management 0x08–0x0F Can be dangerous to write to

A 24C-series EEPROM at 0x50 is the one to read first. They are small, easy to dump, and vendors put things in them that they would not put in flash because “nobody looks at the EEPROM”.

Key takeaways

References


Related course pages: Protocols · Schedule · UART · SPI · Tools of the Trade · Dumping flash

🛠️ Maintenance note: UM10204 Rev 7.0 (2021) is current and the controller/target terminology it introduced has not yet reached most tools — expect decoder UIs and student-facing tutorials to say master/slave for years yet, and teach both. Device addresses in the table are conventions, not guarantees; always confirm against the datasheet for the part in front of you.