This can be an important normal task, for instance when retrying an eeprom waiting for its last write burn cycle to complete before it will respond again.
We needed to do this on both the ESP32 S3 and the ESP32 S2 and found different issues on each (it seemed the Espressif I2C implementation is pretty flakey when you need to do something specific with it…)
Example code
void WaitForLastWriteToComplete ()
{
uint16_t TriesCount = 5000;
i2c_cmd_handle_t OurI2cCmdHandle = i2c_cmd_link_create(); //<<<<<<<<< ESP32 S3 NEEDED THIS HERE!
while (1)
{
//i2c_cmd_handle_t OurI2cCmdHandle = i2c_cmd_link_create(); //<<<<<<<<< ESP32 S2 NEEDED THIS HERE INSTEAD!
//----- DUMMY WRITE OPERATION -----
i2c_master_start(OurI2cCmdHandle);
i2c_master_write_byte(OurI2cCmdHandle, EXAMPLE_I2C_ADDR | EXAMPLE_I2C_WRITE_BIT, EXAMPLE_I2C_ACK_CHECK_EN);
i2c_master_stop(OurI2cCmdHandle);
//Send queued commands
i2c_ret = i2c_master_cmd_begin(EXAMPLE_I2C_PORT_NUMBER, OurI2cCmdHandle, (1 / portTICK_PERIOD_MS)); //ticks_to_wait small value as we're not expecting the device to clock stretch
if (i2c_ret != ESP_FAIL) //ESP_FAIL returned for Sending command error, slave hasn't ACK the transfer
break;
TriesCount--;
if (TriesCount == 0)
break;
//i2c_cmd_link_delete(OurI2cCmdHandle); //<<<<<<<<< ESP32 S2 NEEDED THIS HERE INSTEAD!
} //while (1)
i2c_cmd_link_delete(OurI2cCmdHandle); //<<<<<<<<< ESP32 S3 NEEDED THIS HERE!
}
Things discovered when getting this to work on an ESP32 S3
For the ESP32 S3
Do not use i2c_cmd_link_delete() and i2c_cmd_link_create() between back-to-back retrying. In tests it causes a 1 second delay after the first NACK generating i2c_ret == 0x107 which is Operation timed out. Very odd as it should have just generated a new short attempt and i2c_ret == -1 ESP_FAIL like the first attempt. But it doesn’t, removing the use of i2c_cmd_link_delete() and i2c_cmd_link_create() between tries solved it.
i2c_master_write_byte() ack_en (Enable ACK signal) value must be 1, not 0. If you use 0 then the NACK is ignored and you get a ESP_OK back on the first try.
For the ESP32 S2
Tried the exact same approach as for the ESP32 S3 we’d done previously, but on the ESP32 S2 we found once the write was acknowledged the ESP32 S2 I2C peripheral then went into crazy mode throwing out repeated stop conditions for no reason. The solution was to either have it write a byte after the address, or move the i2c_cmd_link_create() and i2c_cmd_link_delete() so they are used for every try…. completely opposite to the problem we encountered with the ESP32 S3.