Source: https://mokxi.com/learn/i2c-explained
Updated: 2026-09-27

Learn

# I2C: two wires, many devices, and a scanner to find them

Written by the Mokxi team, updated September 27, 2026

Sign up free or see every lesson

- 192 parts on the bench
- 25 boards running now
- 1.00x real time, on every board

Click to open it in the editor

An LCD backpack, a DS3231 and an MPU6050 on one bus, found by address, with the analyzer decoding it.

I2C lets one microcontroller talk to many chips over the same two wires: SDA carries the data and SCL carries the clock. Every chip on the bus has a seven-bit address, and every conversation starts with the controller calling one of them by name. That is the whole reason an LCD, a clock and a motion sensor can share two pins on an Uno.

The circuit above puts exactly those three on one bus: a 16x2 LCD with a PCF8574 backpack, a DS3231 real-time clock and an MPU6050 motion sensor, all joined to the Uno’s A4 (SDA) and A5 (SCL) through two strips of the breadboard. The sketch is an I2C scanner, the first program worth running on any new I2C build. It asks every address from 1 to 126 whether anyone is there and prints the ones that answer:

On this circuit it prints "idle bus: SDA HIGH, SCL HIGH", then "found 0x27", "found 0x68" and "found 0x69", and finally "3 device(s), 126 addresses in 61 ms". A logic analyzer sits on the same two strips with its decoder set to I2C, so you can watch each address go out and see which ones were acknowledged.

## Addresses and the acknowledge

A transfer begins with a start condition: SDA falls while SCL is high, something that never happens in the middle of a byte. Then the controller clocks out eight bits: the seven-bit address and one bit saying read or write. On the ninth clock it lets go of SDA and listens. The device with that address pulls SDA low, the acknowledge, and every other device stays quiet. A stop condition, SDA rising while SCL is high, ends it.

A scanner is that and nothing more: for each address, Wire.beginTransmission(address) and Wire.endTransmission() with no data in between. endTransmission returns 0 if the address was acknowledged and 2 if nobody answered. On the analyzer each missing address shows as a start, the address with W, and a stop with no acknowledge.

Datasheets disagree about how to write addresses, which causes real confusion. The DS3231 is 0x68 as a seven-bit address, but the first byte on the wire is 0x68 shifted left with the write bit added, 0xD0. Arduino’s Wire library wants the seven-bit form. If a datasheet lists 0xD0 and 0xD1, divide by two.

firmware/uno/examples/i2cscan (the scan loop, condensed from the program running above)

## Two devices, one address

The MPU6050 answers at 0x68 by default, which is also the DS3231’s fixed address. The module has an AD0 pin to move it: tie AD0 to VCC and it answers at 0x69 instead, which is what the red wire from AD0 to the 5 V rail does here.

Open the circuit, delete that wire, and run it again. The scan now finds only 0x27 and 0x68. Both chips acknowledge 0x68 at the same moment, and an acknowledge is just a line pulled low, so two of them look exactly like one. A scanner cannot show you an address clash. On a real bench the symptom is readings that are nonsense or change when you unplug a module that seems unrelated, and the cure is always to check every module’s address before wiring them together.

## Pull-ups: why the bus needs resistors

No device on an I2C bus ever drives a line high. Everyone, the controller included, can only pull a line low or let go of it. Resistors from each line to the supply, the pull-ups, bring it back up. That open-drain arrangement is what makes the acknowledge work: any device can pull SDA low without fighting another that is driving it high.

The scanner checks for them before it scans. It lets go of both lines and reads them: with pull-ups there, both read HIGH. Delete the three modules and run it again and it prints "idle bus: SDA LOW, SCL LOW", a warning, and then claims to have found all 126 addresses, because a line nobody pulls up reads low, and low on the ninth clock is an acknowledge. In Mokxi a line with nothing driving it reads low. On a real board a floating line can read either way, and the stock Arduino Wire library turns on the Uno’s weak internal pull-ups, so a bus with no modules more often finds nothing or hangs. Either way, a scan that finds every address or none is telling you about the wires, not the chips.

Most breakout boards, like the three here, carry their own pull-ups, often 4.7 kilohms. Put several on one bus and their pull-ups end up in parallel: three 4.7 k resistors make about 1.6 k, and pulling that low from 5 V takes about 3.2 mA, slightly over the 3 mA a standard-mode I2C device is specified to sink. Two or three modules is fine in practice. With many more, remove the pull-up resistors from all but one of them.

## Reading a register, and the speed of this bus

Most I2C chips are a small bank of registers. To read one you write the register number, then issue a repeated start (a start with no stop before it) and read. The Desk clock example runs that transfer every second against the DS3231, and its analyzer shows "S 0x68 W", the register pointer, "Sr 0x68 R" and seven bytes of time coming back. Open it with the button below to watch a real read.

Be aware of one difference from real hardware. The ATmega328P model in Mokxi does not include the chip’s TWI peripheral, so Wire here is a software I2C on the same two pins. A real Uno’s hardware runs at 100 kHz; the software version on the Uno works out nearer 25 kHz, which is why 126 addresses take about 60 ms. Every device in the parts bin reads the bus from its edges and does not care about the speed, and the code you write is the same code you would upload.

## Questions

What value pull-up resistor should I use for I2C?

Between about 2.2 k and 10 k. 4.7 k is the usual choice at 100 kHz on short wires. Lower values give sharper edges on long or busy buses at the cost of more current; higher values are fine for one or two devices close together.

Which pins are I2C on an Arduino Uno?

A4 is SDA and A5 is SCL. Newer Uno boards also bring the same two signals out to pins marked SDA and SCL near AREF; they are the same connections, not a second bus.

Can 3.3 V and 5 V devices share an I2C bus?

Only with care. The bus sits at whatever voltage its pull-ups go to, so a 3.3 V device on a bus pulled up to 5 V sees 5 V on its pins. Use a bidirectional level shifter between the two halves, or pull the bus up to 3.3 V if every 5 V device will still read that as high.

My scanner finds nothing. What should I check?

Power and ground to each module, SDA and SCL swapped, the wires on the right pins for your board, and whether anything on the bus has pull-ups. Then check the module’s address against its datasheet in seven-bit form.

Related

## Keep going

UART Serial Explained: Baud, Frames and the Wire SPI Explained: Clock, Data and Chip Select Pull-Up Resistors and the Floating Input They Fix Arduino DS3231 Clock With a TM1637 Display Arduino I2C LCD Not Working: Blank, Blocks, No Text The I2C modules in the parts bin The DS3231 real-time clock The MPU6050 motion sensor The Arduino Uno simulator

## Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.

Start building Open the editor
