Skip to content

Add Wire.setWireTimeout to avoid I2C bus hangs (closes #288) - #303

Merged
ladyada merged 1 commit into
adafruit:masterfrom
adityachilka1:fix/wire-timeout
May 22, 2026
Merged

Add Wire.setWireTimeout to avoid I2C bus hangs (closes #288)#303
ladyada merged 1 commit into
adafruit:masterfrom
adityachilka1:fix/wire-timeout

Conversation

@adityachilka1

Copy link
Copy Markdown

Closes #288.

Scope of the change

Adafruit_SSD1306::begin() (I2C path) now follows the wire->begin() call with wire->setWireTimeout(25000, true) when the compiler can see that the Wire library exposes the timeout API (WIRE_HAS_TIMEOUT defined).

That's the only change — three lines of code plus a short comment, all behind a #ifdef WIRE_HAS_TIMEOUT guard.

Why

The Arduino AVR Wire library historically had no timeout: a noisy or disconnected I2C bus would put TWI into an indefinite loop and lock the entire sketch. This was tracked in ArduinoCore-avr#42 and fixed by ArduinoCore-avr@deea929, which added setWireTimeout(uint32_t us, bool reset_with_timeout) and the WIRE_HAS_TIMEOUT feature macro.

The Adafruit_SSD1306 library never adopted the new API, so users on AVR cores still get hangs on SDA/SCL noise or display disconnect. Issue #288 asked for that.

Limitations

  • Only the I2C begin() path is touched. SPI is unchanged.
  • The timeout is set only when periphBegin is true — i.e. when this library performed wire->begin() itself. If the caller already called Wire.begin() (the periphBegin=false path), their Wire configuration is left untouched on the assumption they're managing it explicitly.
  • Cores that don't define WIRE_HAS_TIMEOUT (older AVR cores, some non-AVR cores) compile out the entire block — no behavioural change there.

Tests run locally

  • python3 ci/run-clang-format.py --clang-format-executable clang-format -e 'ci/*' -e 'bin/*' Adafruit_SSD1306.cpp → clean
  • Full platform matrix deferred to CI (Arduino Library CI workflow)

Maintainer signal

Per @ladyada's comment on #288: "ok can you submit a simple PR that passes CI?" — happy to iterate if anything here needs adjusting.

If the I2C bus stops responding mid-transaction (noise, the display
becomes disconnected, etc.), the underlying Wire library spins in
an indefinite loop and the host MCU effectively hangs. The Arduino
AVR core fixed this in ArduinoCore-avr@deea929 by exposing
setWireTimeout(); cores that implement it define WIRE_HAS_TIMEOUT
so callers can detect the feature at compile time.

Use that detection: when periphBegin owns the wire->begin() call,
follow it with a 25 ms timeout + reset on timeout. We only touch
Wire's configuration when we performed the begin() ourselves; if
the caller managed Wire externally, leave their configuration
untouched.

No behavioural change on cores without WIRE_HAS_TIMEOUT (the entire
block is compiled out).
@ladyada
ladyada merged commit bb99982 into adafruit:master May 22, 2026
1 check passed
@ladyada

ladyada commented May 22, 2026

Copy link
Copy Markdown
Member

ok! if it causes compilation issues in the future we may comment it out fyi

@adityachilka1

Copy link
Copy Markdown
Author

Thanks for the merge!

Re: the future-compile-issue note — the #ifdef WIRE_HAS_TIMEOUT guard around the call is doing exactly that defensive work. Cores that don't advertise WIRE_HAS_TIMEOUT compile out the whole block, so as new cores show up the block stays inert unless they opt in. If a future core defines the macro but ships a different signature (or different default behaviour), commenting it out is a safe single-line revert.

Happy to send a follow-up if you ever want the timeout value made configurable instead of hard-coded to 25 ms.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider using Wire.setWireTimeout feature

3 participants