ESP-IDF: ESP32 I2C Scanner – Finding the Address of I2C Devices

This is a quick guide that shows how to create an ESP-IDF I2C scanner that finds the address of all I2C devices connected to the ESP32 SDA and SCL pins.

ESP-IDF: ESP32 I2C Scanner – Finding the Address of I2C Devices

Related Guide: ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE).

Prerequisites

Before following this guide, you need to install the ESP-IDF extension on VS Code IDE (Microsoft Visual Studio Code). Follow the next guide to install it, if you haven’t already:

To test the I2C scanner, we’ll be using a BME280 sensor. Here’s a list of the parts you need to build the circuit:

Note: you can use any other I2C device, you just need to wire it to the correct I2C pins of your device.

Schematic – ESP32 with BME280 using I2C

To test the I2C communication, we’ll be using the BME280 sensor module. Wire the sensor SDA pin to GPIO 21 and SCL to GPIO 22, as shown in the following schematic diagram (check ESP32 Pinout Reference).

ESP32 with BME280 Wiring Schematic Diagram I2C

In case of the ESP32-S3 boards, the I2C pins are SDA connected to GPIO 8 and SCL connected to GPIO 9, as shown in the following schematic diagram (check ESP32-S3 GPIO Reference Guide).

ESP-IDF ESP32 BME280 Sensor I2C SDA SCL Wiring Schematic Diagram

Creating an ESP-IDF Template App Project for the ESP32

The ESP-IDF extension provides an easy way to create a project from scratch with all the required files and configurations generated automatically.

To create a new ESP-IDF project on VS Code, follow these steps:

  1. Open the ESP-IDF Espressif extension and expand the “Advanced” menu
  2. Click the “New Project Wizard” option
  3. Choose the “Use ESP-IDF v6.X” to select the ESP-IDF framework version
VS Code Open ESP-IDF Extension New Project Wizard Select ESP-IDF Version

In the menu, select the “ESP-IDF Templates” sample project and press the “Create project using template sample project” button.

ESP-IDF ESP32 Create New Sample Project using IDF Template

A new window opens, you need to fill in these fields:

  • Project Name: type the desired project name;
  • Enter Project Directory: click the folder icon and select the target folder to save all your project files. You can use any directory. Note: do NOT use a Google Drive / One Drive / Dropbox folder, because it will write/create many files during the building process—if it’s on a cloud folder, this process might be extremely slow;
  • ESP-IDF Target: select the target device chip, I’m using an ESP32 with the esp32s3 chip;
  • ESP-IDF Board: for the esp32s3 chip, I also need to select the configuration: ESP32-S chip (via builtin USB-JTAG);
  • Serial Port: while having your ESP32 board connected to your computer, select the correct COM port number that refers to your ESP32;
  • Choose Template: click the blue button to create a new project using a template.
ESP-IDF ESP32 Create Open New Project Wizard Menu Select Directory Board Template

Opening the ESP-IDF Project on VS Code

After a few seconds, a notification will appear on a new window on VS Code. You can click “Open Project” to open the newly created ESP-IDF sample project template.

ESP-IDF ESP32 Open New Project Sample

IMPORTANT: if you didn’t see the notification that allows you to automatically open the ESP-IDF project on VS Code, you can easily do it by following these instructions:

Go to File > Open Folder…

ESP-IDF ESP32 Open Project Folder VS Code File Menu

Browse on your computer for the esp-idf-project folder (your project folder name that you’ve previously defined) and “Select Folder“.

ESP-IDF ESP32 Open Project VS Code Select Folder

That’s it! Your new ESP-IDF project template has been successfully created and opened.

ESP-IDF generates many files, folders, and subfolders for your project. For this guide, I recommend keeping all the default files unchanged; we will only modify the main.c file.

The example codes will be written in the main.c file. To open it, follow these instructions:

  1. Open the project explorer by clicking the first icon on the left sidebar.
  2. Select your project folder name, in my case it’s “ESP-IDF-PROJECT“.
  3. Expand the “main” folder.
  4. Click the “main.c” file.
  5. The default main.c template file loads in the code window.
ESP-IDF ESP32 Open Project in a VS Code Browse to Main C File

Code – ESP-IDF: ESP32 I2C Scanner

If you want to find the I2C address of a specific sensor, display, or any other I2C peripheral, connect it to the ESP32 I2C pins and then run the I2C scanner provided.

Copy the following code to the ESP-IDF project main.c file and save it.

/*  
  Rui Santos & Sara Santos - Random Nerd Tutorials
  https://RandomNerdTutorials.com/esp-idf-esp32-i2c-scanner/
*/
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define I2C_SDA_PIN    8        // Change to your SDA pin
#define I2C_SCL_PIN    9        // Change to your SCL pin
#define I2C_TIMEOUT_MS 50       // Timeout for probing each address (in milliseconds)

static const char *TAG = "I2C_SCANNER";

void i2c_scanner_task(void *pvParameters)
{
    // Configure and initialize the I2C master bus
    i2c_master_bus_config_t bus_cfg = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .sda_io_num = I2C_SDA_PIN,
        .scl_io_num = I2C_SCL_PIN,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = false,
    };

    i2c_master_bus_handle_t bus_handle;
    ESP_ERROR_CHECK(i2c_new_master_bus(&bus_cfg, &bus_handle));

    while (1) {
        ESP_LOGI(TAG, "Starting I2C scan...");

        uint8_t found_addresses[128] = {0};  // To store found devices
        int devices_found = 0;

        // Optional: Print classic grid header
        printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");

        for (int addr = 0x00; addr <= 0x7F; addr++) {
            if (addr % 16 == 0) {
                printf("%02x: ", addr);
            }
            // Skip general call (0x00) and reserved addresses
            esp_err_t ret = i2c_master_probe(bus_handle, addr, I2C_TIMEOUT_MS);

            if (ret == ESP_OK) {
                printf("%02x ", addr);
                if (addr >= 0x08 && addr <= 0x77) {  // Find only count valid 7-bit addresses
                    found_addresses[devices_found++] = addr;
                }
            } else if (ret == ESP_ERR_NOT_FOUND) {
                printf("-- ");
            } else {
                printf("UU ");  // Timeout or other error
            }

            if ((addr + 1) % 16 == 0) {
                printf("\n");
            }
        }
        // Print summary
        printf("\nScan complete: %d device(s) found.\n", devices_found);
        for (int i = 0; i < devices_found; i++) {
            printf("%d. Device 0x%02x address\n", i + 1, found_addresses[i]);
        }
        if (devices_found == 0) {
            printf("\n");  // Extra newline for readability when none found
        }
        // Repeats scan every 10 seconds (optional)
        vTaskDelay(pdMS_TO_TICKS(10000));
    }
    // Cleanup (never reached in this loop)
    ESP_ERROR_CHECK(i2c_del_master_bus(bus_handle));
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_LOGI(TAG, "I2C scanner starting on SDA=%d, SCL=%d", I2C_SDA_PIN, I2C_SCL_PIN);
    xTaskCreate(i2c_scanner_task, "i2c_scanner", 4096, NULL, 5, NULL);
}

View raw code

How the Code Works

In this section, we’ll take a look at the code to see how it works.

Libraries

We start by including the required libraries:

  • stdio.h – the standard C library will be used for the printf function that prints the debugging information in the serial monitor;
  • esp_log.h – offers a framework to format log messages in the serial monitor for debugging;
  • i2c_master.h – the ESP-IDF I2C driver;
  • FreeRTOS.h – provides the core FreeRTOS types and functions;
  • task.h – allows to use task management.
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

Setting SDA and SCL Pins

Make sure you have your I2C peripheral properly connected to your board on the right I2C pins. In my case, I’m using an ESP32-S3 (I2C_SDA_PIN -> GPIO 8 and I2C_SCL_PIN -> GPIO 9).

#define I2C_SDA_PIN    8
#define I2C_SCL_PIN    9
#define I2C_TIMEOUT_MS 50

Logging Tag

The “I2C_SCANNER” prefix will be used to log all the debugging messages.

static const char *TAG = "I2C_SCANNER";

i2c_scanner_task() function

The i2c_scanner_task() function is the FreeRTOS task that has everything required to run the I2C scanner to find the connected devices.

void i2c_scanner_task(void *pvParameters)
{
    (...)
}

This structure creates the I2C master bus configuration with the correct I2C pins,

i2c_master_bus_config_t bus_cfg = {
    .clk_source = I2C_CLK_SRC_DEFAULT,
    .sda_io_num = I2C_SDA_PIN,
    .scl_io_num = I2C_SCL_PIN,
    .glitch_ignore_cnt = 7,
    .flags.enable_internal_pullup = false,
};

The i2c_new_master_bus initializes the I2C hardware on the ESP32 with the configuration variable to set the I2C pins.

i2c_master_bus_handle_t bus_handle;
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_cfg, &bus_handle));

Start the I2C Scanner

The actual scanner is inside an infinite loop that runs continuously every 10 seconds:

while (1)
{
    (...)
    ESP_ERROR_CHECK(i2c_del_master_bus(bus_handle));
}

This code section loops through all 128 possible 7-bit addresses (from 0x00 to 0x7F). The key part of the scanner is the i2c_master_probe() function that sends an I2C acknowledgment message to see if any connected device acknowledges it. Devices that respond with ESP_OK are valid and their address is printed in the terminal.

uint8_t found_addresses[128] = {0}; 
int devices_found = 0;

printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");

for (int addr = 0x00; addr <= 0x7F; addr++) {
    if (addr % 16 == 0) {
        printf("%02x: ", addr);
    }

    esp_err_t ret = i2c_master_probe(bus_handle, addr, I2C_TIMEOUT_MS);

    if (ret == ESP_OK) {
        printf("%02x ", addr);
        if (addr >= 0x08 && addr <= 0x77) {
            found_addresses[devices_found++] = addr;
        }
    } else if (ret == ESP_ERR_NOT_FOUND) {
        printf("-- ");
    } else {
        printf("UU ");
    }

    if ((addr + 1) % 16 == 0) {
        printf("\n");
    }
}

Once the scan is complete, it prints a message saying how many devices has found and a numbered list with their corresponding I2C address.

printf("\nScan complete: %d device(s) found.\n", devices_found);
for (int i = 0; i < devices_found; i++) {
    printf("%d. Device 0x%02x address\n", i + 1, found_addresses[i]);
}
if (devices_found == 0) {
    printf("\n"); 
}

app_main(void)

When creating an ESP-IDF project, the app_main function will always be called to run. This function is where you need to write your code for any ESP-IDF applications; it is the equivalent of the setup() in Arduino programming. When the ESP32 boots, the ESP-IDF framework calls app_main.

In the app_main(void) function, you start by printing some debugging information. Finally, create a FreeRTOS task that runs the i2c_scanner_task() function.

void app_main(void)
{
    ESP_LOGI(TAG, "I2C scanner starting on SDA=%d, SCL=%d", I2C_SDA_PIN, I2C_SCL_PIN);
    xTaskCreate(i2c_scanner_task, "i2c_scanner", 4096, NULL, 5, NULL);
}

Build and Flash Code to the ESP32 Board

To build and flash ESP-IDF code to the ESP32, you always need to follow this procedure. You need to select the flash method (UART), the COM port number, the target device (ESP32), build the code, and finally, flash it to the board. All these commands are available in the bottom menu bar of VS Code.

Make sure all your options are correct (they may already be properly configured if you used the project wizard).

VS Code ESP-IDF Check All the Configured Settings UART COM Port Target Board

However, if your setup is not correct, follow the next instructions to ensure everything is set up correctly. First, click the “Star” icon and select the flash method as UART.

VS Code ESP-IDF Select Flash UART Option to Program Flash ESP32

While the ESP32 board is connected to your computer, click the COM Port (plug icon) and select the correct port number that refers to your ESP32.

VS Code ESP-IDF Programming ESP32 Board Select Correct COM Port Number

You also need to select the target device. Click on the chip icon at the bottom bar. In my case, I have an ESP32 with the esp32s3 chip.

VS Code Select the ESP32 S3 or Correct Target Device ESP-IDF

For this board, I also need to select the configuration: ESP32-S3 chip (via builtin USB-JTAG).

VS Code ESP-IDF Select the ESP32 S3 chip via built in USB JTAG Target Device

Finally, your command bar at the bottom of VS Code should have similar options selected.

VS Code ESP-IDF Check All the Configured Settings UART COM Port Target Board

Now, you can build the project by clicking the wrench icon (Build Project) as shown in the image below.

VS Code Build Project Example Code ESP32 ESP-IDF

The first time you build a project, it usually takes a bit more time. Once completed, it should print a similar message in the Terminal menu and show a “Build Successfully” message.

VS Code Build Example Project ESP32 ESP-IDF Success Message

This is the final step. You can now flash the ESP-IDF project to the ESP32 by clicking the “Flash Device” button (thunder icon).

VS Code Flash Hello World Code Project to ESP32 ESP-IDF

Depending on your board, you might need to hold down the on-board BOOT button on your ESP32 to put it into flashing mode. Once the process is completed, it will pop-up a info message saying “Flash Done“.

VS Code Flash Hello World Project to ESP32 ESP-IDF Done Success Message

Demonstration

If you followed all the steps, the example should be running successfully on your board. Open your Terminal window — click the “Monitor Device” tool that is illustrated with a screen icon.

VS Code Open Terminal Window Monitor Device ESP32 ESP-IDF

Wait a couple of seconds, and the I2C address of your peripheral will be printed in the Serial Monitor. If you have more than one device connected to the same I2C bus, it will display the address of all devices.

Having the BME280 sensor attached to your ESP32:

ESP-IDF ESP32 BME280 I2C Sensor Wiring Circuit Demonstration

The terminal should be printing a message saying that the scan has been completed and the devices that were found. In our case, we found “Device 0x76 address” which corresponds to the BME280 I2C address.

ESP-IDF ESP32 Read I2C Scanner Find I2C Device Address

Wrapping Up

In this guide, you learned how to quickly find the address of I2C devices. You just need to connect your I2C peripheral to the ESP32 board and upload the I2C scanner code provided.

We hope you’ve found this guide useful. To learn more about I2C, but using Arduino IDE we recommend reading our article: ESP32 I2C Communication.

You might find it helpful to read other ESP-IDF guides:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

2 thoughts on “ESP-IDF: ESP32 I2C Scanner – Finding the Address of I2C Devices”

  1. The project does not run on my Mac Mini M4.
    1. The libraries were not found
    2. Error: …/.espressif/tools/python/v6.0.2/venv/lib/python3.11/site-packages/pydantic_core/_pydantic_core.cpython-311-darwin.so’
    (mach-o file, but is an incompatible architecture (have ‘arm64’, need
    ‘x86_64’))

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.