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.
Summary
On ESP32-S3,
machine.I2C0.Tx(addr, w, r)with a non-emptyrreturnsnilwhen the addressed device does not acknowledge. A write-onlyTxto the same address returnsi2c: 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 tomachine_esp32xx_i2c.gowas reverted in #5602. The&& !readLastcheck is still present inmachine_esp32xx_i2c.goondev:tinygo/src/machine/machine_esp32xx_i2c.go
Line 288 in e53dfdf
Environment
tinygo version 0.42.0 linux/arm64 (using go version go1.26.2 and LLVM version 22.1.4)), targetesp32s3-generic1. Read and write to an address with no device
Result. Each call was repeated 3 times per run, with identical results within a run:
Observed in 9 runs. The read never returned an error. The data was
00 00 00 00in 7 runs and81 81 81 81in 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.Txcalls, a register-level transaction (section 3) reported an address NACK for 0x5D: command 1, the address write, was not executed.machine.I2C0.Txreads from 0x5D returnederr: nilwith00 00 00 00or81 81 81 81. Observed in 7 runs.3. Register-level observation: READ commands with
ack_enFor comparison, I built the same read transaction directly on the I2C0 registers, following ESP-IDF v4.4
i2c_master_write_read_device: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 thedonebit (bit 31) of COMD0–COMD6,INT_RAW, and the RX FIFO contents.a) READ commands with
ack_enset, as inmachine_esp32xx_i2c.go(i2cCMD_READ = 3<<11 | 1<<8,i2cCMD_READLAST = 3<<11 | 5<<8):All seven commands were executed, and the RX FIFO holds the correct data.
INT_RAWhas bothTRANS_COMPLETE(0x80) andNACK(0x400) set. The result was identical in 5 runs.b) READ commands without
ack_en, as ESP-IDF v4.4 builds them ini2c_master_read_static(onlyack_valandop_code):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
machine.I2C0): https://github.com/0hJonny/tinygo-lilygo-badge/blob/9d0f8a8087b0e794b5b2957db564c27ae2a55a95/cmd/i2cbug/main.goI can run tests or patches on ESP32-S3 hardware.