I2C and the Qwiic connector

Beginner15 min readUpdated September 2026

How a single pair of wires can carry a motion sensor, a barometer, a compass and whatever you plug in next, and how to find and fix the problems when two of them disagree.

The Vector S3 has a motion sensor, a barometer and a compass on board, plus a Qwiic port for plugging in more. Every one of them talks to the ESP32-S3 through the same two pins: GPIO13 and GPIO14.

That's not a shortcut someone took. It's I2C (say "I-squared-C" or "I-two-C"), a way for many chips to share two wires. Almost every sensor, display and small add-on board you'll buy speaks it, so understanding it pays off quickly. This article covers how the conversation works, why the wires need pull-up resistors, what limits speed and cable length, and what to do when two devices want the same address.

It builds on the pull-up resistors from Buttons, pull-ups and bounce, so it's worth reading that first.

Two wires, a whole street of devices

I2C uses just two signal wires, plus power and ground:

  • SDA, the data line, carries the actual bits.
  • SCL, the clock line, ticks once for every bit, so both sides agree on exactly when to read SDA.

One chip runs the show: the controller, which on the Vector is the ESP32-S3. It starts every conversation and drives the clock. Everything else is a target, a device that waits to be spoken to. (Older documents call these master and slave.)

Think of the bus as a street and each target as a house with a number on the door. The controller stands in the street and calls out a number; only the house with that number answers. On I2C those numbers are addresses, 7 bits long, so they run from 0 to 127. A handful are reserved, which leaves room for about 112 devices on one bus.

Addresses are usually written in hexadecimal, with a 0x in front. Here are the ones already taken on the Vector S3:

AddressDevice
0x68ICM-42670 motion sensor (IMU)
0x76 or 0x77SPL07-003 barometer
0x7CQMC6309 compass

0x68 is just the number 104 written in base 16. In binary it's 1101000, and those seven bits are exactly what travels down the wire when the controller calls that address.

A conversation, one bit at a time

Every I2C exchange follows the same pattern:

  1. START. The controller pulls SDA low while SCL is high. Normally data may only change while the clock is low, so this one exception means "everyone, listen".
  2. Address. The controller clocks out the 7-bit address, biggest bit first.
  3. Read or write. One more bit: 0 means the controller is about to send, 1 means it wants the device to send.
  4. ACK. The device with that address pulls SDA low for one clock tick: "that's me". If nobody does, the line stays high. That's a NACK, and the controller gives up.
  5. Data. Eight bits at a time, from whoever is sending, each byte followed by an ACK from whoever is receiving.
  6. STOP. SDA rises while SCL is high, the mirror image of START. The bus is free again.

The Vector S3 talking to one of its sensors, slowed down from as many as 400,000 clock ticks a second to about two. Pick who to talk to, press play, and watch the conversation build one clock tick at a time.

SCLclockSDAdatacontroller drives SDAdevice drives SDAnobody: pull-up holds it high

Ready

Both lines sit high while the bus is idle, held up by pull-up resistors. Press play or step.

Talk to

The controller wants to

0x2A = 00101010
Address
0x68
1101000
Direction
Write
R/W bit = 0
Answer
—
someone's home
Clock ticks
0
of 18

Try the address nobody answers. The controller calls out 0x3C, waits for someone to pull SDA low, and hears nothing. That silence is how your code finds out a device is missing, and it's how the scanner at the end of this article works.

Why the lines need pull-ups

Here's the clever part. No chip on an I2C bus ever drives a line high. Each one can only do two things: pull the line down to ground, or let go of it. This is called open-drain.

In water terms, every device on the bus has a valve to the sea, and that's all. Nobody has a pump. So what makes the line high when everyone lets go? The same thing as in Buttons, pull-ups and bounce: a pull-up resistor, a thin pipe from the 3.3 V reservoir. When every valve is shut, it quietly fills the line back up to 3.3 V. When any device opens its valve, the wide pipe to the sea wins and the line drops to 0 V.

That design is what makes sharing possible:

  • Nothing can fight. If one chip pushed a line high while another pulled it low, you'd have a short circuit between them. With open-drain, the worst that happens is two chips both pulling low, which is harmless.
  • Anyone can speak. The controller lets go of SDA during the ACK bit, and the device pulls it low. Neither has to know who else is connected.

The Vector S3 already has 4.7 kΩ pull-ups on SDA and SCL, so you don't need to add any. Many Qwiic boards carry their own pull-ups too. Those end up in parallel with the Vector's, making the combined pull-up stronger. With one or two boards that's fine. With a long chain, check each board's docs; most have a jumper you can cut to disconnect their pull-ups.

Speed, cables and capacitance

I2C usually runs at 100 kHz (standard mode) or 400 kHz (fast mode), meaning 100,000 or 400,000 clock ticks a second. The ESP32-S3 can do both. You choose with one line:

Wire.setClock(400000);  // fast mode; the default is usually 100 kHz

What limits the speed is the pull-up itself. Every wire and every chip on the bus has a little capacitance, the stretchy membrane from Electricity, explained with water. When a device lets go of the line, the pull-up has to fill that membrane through a thin pipe before the voltage reaches "high". Pulling low is fast, because the device's valve is wide. Rising is slow.

More devices and longer cables mean a bigger membrane and a slower rise. Too slow, and the line hasn't reached HIGH by the time the clock wants to read it.

Nobody drives an I2C line high. The pull-up resistor has to fill the wire's capacitance every time it's released, like water slowly stretching a membrane. Add devices and cable, change the pull-up, and watch what happens to the clock's edges.

3.3 V2.3 V1.0 V0 Vreads HIGHundefinedreads LOWtwo clock periods at 400 kHz (2.5 µs each) · dashed: a perfect square wave

Clean edges: rising in 247 ns, well inside the 300 ns allowed at 400 kHz.

Pull-up resistor

Clock speed

2

Each one adds a little capacitance, roughly 10 pF

0.2 m

Roughly 60 pF per metre; it varies by cable

Bus capacitance
62 pF
400 pF max
Rise time
247 ns
300 ns allowed
Pulling low
617 µA
3 mA max per device
τ = R × C
291 ns
pull-up × capacitance

The practical rules:

  • Keep cables short. I2C was designed for chips on the same board. A few tens of centimetres of Qwiic cable is fine. Past a metre or so, expect trouble.
  • Slow down before you speed up. If a long chain misbehaves, drop back to 100 kHz first.
  • Stronger pull-ups help, up to a point. A smaller resistor fills the line faster, but every device then has to sink more current to pull it low. Below about 1 kΩ at 3.3 V, you're past what the standard allows.
  • For long distances, use the right tool. Special I2C bus extender chips exist for runs of several metres.

Sharing the bus: address clashes

Addresses are usually fixed by the chip's manufacturer. That's fine until you plug in two of the same sensor, or two different chips that happen to share an address. Both answer at once, their bits collide on SDA, and you get garbage from both.

Every device hangs off the same two wires. Plug add-ons into the Qwiic port, run a scan, and see who answers. Then plug in something that already has a twin on the bus.

Vector S3controlleridleSDA · GPIO14SCL · GPIO13onboardMotionsensor0x68onboardBarometer0x76onboardCompass0x7CQwiicOLEDdisplay0x3C

Qwiic add-ons (tap to plug in or unplug)

OLED display: address jumper

Every address on the bus is unique, so each question has exactly one answerer.

Serial Monitor

Press "Scan the bus" to ping every address from 0x01 to 0x7E.
Wires used
2
SDA + SCL, shared
Devices
4
3 onboard, 1 plugged in
Clashes
0
all unique
Scan
—
0x01 to 0x7E

There are a few ways out:

  • An address jumper. Many boards have a solder jumper or a pin labelled ADDR or AD0 that moves the chip to a second address. It's the quickest fix, if the board has one.
  • A different chip. Sometimes the easiest answer is a sensor from another family with a different address.
  • An I2C multiplexer. A switch chip that sits on the bus and connects the controller to one of several branches at a time. Each branch can hold its own copy of the same address.
  • A second bus. The ESP32-S3 has two I2C controllers. In Arduino, the second one is Wire1, and you can put it on almost any free pins.

Qwiic: I2C without soldering

Qwiic (from SparkFun) and STEMMA QT (from Adafruit) are the same idea: a small 4-pin JST-SH connector carrying ground, 3.3 V, SDA and SCL. The plug only fits one way round, so you can't swap the wires. Most boards have two connectors, so you can daisy-chain one to the next.

On the Vector S3, the Qwiic port is the same bus as the SDA and SCL header pins and the onboard sensors. Anything you plug in shares the bus, so it needs an address that isn't already taken.

Qwiic runs at 3.3 V. Most Qwiic and STEMMA QT boards are designed for it, but if you wire in a board meant for 5 V, check its datasheet first.

Try it: an I2C scanner

The fastest way to see what's on a bus is to ask every address in turn and note who answers. That's exactly what the scan in the widget above does:

#include <Wire.h>
 
void setup() {
  Serial.begin(115200);
  Wire.begin(14, 13);  // Vector S3: SDA = GPIO14, SCL = GPIO13
  delay(500);
 
  Serial.println("Scanning...");
  int found = 0;
  for (uint8_t address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    // endTransmission() returns 0 when a device ACKed its address.
    if (Wire.endTransmission() == 0) {
      Serial.printf("Found device at 0x%02X\n", address);
      found++;
    }
  }
  Serial.printf("%d device(s) found\n", found);
}
 
void loop() {}

On a bare Vector S3, you should see the three onboard sensors: 0x68, 0x76 or 0x77, and 0x7C. Plug in a Qwiic board, press reset, and a new line appears. If it doesn't, the troubleshooting list below will usually tell you why.

Reading from a device

Most sensors organise their data into numbered registers, like pigeonholes. To read one, you write the register number, then ask for the data. In Arduino that looks like this:

uint8_t readRegister(uint8_t device, uint8_t reg) {
  Wire.beginTransmission(device);
  Wire.write(reg);                  // which register
  Wire.endTransmission(false);      // false: keep the bus, no STOP yet
  Wire.requestFrom(device, (uint8_t)1);
  return Wire.available() ? Wire.read() : 0;
}

The register numbers and what they contain come from each sensor's datasheet. In practice, a library for the sensor does all of this for you, but it's exactly these conversations underneath.

When it doesn't work

  • The scanner finds nothing. Check that Wire.begin() has the right pins, that the device is powered, and that SDA and SCL aren't swapped.
  • A device shows up at an unexpected address. Its address jumper is set differently from what your code expects. Use the address the scanner reports.
  • Everything is found, but readings are garbage. Look for an address clash, a long cable, or too many pull-ups in parallel. Try 100 kHz.
  • The bus freezes and nothing answers. A device can get stuck halfway through a byte, holding SDA low. Power-cycling the board clears it.

Once you've seen a conversation bit by bit, most I2C problems stop being mysterious. It's two wires, some pull-ups, and devices taking turns.