Introduction
I2C (Inter-Integrated Circuit) is a popular communication protocol that lets microcontrollers talk to sensors, displays, and other peripherals using just two wires: SDA (data) and SCL (clock). But when you’re prototyping, you often don’t know the exact address of a new I2C device. That’s where an I2C scanner comes in handy. It probes every possible address on the bus and reports which ones respond.
In this tutorial, we’ll build an I2C scanner for the Raspberry Pi Pico using the Velxio in-browser simulator. You’ll learn how to wire up a simple circuit with two indicator LEDs, write the scanning code in Arduino C++, and interpret the results. No hardware needed—just your browser!

Circuit Walkthrough
Our circuit is minimal but instructive. We’ll use two LEDs to give visual feedback during scanning:
- Blue LED (led-scan): Connected to GP12 (D12 on the Pico) through a current-limiting resistor (internal to the LED component). Its anode (A) connects to GP12, and its cathode (C) goes to ground. This LED blinks each time the scanner sends a start condition on the I2C bus.
- Green LED (led-found): Connected to GP10 (D10). Its anode goes to GP10, cathode to ground. This LED lights up whenever a device is found during the scan.
Both LEDs share a common ground connection to the Pico’s GND.1 pin. The I2C bus itself uses the default pins: SDA = GP4 and SCL = GP5. No external pull-up resistors are needed because the Pico’s internal pull-ups are enabled by the Wire library.
To build this circuit in Velxio:
- Open the component picker from the canvas toolbar.

- Add a Raspberry Pi Pico board.
- Add two LEDs (blue and green) from the Output category.
- Wire them as described: GP12 → blue LED anode, GP10 → green LED anode, both cathodes → GND.
- Use the canvas toolbar to zoom and arrange components neatly.

Code Walkthrough
Now let’s dive into the code. Open the code editor in Velxio and paste the following sketch:
// Raspberry Pi Pico — I2C Scanner
// Scans I2C bus (Wire / I2C0: SDA=GP4, SCL=GP5) for devices
#include <Wire.h>
void setup() {
Serial.begin(115200);
delay(500);
Wire.begin(); // SDA=GP4, SCL=GP5 by default on Pico
Serial.println("=== Pico I2C Scanner ===");
Serial.println("Default I2C0: SDA=GP4, SCL=GP5");
Serial.println();
}
void loop() {
Serial.println("Scanning I2C bus...");
int found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0) {
found++;
Serial.print(" Device found at 0x");
if (addr < 16) Serial.print("0");
Serial.print(addr, HEX);
// Identify known addresses
switch (addr) {
case 0x48: Serial.print(" (Temperature sensor)"); break;
case 0x50: Serial.print(" (EEPROM)"); break;
case 0x68: Serial.print(" (DS1307 RTC)"); break;
case 0x27: Serial.print(" (LCD backpack)"); break;
case 0x3C: Serial.print(" (SSD1306 OLED)"); break;
default: break;
}
Serial.println();
}
}
Serial.print("Scan complete. Found ");
Serial.print(found);
Serial.println(" device(s).");
Serial.println();
delay(5000);
}
How It Works
Wire.begin()initializes the I2C peripheral on the default pins (GP4/GP5). On the Pico, this sets up I2C0.- The
forloop iterates through all possible 7-bit addresses from 1 to 127. Address 0 is reserved for general call, so we skip it. - For each address, we call
Wire.beginTransmission(addr)and thenWire.endTransmission(). The return value ofendTransmission()is 0 if the device acknowledged, or a non-zero error code otherwise. - If a device responds, we print its address in hexadecimal format. The code also includes a switch statement that labels some common addresses (e.g., 0x3C for SSD1306 OLED displays).
- The blue LED on GP12 toggles each time a scan cycle starts (you can add
digitalWrite(12, HIGH)anddigitalWrite(12, LOW)around the scan loop for visual feedback). The green LED on GP10 lights up when a device is found.
To compile and run, click the compile button in the editor toolbar.

Key Concepts
- I2C Protocol: A multi-master, multi-slave, packet-switched, single-ended, serial communication bus. Each device has a unique address.
- Wire Library: Arduino’s built-in library for I2C communication. On the Pico, it uses I2C0 by default.
- Pull-up Resistors: I2C lines need pull-up resistors to VCC. The Pico has internal pull-ups that can be enabled, but for reliable operation with many devices, external 4.7kΩ resistors are recommended.
- Addressing: 7-bit addresses range from 0x01 to 0x7F (127). Some addresses are reserved (e.g., 0x00 for general call).
Common Pitfalls & Debugging Tips
- No devices found? Check that your I2C devices are powered and connected correctly. In simulation, ensure you’ve added virtual I2C peripherals (like an OLED or temperature sensor) to the circuit.
- Wrong pins? The Pico’s default I2C0 pins are GP4 (SDA) and GP5 (SCL). If you’re using different pins, call
Wire.setSDA(pin)andWire.setSCL(pin)beforeWire.begin(). - Serial Monitor not showing output? Make sure the baud rate matches (115200 in our code). In Velxio, open the Serial Monitor from the toolbar.
- LEDs not blinking? Verify the wiring: anode to GPIO, cathode to GND. Also check that you’ve set the pins as outputs in
setup()withpinMode(12, OUTPUT)andpinMode(10, OUTPUT).
Suggested Extensions
- Add a real I2C device: Place an SSD1306 OLED or a BMP280 sensor on the canvas and see the scanner detect it.
- Scan multiple buses: The Pico has two I2C peripherals (I2C0 and I2C1). Modify the code to scan both.
- Display results on an LCD: Instead of Serial, show the found addresses on a 16x2 LCD.
- Auto-detect and configure: Once you know a device’s address, automatically initialize its library.
Try It Yourself!
Ready to scan the I2C bus? Open the live example in Velxio and start experimenting:
Launch the Pico I2C Scanner on Velxio
No downloads, no soldering—just pure simulation. Modify the code, add new components, and see the results instantly. Happy scanning!