In this task, we are implementing the I2C scanner using a microcontroller. It detects all connected I2C slave devices and displays their addresses over UART.
Note: When interfacing I²C devices operating at different voltages (e.g., 5 V ↔ 3.3 V), always use a voltage level shifter to ensure safe logic levels and reliable communication.
So, by connecting and configuring the slave devices and the I2C communication, we can implement the task.
Below are the solutions to the given task using different microcontrollers
We’re using an STM32 NUCLEO-F103RB board as an I2C master, which operates at a 3.3V logic level.
Circuit Diagram
Project Setup in STM32CubeIDE
HAL_Init()
→ Initializes HAL and system tick.SystemClock_Config()
→ Configures system clock (HSI + PLL).MX_GPIO_Init()
→ Initializes GPIO ports.MX_USART2_UART_Init()
→ Configures UART2.MX_I2C1_Init()
→ Configures I2C1.I2C Initialization (MX_I2C1_Init)
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler();
}
UART Initialization (MX_USART2_UART_Init)
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
if (HAL_UART_Init(&huart2) != HAL_OK) {
Error_Handler();
}
Header Includes
#include <string.h>
#include <stdio.h>
Private Variables
uint8_t tx_data = 0;
uint8_t address;
uint8_t devices = 0;
HAL_StatusTypeDef status;
Main Loop I2C Scanner Logic
The main scanning logic is implemented in the while(1)
loop:
HAL_UART_Transmit(&huart2, (uint8_t*)"\n\rI2C Scanner \n\r\n\r", 18, HAL_MAX_DELAY);
HAL_Delay(10);
while (1) {
devices = 0;
HAL_UART_Transmit(&huart2, (uint8_t*)"Scanning...\n\r", 13, HAL_MAX_DELAY);
for (address = 1; address <= 127; address++) {
status = HAL_I2C_IsDeviceReady(&hi2c1, address << 1, 2, 2);
if (status == HAL_OK) {
char msg[40];
int len = sprintf(msg, "I2C device found at address 0x%02X\n\r", address);
HAL_UART_Transmit(&huart2, (uint8_t*)msg, len, HAL_MAX_DELAY);
devices++;
}
}
if (devices == 0) {
HAL_UART_Transmit(&huart2, (uint8_t*)"No I2C devices found\n\r\n\r", 22, HAL_MAX_DELAY);
} else {
char msg[64];
int len = sprintf(msg, "Total no. of slave devices connected are %d\n\r\n\r", devices);
HAL_UART_Transmit(&huart2, (uint8_t*)msg, len, HAL_MAX_DELAY);
}
HAL_Delay(5000);
}
Scans all 127 possible I2C addresses every 5 seconds, transmits found devices' addresses via UART. Uses HAL_I2C_IsDeviceReady to check device presence and formats output messages.
Key HAL Functions Used
HAL_I2C_IsDeviceReady()
: Checks if a device responds at a given address.HAL_UART_Transmit()
: Sends scan results to a serial monitor.
Expected Output
When executed, the program outputs detected I2C devices via UART:
I2C Scanner
Scanning...
I2C device found at address 0x3C
Total no. of slave devices connected are: 1
The complete STM32CubeIDE project (including .ioc configuration, main.c, and HAL files) is available here:
📥 Download Project
We are using the ESP32 DevKit v4 development board and programming it using the Arduino IDE.
By default, the ESP32 Arduino core assigns:
In the given task, we used the default I²C Pins.
#include <Wire.h> // Include I2C library
// Define I2C pins for ESP32
#define I2C_SDA 21 // Default SDA pin for ESP32
#define I2C_SCL 22 // Default SCL pin for ESP32
void setup() {
// Start Serial Monitor for debugging
Serial.begin(115200);
delay(1000); // Give Serial some time to initialize
// Initialize I2C communication with custom SDA and SCL pins
Wire.begin(I2C_SDA, I2C_SCL);
Serial.println("\nESP32 I2C Scanner");
}
void loop() {
byte error, address; // Variables to hold I2C error code and device address
int devices = 0; // Counter for found devices
Serial.println("Scanning I2C bus...");
// Loop through all possible I2C addresses (1–126)
for (address = 1; address < 127; address++) {
// Try to start transmission with the current address
Wire.beginTransmission(address);
error = Wire.endTransmission(); // End transmission and capture error status
if (error == 0) {
// If no error, a device responded at this address
Serial.print("I2C device found at address 0x");
if (address < 16) Serial.print("0"); // Format leading zero for addresses < 0x10
Serial.println(address, HEX);
devices++;
}
else if (error == 4) {
// Error code 4 means unknown error at this address
Serial.print("Unknown error at address 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
}
}
// Print summary of scan results
if (devices == 0) {
Serial.println("No I2C devices found\n");
} else {
Serial.print("Total I2C devices found: ");
Serial.println(devices);
Serial.println("Scan complete\n");
}
delay(5000); // Wait 5 seconds before scanning again
}
Wire.begin(21, 22)
→ sets up I²C on ESP32 default pins.Wire.beginTransmission(address)
.Wire.endTransmission()
result:0
→ Device responded → print address.4
→ Unknown error → print error message.We are using the Arduino UNO development board and programming it using the Arduino IDE.
#include <Wire.h>
void setup() {
Wire.begin(); // Initialize I2C
Serial.begin(115200); // Initialize Serial communication
Serial.println("\nI2C Scanner");
}
void loop() {
byte error, address;
int devices = 0;
Serial.println("Scanning...");
for (address = 1; address <= 127; address++) { // I2C addresses range from 1 to 127
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) { // Device responded
Serial.print("I2C device found at address 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
devices++;
} else if (error == 4) {
Serial.print("Unknown error at address 0x");
if (address < 16) Serial.print("0");
Serial.println(address, HEX);
}
}
if (devices == 0) {
Serial.println("No I2C devices found\n");
} else {
Serial.print("Total no. of slave devices connected are ");
Serial.println(devices);
Serial.println("Scan complete\n");
}
delay(5000); // Wait 5 seconds before scanning again
}
Wire.begin()
function.Wire.beginTransmission()
and Wire.endTransmission()
.Wire.endTransmission()
returns 0, which means the slave responded successfully.Sample output with one slave device connected
Sample output with two slave devices connected