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:
| Address | Device |
|---|---|
0x68 | ICM-42670 motion sensor (IMU) |
0x76 or 0x77 | SPL07-003 barometer |
0x7C | QMC6309 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:
- 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".
- Address. The controller clocks out the 7-bit address, biggest bit first.
- Read or write. One more bit: 0 means the controller is about to send, 1 means it wants the device to send.
- 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.
- Data. Eight bits at a time, from whoever is sending, each byte followed by an ACK from whoever is receiving.
- 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.
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
- 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 kHzWhat 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.
Clean edges: rising in 247 ns, well inside the 300 ns allowed at 400 kHz.
Pull-up resistor
Clock speed
Each one adds a little capacitance, roughly 10 pF
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.
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.
