Description
A problem was encountered with the I2C Wire Library for ESP32
An ESP8266 program that sent a long I2C Write string to a slave device no longer worked when ported to ESP32.
An oscilloscope trace showed SCL was only going to mid level when the slave device was trying to pull it low. This seemed to be caused by both the ESP32 and slave device trying to drive the clock line at the same time.
Further investigation showed that Wire.begin called i2cAttachSCL() in esp32-hal-i2c.c and this used pinMode to set SCL as OUTPUT, rather than OUTPUT_OPEN_DRAIN.
I understand that the I2C standard calls for external pullup resistors on both SCL and SDA lines. For SCL, this is needed along with Open Drain outputs, so that the slave device can pull the SCL line low to temporarily suspend bus activity, known as "clock stretching".
To test the theory, I changed line 70 in esp32-hal-i2c.c from pinMode(scl, OUTPUT); to pinMode(scl, OUTPUT_OPEN_DRAIN); as shown below.
i2c_err_t i2cAttachSCL(i2c_t * i2c, int8_t scl)
{
if(i2c == NULL){
return I2C_ERROR_DEV;
}
pinMode(scl, OUTPUT_OPEN_DRAIN);
// pinMode(scl, OUTPUT);
pinMatrixOutAttach(scl, I2C_SCL_IDX(i2c->num), false, false);
pinMatrixInAttach(scl, I2C_SCL_IDX(i2c->num), false);
return I2C_ERROR_OK;
}
The code started working again and the ESP32 communicated correctly with the I2C slave device.
I am a newbie so please forgive me if this should have been posted as a Pull Request.