I2C Communication Issues Between ESP32 Master and ESP32Slave

Issue 1: When the master device is powered ON while the slave device remains powered OFF, the master sends the first packet of data after the slave powers ON. However, the slave does not respond correctly, and the master receives an unknown or corrupted response (junk data).

Issue 2: The master sends a "Motor ON" command to the slave, which the slave processes correctly and activates the motor as expected. However, when the master subsequently sends a request to the slave to retrieve the relay status, an unexpected issue arises. Instead of receiving the accurate status, the master receives an incorrect response opposite to the intended action. For instance, if the master sends an "ON" command (H), the master receives a response indicating "OFF" (L) from the slave during the status query.

Request for Assistance:

What could be causing the slave to send incorrect or corrupted responses during initialization?
How can I ensure reliable status reporting from the slave to the master after the motor command is executed?
Are there any recommended best practices to avoid junk responses in I2C communication during power transitions?

Please can you post both sketches in code tabs using this icon on the edit window image
If the serial monitor lines showing "endTansmission" value are that function's return value, then non-zero values indicate an error.
Posting a schematic may also help, particularly if it's an issue with motor power causing the coruption

1 Like

//MASTER `#include <Wire.h>

#define SLAVE_ADDRESS 0x55

void setup() {
Wire.begin(); // Initialize I2C as Master
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println("Master ready");
}

void loop() {
// Wait for the slave to respond to a ping message
pingSlave();
delay(3000);
receiveResponse();

delay(1000);
// Send the "Turn On" command
sendCommand("ON");
delay(6000); // Wait for the slave to respond
receiveResponse();

delay(15000);

// Send the "Turn Off" command
sendCommand("OFF");
delay(6000); // Wait for the slave to respond
receiveResponse();

delay(30000);
}

void pingSlave() {
Wire.beginTransmission(SLAVE_ADDRESS);
Wire.print("PING"); // Send PING message
uint8_t error = Wire.endTransmission();
if (error != 0) {
Serial.printf("Ping failed: %u\n", error);
return; // No acknowledgment from slave
}
Serial.println("Send ping request successfully");
delay(100); // Wait briefly for the slave to process
}

void sendCommand(String command) {
Wire.beginTransmission(SLAVE_ADDRESS);
Wire.print(command); // Send command to slave
uint8_t error = Wire.endTransmission(true);
Serial.print("Sent command: ");
Serial.println(command);
Serial.printf("endTransmission: %u\n", error);
Wire.flush(); // Ensure the buffer is cleared
}

void receiveResponse() {
byte response = 0; // Variable to store the received byte
Wire.requestFrom(SLAVE_ADDRESS, 3); // Request 1 byte from the slave

if (Wire.available()) {
response = Wire.read(); // Read the byte sent by the slave
Serial.println("Received response: " + String(response));
if (response == 200) {
Serial.println("Receive ping response");
} else if (response == 1) {
Serial.println("Motor ON");
} else if (response == 0) {
Serial.println("Motor OFF");
} else {
Serial.println("Unknown response");
}
} else {
Serial.println("No response received");
}
} // SLAVE #include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SDA_2 27
#define SCL_2 14
#define MOTOR_ON_PIN 25
#define MOTOR_OFF_PIN 26
#define SLAVE_ADDRESS 0x55 // I2C Slave address
bool motorStatus = false; // false means OFF, true means ON
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, -1);

byte responseToSend = 0; // 1 for ON, 0 for OFF

// Callback to handle commands received from Master
void receiveCommand(int numBytes) {
String commandReceived = ""; // Clear previous command
while (Wire.available()) {
char c = Wire.read();
commandReceived += c;
}
Serial.println("Received command: " + commandReceived);
updateDisplay("Received: " + commandReceived);

if (commandReceived == "PING") {
Serial.println("Received PING, preparing PONG");
responseToSend = 200;
} else if (commandReceived == "ON") {
turnMotorOn();
responseToSend = 1; // Set response to 1 for motor ON
} else if (commandReceived == "OFF") {
turnMotorOff();
responseToSend = 0; // Set response to 0 for motor OFF
} else {
updateDisplay("Unknown Command");
Serial.println("Unknown Command");
}
}

// Callback to send responses to Master
void sendResponse() {
Wire.beginTransmission(SLAVE_ADDRESS);
Wire.write(responseToSend);
Serial.println("Sent response: " + String(responseToSend));
uint8_t error = Wire.endTransmission();
Serial.printf("endTransmission: %u\n", error);
if (error == 0) {
Serial.println("Slave acknowledged communication.");
} else {
Serial.println("Failed to communicate with slave.");
}
}

void setup() {
// Initialize I2C as Slave
Wire.begin((uint8_t)SLAVE_ADDRESS);
Wire.onReceive(receiveCommand); // Set up receive callback
Wire.onRequest(sendResponse); // Set up request callback
Serial.begin(115200);
pinMode(MOTOR_ON_PIN, OUTPUT); // Set the ON relay pin as output
pinMode(MOTOR_OFF_PIN, OUTPUT); // Set the OFF relay pin as output
digitalWrite(MOTOR_ON_PIN, HIGH); // Ensure the ON relay is off initially
digitalWrite(MOTOR_OFF_PIN, HIGH); // Ensure the OFF relay is off initially
Serial.println("I2C Slave initialized successfully.");

Wire1.begin(SDA_2, SCL_2);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
updateDisplay("Slave Initialized");
}

void turnMotorOn() {
digitalWrite(MOTOR_ON_PIN, LOW); // Turn on the motor
motorStatus = true;
Serial.println("Motor turned ON");
updateDisplay("Motor turned ON");
}

void turnMotorOff() {
digitalWrite(MOTOR_OFF_PIN, HIGH); // Ensure the motor-off pin is inactive
motorStatus = false;
Serial.println("Motor turned OFF");
updateDisplay("Motor turned OFF");
}

void updateDisplay(const String& message) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(WHITE);
display.println(message);
display.display();
}

void loop() {
}` This is the Master and Slave code part. I didn't connect any motor in the slave, simply I connect master to slave I2C pins and ground pin. Additionly slave side connect OLED display. That all the schematic connections.

It would help if you posted it in code tabs. It's hard to read with no indentation. I'm not going to have time to look at it today as I'll be out socialising with family.

1 Like

I spent some time trying to build and understand your code, but I didn't get the slave to build and gave up. I get

Linking everything together...
"C:\\Users\\David\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\1.22.0-80-g6c4433a-5.2.0/bin/xtensa-esp32-elf-gcc" -nostdlib "-LC:\\Users\\David\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.4/tools/sdk/lib" "-LC:\\Users\\David\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.4/tools/sdk/ld" -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld -T esp32.rom.libgcc.ld -T esp32.rom.spiram_incompatible_fns.ld -u ld_include_panic_highint_hdl -u call_user_start_cpu0 -Wl,--gc-sections -Wl,-static -Wl,--undefined=uxTopUsedPriority -u __cxa_guard_dummy -u __cxx_fatal_exception -Wl,--start-group "C:\\Users\\David\\AppData\\Local\\Temp\\arduino_build_798851\\sketch\\ForumESP32I2CSlave.ino.cpp.o" "C:\\Users\\David\\AppData\\Local\\Temp\\arduino_build_798851\\libraries\\Wire\\Wire.cpp.o" "C:\\Users\\David\\AppData\\Local\\Temp\\arduino_cache_622040\\core\\core_a1f33d9f609e795adf937e044a6a8b7b.a" -lgcc -lesp32 -lphy -lesp_http_client -lmbedtls -lrtc -lesp_http_server -lbtdm_app -lspiffs -lbootloader_support -lmdns -lnvs_flash -lfatfs -lpp -lnet80211 -ljsmn -lface_detection -llibsodium -lvfs -ldl_lib -llog -lfreertos -lcxx -lsmartconfig_ack -lxtensa-debug-module -lheap -ltcpip_adapter -lmqtt -lulp -lfd -lfb_gfx -lnghttp -lprotocomm -lsmartconfig -lm -lethernet -limage_util -lc_nano -lsoc -ltcp_transport -lc -lmicro-ecc -lface_recognition -ljson -lwpa_supplicant -lmesh -lesp_https_ota -lwpa2 -lexpat -llwip -lwear_levelling -lapp_update -ldriver -lbt -lespnow -lcoap -lasio -lnewlib -lconsole -lapp_trace -lesp32-camera -lhal -lprotobuf-c -lsdmmc -lcore -lpthread -lcoexist -lfreemodbus -lspi_flash -lesp-tls -lwpa -lwifi_provisioning -lwps -lesp_adc_cal -lesp_event -lopenssl -lesp_ringbuf -lfr -lstdc++ -Wl,--end-group -Wl,-EL -o "C:\\Users\\David\\AppData\\Local\\Temp\\arduino_build_798851/ForumESP32I2CSlave.ino.elf"
C:\Users\David\AppData\Local\Temp\arduino_build_798851\sketch\ForumESP32I2CSlave.ino.cpp.o:(.literal._Z5setupv+0x20): undefined reference to `TwoWire::onReceive(void (*)(int))'
C:\Users\David\AppData\Local\Temp\arduino_build_798851\sketch\ForumESP32I2CSlave.ino.cpp.o:(.literal._Z5setupv+0x24): undefined reference to `TwoWire::onRequest(void (*)())'
C:\Users\David\AppData\Local\Temp\arduino_build_798851\sketch\ForumESP32I2CSlave.ino.cpp.o: In function `setup()':
C:\Users\David\Dropbox\WorkArea\Electronics\Microcontrollers\Sketches\ForumESP32I2CSlave/ForumESP32I2CSlave.ino:51: undefined reference to `TwoWire::onReceive(void (*)(int))'
C:\Users\David\Dropbox\WorkArea\Electronics\Microcontrollers\Sketches\ForumESP32I2CSlave/ForumESP32I2CSlave.ino:51: undefined reference to `TwoWire::onRequest(void (*)())'
collect2.exe: error: ld returned 1 exit status
Using library Wire at version 1.0.1 in folder: C:\Users\David\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\Wire 
exit status 1
Error compiling for board ESP32 Dev Module.

Which might be due to me using an older ESP32 library on IDE 1.8.13.

Did you ever have some I2C code working 100% reliably? e.g. start the slave first, then run the master which sends something, expects a specific reply, and checks it got the reply it expected?

If you've never had it working reliably, can you try this simple example https://randomnerdtutorials.com/esp32-i2c-master-slave-arduino/ ?

Regarding the code you posted: In the callbacks receiveCommand() and sendResponse(), I would avoid using serial.print and anything else that takes a significant time to execute. I'd e.g. have receiveCommand() deposit what was received in a buffer than can be read by loop() and do the printing from loop().

In this version of Arduino, I didn't encounter any issues during compilation or while testing the code. I also referred to the suggested website and tried their basic code. However, I faced the same issue where the master did not receive the first packet transmitted by the slave. Instead, it printed characters like //..........., then received the '1st packet' during the second transmission, the '2nd packet' during the third transmission, and so on.

I'll try you recommended approach: Instead of performing time-consuming operations (like printing to Serial) directly within the callback, I will use the callback only to store the received data in a buffer. Then, I can process the buffered data and take necessary actions in the loop() function.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.