Skip to content

esp32s3/esp32c3: I2C.Tx returns nil when a read is not acknowledged (esp32xx counterpart of #5584) #5767

Description

@0hJonny

Summary

On ESP32-S3, machine.I2C0.Tx(addr, w, r) with a non-empty r returns nil when the addressed device does not acknowledge. A write-only Tx to the same address returns i2c: error: expected ACK not NACK.

#5584 reports the same behaviour on the classic ESP32. It was fixed in #5585 (machine_esp32_i2c.go). The same change to machine_esp32xx_i2c.go was reverted in #5602. The && !readLast check is still present in machine_esp32xx_i2c.go on dev:

case mask&esp.I2C_INT_STATUS_NACK_INT_ST_Msk != 0 && !readLast:

Environment

  • TinyGo 0.42.0 (tinygo version 0.42.0 linux/arm64 (using go version go1.26.2 and LLVM version 22.1.4)), target esp32s3-generic
  • Board: LILYGO T5-4.7-S3 rev V2.4 (ESP32-S3-WROOM-1-N16R8)
  • I2C0: SCL = GPIO17, SDA = GPIO18, 400 kHz. The board has external pull-ups, and the test program also enables the internal ones. SCL and SDA read high when idle in both cases.
  • On the bus: a GT911 at 0x5D. Nothing at 0x42.

1. Read and write to an address with no device

cfg := machine.I2CConfig{SCL: machine.GPIO17, SDA: machine.GPIO18, Frequency: 400 * machine.KHz}
machine.I2C0.Configure(cfg)

buf := []byte{0xEE, 0xEE, 0xEE, 0xEE}
err := machine.I2C0.Tx(0x42, []byte{0x81, 0x40}, buf) // read, nothing at 0x42
println(err == nil, buf[0], buf[1], buf[2], buf[3])

err = machine.I2C0.Tx(0x42, []byte{0x81, 0x40}, nil) // write only, same address
println(err)

Result. Each call was repeated 3 times per run, with identical results within a run:

read  0x42 (nothing there) | err: nil | data: 00 00 00 00
write 0x42 (nothing there) | err: i2c: error: expected ACK not NACK

Observed in 9 runs. The read never returned an error. The data was 00 00 00 00 in 7 runs and 81 81 81 81 in 2 runs.

2. Read from a device that does not acknowledge

In some runs the GT911 at 0x5D stopped acknowledging its address. Immediately before and after the machine.I2C0.Tx calls, a register-level transaction (section 3) reported an address NACK for 0x5D: command 1, the address write, was not executed. machine.I2C0.Tx reads from 0x5D returned err: nil with 00 00 00 00 or 81 81 81 81. Observed in 7 runs.

3. Register-level observation: READ commands with ack_en

For comparison, I built the same read transaction directly on the I2C0 registers, following ESP-IDF v4.4 i2c_master_write_read_device:

COMD0 RSTART
COMD1 WRITE  addr+W, 0x81, 0x40   (ack_en)
COMD2 RSTART
COMD3 WRITE  addr+R               (ack_en)
COMD4 READ   3 bytes
COMD5 READ   1 byte, ack_val = 1 (NACK)
COMD6 STOP

It was run with two encodings of the READ commands, reading the GT911 Product ID (4 bytes, "911") while the device was acknowledging. After each transaction I recorded the done bit (bit 31) of COMD0–COMD6, INT_RAW, and the RX FIFO contents.

a) READ commands with ack_en set, as in machine_esp32xx_i2c.go (i2cCMD_READ = 3<<11 | 1<<8, i2cCMD_READLAST = 3<<11 | 5<<8):

COMD0..6 done: 1111111 | INT_RAW: 0x00008692 | RX FIFO: 4 bytes: 39 31 31 00

All seven commands were executed, and the RX FIFO holds the correct data. INT_RAW has both TRANS_COMPLETE (0x80) and NACK (0x400) set. The result was identical in 5 runs.

b) READ commands without ack_en, as ESP-IDF v4.4 builds them in i2c_master_read_static (only ack_val and op_code):

err: nil | data: 39 31 31 00

No NACK interrupt, in every run where the device was acknowledging.

With encoding (b), and with NACK checked on every transaction, a read from 0x42 returns an address NACK: COMD1 is not executed and the RX FIFO is empty.

Code

I can run tests or patches on ESP32-S3 hardware.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions