Skip to content

Fix blank OLED on T-Beam Supreme V3 (I2C bus init + 0x3D address)#2815

Merged
liamcottle merged 2 commits into
meshcore-dev:devfrom
UltimateCodeWarrior:fix/tbeam-supreme-v3-oled-i2c
Jul 21, 2026
Merged

Fix blank OLED on T-Beam Supreme V3 (I2C bus init + 0x3D address)#2815
liamcottle merged 2 commits into
meshcore-dev:devfrom
UltimateCodeWarrior:fix/tbeam-supreme-v3-oled-i2c

Conversation

@UltimateCodeWarrior

Copy link
Copy Markdown

Summary

Fixes the blank OLED on the LilyGo T-Beam Supreme V3 (ESP32-S3 + SX1262 + SH1106). Closes #2609 (see also #2591).

There are two independent bugs on this board, and both are needed for the display to work:

  1. The OLED I2C bus (Wire) is never initialized. On the Supreme the PMU/RTC are on Wire1 (GPIO 42/41, brought up by XPowersLib), while the OLED + BME280 + magnetometer are on the primary Wire bus (GPIO 17/18). Nothing ever calls Wire.begin() for it, so Adafruit_SH110X ends up on the wrong (default) pins.
  2. The OLED is at I2C address 0x3D, not 0x3C. On this board 0x3C is occupied by the QMC6310 magnetometer, so the panel was moved to 0x3D (per LilyGo's hardware docs). SH1106Display::begin() only ever tried DISPLAY_ADDRESS (0x3C).

Changes

  • src/helpers/esp32/TBeamBoard.cpp — initialize Wire on PIN_BOARD_SDA/PIN_BOARD_SCL for TBEAM_SUPREME_SX1262 so the OLED bus is actually up before display.begin().
  • src/helpers/ui/SH1106Display.cpp — probe 0x3D first (only the OLED uses it) and fall back to 0x3C. This avoids the false-positive where 0x3C ACKs because of the magnetometer.

Both changes are backwards-compatible: boards with the OLED at the standard 0x3C and nothing at 0x3D still resolve to 0x3C.

Evidence

Boot-time I2C scan on the affected board (Wire, GPIO 17/18):

I2C scan -> 0x3C 0x3D 0x77
  • 0x3C = QMC6310 magnetometer
  • 0x3D = SH1106 OLED
  • 0x77 = BME280

After this change the display initializes at 0x3D and renders normally.

Test plan

  • T-Beam Supreme V3 (T_Beam_S3_Supreme_SX1262_repeater): OLED renders boot logo + home screen.
  • Regression check on a board with the OLED at 0x3C (e.g. standard T-Beam) to confirm the fallback path still initializes the display.

The SH1106 OLED on the LilyGo T-Beam Supreme V3 never lights up because:

1. The primary I2C bus (Wire) that the OLED, BME280 and magnetometer share
   is never initialized. The PMU/RTC use Wire1 (via XPowersLib); nothing
   brings up Wire on PIN_BOARD_SDA/PIN_BOARD_SCL, so the display driver
   talks on the wrong default pins.
2. The OLED is at I2C address 0x3D on this board (0x3C is taken by the
   QMC6310 magnetometer), but SH1106Display::begin() only tried 0x3C.

Initialize Wire for TBEAM_SUPREME_SX1262, and have SH1106Display::begin()
prefer 0x3D (only ever used by the OLED) before falling back to 0x3C.
Backwards-compatible for boards with the OLED at 0x3C.

Closes meshcore-dev#2609
@UltimateCodeWarrior
UltimateCodeWarrior force-pushed the fix/tbeam-supreme-v3-oled-i2c branch from 10614ac to b8b6598 Compare June 23, 2026 06:39
@UltimateCodeWarrior
UltimateCodeWarrior changed the base branch from main to dev June 23, 2026 06:39
romeomont

This comment was marked as duplicate.

@romeomont romeomont left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on my T-Beam Supreme V3 and the OLED now works correctly. LGTM, good to merge.

@romeomont

Copy link
Copy Markdown

Re: the outstanding regression-check item — I only had a T-Beam Supreme on hand (the 0x3D board, already covered by the checked test-plan item), not a 0x3C board, so I did a code-level review of the fallback path instead of a hardware run:

uint8_t addr = 0;
if (i2c_probe(Wire, 0x3D)) {
  addr = 0x3D;
} else if (i2c_probe(Wire, DISPLAY_ADDRESS)) {
  addr = DISPLAY_ADDRESS;
}
return addr && display.begin(addr, true);

On a standard board with the OLED at 0x3C and nothing at 0x3D:

  1. i2c_probe(Wire, 0x3D) NACKs (no device there) → endTransmission() returns nonzero → probe returns false, addr stays 0.
  2. Falls through to i2c_probe(Wire, DISPLAY_ADDRESS) (0x3C) → OLED ACKs → addr = 0x3C.
  3. display.begin(0x3C, true) is called — identical to pre-PR behavior.

Probing an absent address is a standard non-destructive I2C operation, and Wire init is unchanged for non-Supreme boards (the new Wire.begin() call in TBeamBoard.cpp is gated to TBEAM_SUPREME_SX1262 only), so the precondition for SH1106Display::begin() is the same as before this PR.

Logic checks out for the 0x3C fallback case. I don't currently have a plain 0x3C board (standard T-Beam / T-Beam 1W / Station G2 / etc.) to physically confirm, so this is a code review, not a hardware-verified test run — flagging that distinction for whoever merges.

@IoTThinks

Copy link
Copy Markdown
Contributor

No idea if it is better to define "DISPLAY_ADDRESS" in platformio.ini for T-Beam Supreme V3.
Then in the code ifdef DISPLAY_ADDRESS then use the defined address.

Keep SH1106Display generic by relying on DISPLAY_ADDRESS while the T-Beam Supreme variant declares its 0x3D OLED address.
@UltimateCodeWarrior

Copy link
Copy Markdown
Author

Good suggestion. I pushed a follow-up commit (0de2b4fc) that moves the T-Beam Supreme V3 OLED address into the variant config with DISPLAY_ADDRESS=0x3D, and keeps SH1106Display generic by using the configured DISPLAY_ADDRESS instead of hard-coding a Supreme-specific 0x3D probe in the shared display driver.

@liamcottle
liamcottle merged commit 7124fa1 into meshcore-dev:dev Jul 21, 2026
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.

T-Beam Supreme V3 OLED Display is not showing information

5 participants