From 5285050ffde7824334a0f136dda5c9dbec6ddc36 Mon Sep 17 00:00:00 2001 From: strasharo <3337997+strasharo@users.noreply.github.com> Date: Sun, 9 Aug 2026 21:26:23 +0300 Subject: [PATCH] Fix SH1106 OLED address detection on T-Beam Supreme sub-variants The T-Beam S3 Supreme ships with a QMC6310N magnetometer whose configurable I2C address collides with the OLED's: depending on the board sub-variant, the display sits at either 0x3C or 0x3D, with the magnetometer occupying the other one. SH1106Display::begin() only ever tried the single address from DISPLAY_ADDRESS, so boards where the build-time default doesn't match their wiring get a silently blank screen even though the panel and bus are both fine. begin() now probes both 0x3C and 0x3D and disambiguates by reading the magnetometer's chip-ID register (0x00 returns 0x80 on a QMC6310N) - the same technique LilyGo's own factory test firmware uses - instead of picking whichever address happens to ACK first. Fixes #3147 --- src/helpers/ui/SH1106Display.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/helpers/ui/SH1106Display.cpp b/src/helpers/ui/SH1106Display.cpp index c3840c02af..30f5ecf88b 100644 --- a/src/helpers/ui/SH1106Display.cpp +++ b/src/helpers/ui/SH1106Display.cpp @@ -23,9 +23,25 @@ ColorVal UIColor::corp_blue = SH110X_WHITE; bool SH1106Display::begin() { // Wire must already be initialised by board.begin() before this is called. - // Boards with non-standard SH1106 addresses should define DISPLAY_ADDRESS - // in their variant/platformio configuration. - return i2c_probe(Wire, DISPLAY_ADDRESS) && display.begin(DISPLAY_ADDRESS, true); + // Some boards (e.g. T-Beam S3 Supreme) ship a magnetometer whose configurable + // I2C address collides with the OLED's, at either 0x3C or 0x3D depending on + // hardware revision. Probe both candidates and disambiguate via the + // magnetometer's chip-ID register: a read of reg 0x00 returns 0x80 on a + // QMC6310N. Same trick LilyGo's factory firmware uses. (from PR #2591) + uint8_t candidates[] = { DISPLAY_ADDRESS, (DISPLAY_ADDRESS == 0x3C) ? (uint8_t)0x3D : (uint8_t)0x3C }; + uint8_t found = 0; + for (uint8_t addr : candidates) { + if (!i2c_probe(Wire, addr)) continue; + Wire.beginTransmission(addr); + Wire.write((uint8_t)0x00); + if (Wire.endTransmission() != 0) continue; + if (Wire.requestFrom((int)addr, (int)1) != 1) continue; + if (Wire.read() == 0x80) continue; // magnetometer, not the OLED + found = addr; + break; + } + if (found == 0) return false; + return display.begin(found, true); } void SH1106Display::turnOn()