Repository navigation
ESP32 I2C: decide TwoWire::end() in one place; recover a controller left bus busy - #368
Merged
Merged
Conversation
…ion.h Whether lgfx may call TwoWire::end() (released ports, the temporary pin switcher) was decided in three places from ARDUINO_ESP32_GIT_VER, which comes from core_version.h. Board packages that do not ship that header left it undefined, so the port was handed back with its pins as GPIO while the Arduino driver stayed installed; the next init found the driver open and the bus stayed unusable. The decision is now one macro, LGFX_TWOWIRE_HAS_END. With core_version.h it is the previous rule (any version but 2.0.0, which has no end()). Without it, esp_arduino_version.h decides (>= 2.0.1). The git version stays first because 2.0.1 and 2.0.2 still report 2.0.0 there. This also covers 2.0.1 to 2.0.4 without core_version.h, where WIRE_HAS_END is not yet defined. For every official release the result is unchanged. The unused i2c_context_t::setPins(), which carried the third copy of the rule, is removed.
A device can leave the controller waiting for a STOP that never comes: bus_busy stays set and TIME_OUT is raised while nothing is transferred. beginTransaction() only waited 128 us for busy to clear and then started the address phase, which failed at the first wait, and every following transfer failed the same way. The ESP32 has no FSM reset bit, so nothing else cleared it. If the port is still busy after that wait, i2c_stop() now recovers the bus and the controller first, as the ESP-IDF driver does before it starts a command. It runs after save_reg(), so load_reg() at the end of the transfer still restores the registers shared with the platform driver. The C2 and C3 skip the peripheral reset in i2c_stop() and rely on the fsm_rst that follows.
This was referenced Oct 20, 2026
Merged
lovyan03
pushed a commit
that referenced
this pull request
Oct 20, 2026
…eft bus busy Port of the develop change (#368) to the master layout: the TwoWire::end() guard becomes LGFX_TWOWIRE_HAS_END (git version first, esp_arduino_version.h when core_version.h is missing; #366) for release() and the pin search, and beginTransaction() runs i2c_stop() when the bus is still busy after the 128 us wait (#362). The unused member setPins() that develop removed is left as it is.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two fixes in the ESP32 I2C layer (
src/lgfx/v1/platforms/esp32/common.inl), synced from the shared lgfx source tree.1. Decide
TwoWire::end()in one place, also withoutcore_version.h(fixes #366)Some board packages, such as M5Stack 2.1.4 (
m5stack:esp32, core 2.0.14 based), do not shipcore_version.h, soARDUINO_ESP32_GIT_VERis undefined.i2c::release()then skippedWire.end()while moving the pins back to GPIO, and the nextinit()found the Arduino driver still installed: the bus stayed unusable. WithWire.hincluded,M5.begin()could not find the built-in IMU (thanks @niguragu for the report, the analysis and the first fix).Whether lgfx calls
TwoWire::end()(inrelease()and in the temporary pin switcher) is now decided in one place:core_version.h: the previous rule (every version but 2.0.0, which has noend()), unchanged for every official arduino-esp32 releaseesp_arduino_version.h, 2.0.1 or later2. Recover a controller left bus busy before a transfer (fixes #362)
Proposed by @GOB52 in #362. A device such as the Unit UltraSonic I2C (RCWL9620) can leave the ESP32 controller waiting for a STOP during its measurement:
bus_busystays set andTIME_OUTis raised.beginTransaction()waited 128 µs and then started the address phase, which failed, and so did every following transfer. If the port is still busy after that wait,i2c_stop()now recovers the bus and the controller first, as the ESP-IDF driver does. It runs aftersave_reg(), so the registers shared with the platform driver are still restored at the end of the transfer.Testing
On an M5Stack Gray (ESP32-D0WDQ6).
Wire.hincluded: built-in IMUM5.In_I2C.release()→begin()10 times, then scan the IMU0x01, wait 150 ms, read 3 bytesTIME_OUTafter the wait every time)release()→begin()10 timesWith the UltraSonic unit, some reads still fail at the second byte (
connection_lost; 68 to 71 of 100 succeed, against 93 of 100 withWire). The rate is the same when the recovery uses only the peripheral reset, so it is not caused by this change; it will be looked at separately.The builds for the upstream CI versions pass locally (46 environments), and the fork CI passed.