From 70bcf17f20546665fbf7fc720d43d6c7f724a116 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Sat, 24 Oct 2026 22:09:56 +1100 Subject: [PATCH] machine/esp32xx: wait for the I2C bus to be free before a transaction After a NACK, the controller keeps the bus busy for approximately 12 us. If the next transaction starts in this time, it does not complete and gives a timeout. Thus a read only I2C scan finds no devices. Wait for SR.BUS_BUSY to clear before the transaction starts. If the bus stays busy, reset the FSM before returning the timeout, as ESP-IDF does. --- src/machine/machine_esp32xx_i2c.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/machine/machine_esp32xx_i2c.go b/src/machine/machine_esp32xx_i2c.go index 5c9da51965..7a23c4bef4 100644 --- a/src/machine/machine_esp32xx_i2c.go +++ b/src/machine/machine_esp32xx_i2c.go @@ -201,6 +201,18 @@ func nanotime() int64 const i2cIntMask = esp.I2C_INT_STATUS_END_DETECT_INT_ST_Msk | esp.I2C_INT_STATUS_TRANS_COMPLETE_INT_ST_Msk | esp.I2C_INT_STATUS_TIME_OUT_INT_ST_Msk | esp.I2C_INT_STATUS_NACK_INT_ST_Msk func (i2c *I2C) transmit(addr uint16, cmd []i2cCommand, timeoutMS int) error { + // After a NACK the controller keeps the bus busy for some microseconds. + // A start in this time does not complete and gives a timeout. + end := nanotime() + int64(timeoutMS)*1000000 + for i2c.Bus.GetSR_BUS_BUSY() != 0 { + if nanotime() > end { + // Reset the FSM so a later call can recover, same as ESP-IDF + // https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/i2c.c#L1451 + i2c.resetMaster() + return errI2CBusReadyTimeout + } + } + // Reset the FIFOs, bytes left after a NACK would go out in the next transaction. // Same as ESP-IDF https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/i2c.c#L1456 i2c.Bus.SetFIFO_CONF_TX_FIFO_RST(1)