Go back

I2C Scanner with Arduino: Find Every Device on the Bus

Introduction

Have you ever connected a new I2C sensor or display to your Arduino and wondered, “What address does this thing use?” You’re not alone! The I2C protocol is a powerful way to communicate with multiple devices using just two wires, but every device has a unique address—and sometimes that address isn’t obvious from the datasheet. That’s where an I2C scanner comes in. It’s a simple sketch that probes every possible address and reports back which ones respond.

In this tutorial, we’ll build an I2C scanner using an Arduino Uno and an SSD1306 OLED display (which also serves as a known device on the bus). We’ll run everything inside the Velxio in-browser simulator, so you don’t need any hardware to follow along. By the end, you’ll understand how the I2C bus works, how to wire up devices, and how to write code that scans for them.

Let’s dive in!

Circuit Walkthrough

Circuit diagram of i2c-scanner on the Velxio simulator canvas

The circuit is refreshingly simple. We only need two components:

Wiring Details

Connect the OLED to the Arduino using four wires:

Arduino PinOLED PinWire Color
A4 (SDA)DATABlue
A5 (SCL)CLKOrange
GNDGNDBlack
5VVINRed

Note: On the Arduino Uno, A4 and A5 are the dedicated I2C pins. Other boards may use different pins (e.g., SDA/SCL on an ESP32).

In the Velxio simulator, you can add the OLED from the component picker and wire it up by clicking on the pins. The canvas toolbar helps you zoom and pan to make connections easier.

Velxio canvas toolbar above the simulation viewport

Code Walkthrough

Source code for i2c-scanner in the Velxio Monaco editor

Now let’s look at the code. Open the code editor in Velxio (you’ll see the Monaco editor with syntax highlighting). The sketch is written in Arduino C++ and uses the built-in Wire library.

Including the Library

#include <Wire.h>

The Wire library handles all the low-level I2C communication. It’s included with the Arduino IDE, so no extra installation is needed.

Setup Function

void setup() {
  Wire.begin();
  Serial.begin(9600);

  Serial.println("===========================");
  Serial.println("  I2C Bus Scanner (TWI)");
  Serial.println("===========================");
  Serial.println("Scanning...");
  Serial.println();

  int devicesFound = 0;

  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("  Device found at 0x");
      if (addr < 16) Serial.print("0");
      Serial.print(addr, HEX);

      // Identify known addresses
      switch (addr) {
        case 0x27: Serial.print("  (PCF8574 LCD backpack)"); break;
        case 0x3C: Serial.print("  (SSD1306 OLED)"); break;
        case 0x48: Serial.print("  (Temperature sensor)"); break;
        case 0x50: Serial.print("  (EEPROM)"); break;
        case 0x68: Serial.print("  (DS1307 RTC)"); break;
        case 0x76: Serial.print("  (BME280 sensor)"); break;
        case 0x77: Serial.print("  (BMP180/BMP280)"); break;
      }
      Serial.println();
      devicesFound++;
    }
  }

  Serial.println();
  Serial.print("Scan complete. ");
  Serial.print(devicesFound);
  Serial.println(" device(s) found.");

  if (devicesFound == 0) {
    Serial.println("No I2C devices found. Check connections.");
  }
}

Let’s break it down:

  1. Wire.begin() – Initializes the Arduino as an I2C master.
  2. Serial.begin(9600) – Starts serial communication at 9600 baud so we can see the results in the Serial Monitor.
  3. The loop iterates through addresses 1 to 126 (0x01 to 0x7E). Address 0 is reserved for general call, and 127 is reserved for special purposes.
  4. Wire.beginTransmission(addr) – Starts a transmission to the given address.
  5. Wire.endTransmission() – Ends the transmission and returns an error code. If the error is 0, it means the device acknowledged (ACK) – we found a device!
  6. Printing the address – We print it in hexadecimal format with leading zero for readability.
  7. Switch statement – Identifies common devices by their known addresses. This is optional but helpful.
  8. Counting devices – We increment devicesFound for each ACK.
  9. Final report – Prints the total number of devices found.

Loop Function

void loop() {
  // Rescan every 10 seconds
  delay(10000);
  Serial.println("\nRescanning...");
  setup();
}

The loop() function simply waits 10 seconds and then calls setup() again to rescan. This is useful if you add or remove devices while the system is running.

Key Concepts for Students

What is I2C?

I2C (Inter-Integrated Circuit), also called TWI (Two-Wire Interface), is a synchronous serial communication protocol. It uses two bidirectional open-drain lines: SDA (data) and SCL (clock). Each device on the bus has a unique 7-bit address (though 10-bit addresses exist). The master (Arduino) initiates communication and generates the clock signal.

How the Scanner Works

The scanner sends a start condition followed by the address byte (7-bit address + read/write bit). If a device with that address exists, it responds with an ACK (acknowledge) by pulling SDA low during the ninth clock pulse. If no device responds, a NACK (not acknowledge) is received, and endTransmission() returns a non-zero error code.

Why Use a Scanner?

Common Pitfalls and Debugging Tips

  1. Wrong wiring – Double-check that SDA and SCL are connected correctly. Swapping them won’t work.
  2. Missing pull-up resistors – I2C lines need pull-up resistors (typically 4.7kΩ) to VCC. Many breakout boards include them, but if you’re building from scratch, don’t forget them.
  3. Address conflicts – If two devices share the same address, neither will work reliably. Use the scanner to detect conflicts.
  4. Baud rate mismatch – The scanner uses default 100 kHz (standard mode). Some devices may need slower speeds. You can change it with Wire.setClock(400000) for fast mode.
  5. Serial Monitor not showing output – Make sure the baud rate in the Serial Monitor matches 9600.

In Velxio, you can open the Serial Monitor from the editor toolbar. If you don’t see output, check that the code compiled successfully (look for errors in the console).

Velxio editor toolbar with compile and run controls

Suggested Extensions

Once you have the basic scanner working, try these enhancements:

To add libraries in Velxio, use the Library Manager.

Velxio Library Manager modal listing installed Arduino libraries

Conclusion

You’ve just built a universal I2C scanner that works with any Arduino-compatible board. This tool is invaluable for debugging and exploring new I2C modules. The best part? You can run it right now in your browser on Velxio without any hardware.

Ready to try it yourself? Open the live example and start scanning:

👉 Launch the I2C Scanner on Velxio

Happy hacking!


Share this post on:
David Montero

Written by

David Montero

Creator of Velxio, the open-source circuit and Arduino simulator.

GitHub velxio.dev

Related posts


Previous Post
I2C RTC Clock with DS1307 on Velxio
Next Post
Serial LED Control with Arduino: Send Commands to Blink