I2C Scanner Using ESP32 IDF
Contents
Introduction
I2C (Inter-Integrated Circuit) is a common serial communication protocol used to connect microcontrollers and various peripherals. In I2C communication, there is a master device and one or more slave devices. Each slave device has a unique 7-bit or 10-bit address, and the master device selects the slave device to communicate with by sending this address.
Working Principle
The I2C scanner works by trying to communicate with all possible I2C addresses to identify slave devices present on the BUS. Specifically, the master attempts to send a "start condition" and a "stop condition" to every possible address and checks whether the slave device answers. If the slave answers, it means there is a device at that address.
Install VSCode & ESP-IDF VSCode Extension
Make sure you have installed and configured the ESP-IDF development environment. You can also refer to the article the ESP32 Tutorial – ESP-IDF With VSCode.
Create A New ESP32 Project
We can use VSCode's IDF plug-in to create a new ESP32 project. Please refer to ESP32 Tutorial – How To Create An ESP32 Project With VSCode.
Implemented Using ESP32 IDF
The following is a scanner program written using ESP32 IDF I2C, as shown below...
#include <stdio.h>
#include "driver/i2c.h"
#include "esp_log.h"
#define I2C_MASTER_SCL_IO 22 // Set SCL pin
#define I2C_MASTER_SDA_IO 21 // Set SDA pin
#define I2C_MASTER_NUM I2C_NUM_0 // Use I2C port 0
#define I2C_MASTER_FREQ_HZ 100000 // Set I2C frequency to 100kHz
#define I2C_MASTER_TX_BUF_DISABLE 0 // Disable TX buffer
#define I2C_MASTER_RX_BUF_DISABLE 0 // Disable RX buffer
static const char *TAG = "i2c_scanner";
void i2c_master_init() {
// Configure I2C master settings
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = I2C_MASTER_SDA_IO;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = I2C_MASTER_SCL_IO;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
conf.clk_flags = 0;
// Apply configuration to the I2C driver
i2c_param_config(I2C_MASTER_NUM, &conf);
// Install I2C driver
i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
void i2c_scanner() {
printf("Scanning I2C bus...\n");
// Iterate over all possible I2C addresses
for (int addr = 1; addr < 127; addr++) {
// Create I2C command link
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// Send I2C address with write bit
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_stop(cmd);
// Execute I2C command and check for response
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(1000));
i2c_cmd_link_delete(cmd);
// If device responds, print the address
if (ret == ESP_OK) {
printf("Found device at address 0x%02x\n", addr);
}
}
printf("I2C scan complete.\n");
}
void app_main() {
// Initialize I2C master
i2c_master_init();
// Start scanning for I2C devices
i2c_scanner();
}
Program Description
1. i2c_master_init:
This code configures the SDA and SCL pins of the I2C master, the I2C clock frequency (100kHz), and installs the I2C driver.
The i2c_param_config function is used to set I2C parameters.
The i2c_driver_install function is used to install the I2C driver.
2. i2c_scanner:
This code loops through all possible I2C addresses (1 to 127). For each address, the master attempts I2C communication once. If the communication is successful (i.e. a reply from the slave is received), it means there is a device at that address.
Use i2c_cmd_link_create to create the I2C command chain and send the command via i2c_master_cmd_begin.
If communication is successful (ESP_OK), the discovered device address is printed.
3. Main program (app_main):
Initialize the I2C and run the scanner function.
Compile, Flash & Monitor
Find the "ESP32-IDF: Build, Flash and Monitor" ICON in VSCode to execute and burn.
Results
If there is no problem connecting the I2C device, we can check the printing results on the terminal, and the results will be as follows...
Scanning I2C bus...
Found device at address 0x3C
Found device at address 0x68
I2C scan complete.
I2C Scanner Program Description
When you run this I2C scanner program on ESP32, there are two main results, depending on whether there is an I2C device connected to the BUS.
1. If there is an I2C device connected to the BUS
When at least one device is connected to the I2C BUS, the scanner attempts to communicate with each address. Whenever it successfully communicates with an address and receives a reply from the device, it outputs the device's I2C address. The output might look like this:
Scanning I2C bus...
Found device at address 0x3C
Found device at address 0x68
I2C scan complete.
Scanning I2C bus...: Indicates that I2C scanning has started.
Found device at address 0x3C and Found device at address 0x68: These lines indicate that an I2C device was detected at address 0x3C and 0x68 respectively. Each address corresponds to an I2C slave connected to the BUS.
I2C scan complete.: Indicates that the scan has been completed.
2. If no I2C device is connected to the BUS
When no device is connected to the I2C BUS, the scanner still attempts to communicate with every possible address. However, the scanner does not detect any device addresses because no device answers. The output will look like this:
Scanning I2C bus...
I2C scan complete.
Scanning I2C bus...: Indicates that I2C scanning has started.
I2C scan complete.: Indicates that the scan has been completed.
Question
There is no other output between these two lines, which means that the device was not detected at all addresses within the scan range. This situation may occur under the following circumstances:
1. No device connected: There is no device on the I2C BUS physically connected to the ESP32.
2. Device wiring problem: The SDA or SCL line of the I2C device is not connected correctly, or the wiring is loose.
3. Power supply problem of the device: The I2C device is not powered or has insufficient power, resulting in the inability to answer I2C communication.
4. Address conflict or configuration error: The device address is set incorrectly or there is an address conflict, causing the scanner to be unable to communicate with the device.
5. Device does not support high frequency: Different I2C devices have different upper clock speeds. Some devices may only support standard mode (100 kHz) or fast mode (400 kHz). If the master's I2C clock frequency is set beyond the range supported by the slave, for example to 1 MHz or higher, the slave may not respond to communications properly, causing the scanner to fail to detect the device.
6. Pull-up Resistors are not suitable: In I2C communication, the SDA and SCL lines usually require pull-up resistors to ensure that the signal line can correctly return from low level to high level. When the frequency increases, if the value of the pull-up resistor is too large, the line may not be pulled up correctly during the clock cycle, causing signal problems and affecting communication stability.
Conclusion
The I2C scanner may not be able to scan the device at too high a frequency. This is mainly due to issues such as the upper frequency limit supported by the device, signal integrity, and the setting of the pull-up resistor. By reducing the I2C clock frequency and checking the hardware settings, you can improve the scan success rate and ensure that all connected I2C devices are detected.