ESP-IDF: I2C-сканер на ESP32 — находим адреса I2C-устройств

Перевод статьи ESP-IDF: ESP32 I2C Scanner – Finding the Address of I2C Devices — Rui Santos & Sara Santos, Random Nerd Tutorials.
Это краткое руководство показывает, как создать на ESP-IDF I2C-сканер, который находит адреса всех I2C-устройств, подключённых к пинам SDA и SCL ESP32.
Что понадобится заранее#
Прежде чем следовать этому руководству, установите расширение ESP-IDF в VS Code. Если ещё не сделали:
Для проверки I2C-сканера мы используем датчик BME280. Что понадобится:
- ESP32;
- модуль датчика BME280;
- макетная плата;
- соединительные провода.
Примечание: можно взять любое другое I2C-устройство — просто подключите его к правильным пинам I2C своей платы.
Схема — ESP32 с BME280 по I2C#
Для проверки I2C мы используем модуль BME280. Подключите пин SDA датчика к GPIO 21, а SCL — к GPIO 22 (сверьтесь с распиновкой ESP32).

ESP32 с BME280 по I2C: SDA → GPIO 21, SCL → GPIO 22.
Для плат ESP32-S3 пины I2C другие: SDA подключается к GPIO 8, а SCL — к GPIO 9.

ESP32-S3 с BME280 по I2C: SDA → GPIO 8, SCL → GPIO 9.
Создание проекта ESP-IDF из шаблона#
Расширение ESP-IDF позволяет легко создать проект с нуля — все нужные файлы и настройки генерируются автоматически. Чтобы создать новый проект в VS Code:
- Откройте расширение ESP-IDF (Espressif) и разверните меню Advanced.
- Нажмите New Project Wizard.
- Выберите Use ESP-IDF v6.X, чтобы задать версию фреймворка.

Мастер нового проекта: выбор версии ESP-IDF.
В меню выберите пример ESP-IDF Templates и нажмите Create project using template sample project.

Выбор шаблонного проекта из ESP-IDF Templates.
Откроется окно, где нужно заполнить поля:
- Project Name — имя проекта;
- Enter Project Directory — папка для файлов проекта. Не используйте папки Google Drive / OneDrive / Dropbox: при сборке создаётся множество файлов, и в облачной папке это может быть крайне медленно;
- ESP-IDF Target — целевой чип (в примере — esp32s3);
- ESP-IDF Board — для esp32s3 нужно выбрать конфигурацию: ESP32-S chip (via builtin USB-JTAG);
- Serial Port — COM-порт, к которому подключён ESP32;
- Choose Template — синяя кнопка создания проекта из шаблона.

Форма мастера: имя проекта, папка, чип, плата, порт и шаблон.
Открытие проекта в VS Code#
Через несколько секунд в VS Code появится уведомление. Нажмите Open Project, чтобы открыть только что созданный проект.

Уведомление для открытия нового проекта.
Если уведомление не появилось, откройте проект вручную: File → Open Folder…, найдите папку проекта и нажмите Select Folder.
ESP-IDF создаёт много файлов и папок. Для этого руководства оставьте все файлы по умолчанию без изменений — мы будем менять только файл main.c. Чтобы открыть его: откройте проводник проекта (первая иконка на левой панели), разверните папку проекта, затем папку main и кликните по файлу main.c.

Открытие файла main.c в проводнике проекта.
Код — I2C-сканер на ESP-IDF#
Чтобы найти I2C-адрес конкретного датчика, дисплея или другой периферии, подключите её к пинам I2C ESP32 и запустите сканер. Скопируйте следующий код в файл main.c проекта и сохраните.
/*
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);
}
Как работает код#
Библиотеки. Подключаем нужные библиотеки:
stdio.h— стандартная библиотека C, нужна дляprintf, выводящей отладочную информацию в монитор порта;esp_log.h— фреймворк для форматированных логов в мониторе порта;driver/i2c_master.h— драйвер I2C из ESP-IDF;freertos/FreeRTOS.h— базовые типы и функции FreeRTOS;freertos/task.h— управление задачами.
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
Пины SDA и SCL. Убедитесь, что периферия подключена к правильным пинам I2C. В примере — ESP32-S3 (SDA → GPIO 8, SCL → GPIO 9).
#define I2C_SDA_PIN 8
#define I2C_SCL_PIN 9
#define I2C_TIMEOUT_MS 50
Тег логирования. Префиксом I2C_SCANNER помечаются все отладочные сообщения.
static const char *TAG = "I2C_SCANNER";
Функция i2c_scanner_task(). Это задача FreeRTOS, в которой собрано всё нужное для работы сканера. Структура ниже создаёт конфигурацию мастера шины I2C с правильными пинами, а i2c_new_master_bus инициализирует I2C-железо ESP32 по этой конфигурации.
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));
Запуск сканера. Сам сканер работает в бесконечном цикле, повторяясь каждые 10 секунд. Код перебирает все 128 возможных 7-битных адресов (от 0x00 до 0x7F). Ключевая часть — функция i2c_master_probe(), которая отправляет по I2C сигнал подтверждения (ACK), чтобы проверить, откликнется ли подключённое устройство. Устройства, ответившие ESP_OK, считаются найденными, и их адрес выводится в терминал.
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");
}
}
По завершении сканирования выводится, сколько устройств найдено, и нумерованный список их I2C-адресов.
app_main(void). В проекте ESP-IDF функция app_main всегда вызывается при запуске — это аналог setup() в Arduino. Здесь мы выводим отладочную информацию и создаём задачу FreeRTOS, выполняющую i2c_scanner_task().
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);
}
Сборка и прошивка кода на ESP32#
Чтобы собрать и прошить код ESP-IDF, нужно: выбрать метод прошивки (UART), номер COM-порта, целевой чип (ESP32), собрать код и прошить его на плату. Все эти команды доступны в нижней панели VS Code (если вы создавали проект мастером, они, возможно, уже настроены верно).
Если настройки неверны: сначала кликните по иконке «звезды» и выберите метод прошивки UART. Затем при подключённой плате кликните по иконке COM-порта (розетка) и выберите правильный порт. Также выберите целевой чип, кликнув по иконке чипа внизу (в примере — esp32s3), и для этой платы — конфигурацию ESP32-S3 chip (via builtin USB-JTAG).
Теперь соберите проект, кликнув по иконке гаечного ключа (Build Project). Первая сборка обычно занимает чуть больше времени; по завершении в терминале появится сообщение Build Successfully.

Успешная сборка проекта.
Последний шаг — прошить проект на ESP32, кликнув Flash Device (иконка молнии). Возможно, потребуется зажать кнопку BOOT на плате, чтобы перевести её в режим прошивки. По завершении появится сообщение Flash Done.

Сообщение об успешной прошивке.
Демонстрация#
Если вы всё сделали правильно, пример уже работает на плате. Откройте окно терминала, кликнув по инструменту Monitor Device (иконка экрана).
Подключив датчик BME280 к ESP32:

ESP32 с BME280, подключённым по I2C.
Через пару секунд в мониторе порта появится I2C-адрес периферии. Если на одной шине несколько устройств — покажутся адреса всех. В нашем случае найдено Device 0x76 address, что соответствует I2C-адресу BME280.

Вывод сканера: найдено устройство по адресу 0x76 (BME280).
Итог#
В этом руководстве вы научились быстро находить адреса I2C-устройств. Достаточно подключить I2C-периферию к ESP32 и загрузить приведённый код сканера.
Спасибо за чтение.