Skip to content

TS101: fix OLED partial-screen rendering and vertical offset - #2238

Open
Edrig wants to merge 9 commits into
Ralim:devfrom
Edrig:fix/ts101-oled-vertical-offset
Open

TS101: fix OLED partial-screen rendering and vertical offset#2238
Edrig wants to merge 9 commits into
Ralim:devfrom
Edrig:fix/ts101-oled-vertical-offset

Conversation

@Edrig

@Edrig Edrig commented Aug 25, 2026

Copy link
Copy Markdown
Contributor
  • Please check if the PR fulfills these requirements
  • The changes have been tested locally
  • There are no breaking changes known
  • What kind of change does this PR introduce?

Bug fix (TS101 OLED driver).

  • What is the current behaviour?

On stock TS101 (MODEL_TS101), the shared OLED transmission path (I2C_CLASS::Transmit/writeRegistersBulk, a single bulk 0x80-continuation-prefixed I2C transaction) does not reliably drive the full 128x32 SSD1306 panel: the boot splash stays on screen and the menu only renders in a small area at the top-left. Every other model on this driver (TS100, TS80, TS80P, Pinecil, Sequre S60/S60P/T55, ...) is unaffected — this is specific to the TS101's panel/wiring.

  • What is the new behaviour (if this is a feature change)?
  1. Ports the per-byte I2C command path from the community fork IronOS-TS101-FullOLED — sends each command/data chunk as its own I2C transaction instead of one large bulk transfer. Applied to refresh(), setDisplayState(), initialize(), setRotation(), setBrightness(), setInverseDisplay() and maskScrollIndicatorOnOLED().
  2. Adds the missing SSD1306 "Set Display Offset" (0xD3) command with an orientation-dependent value, matching the official Miniware TS101 firmware disassembly (TS101AppV220.hex) — without it the panel can render shifted by half its screen height.
  3. Per review feedback, replaced the raw #ifdef MODEL_TS101 checks in the shared OLED driver with two descriptive flags defined in TS101's own configuration.h: OLED_I2C_PER_BYTE_TRANSFERS (bus can't do bulk transfers) and OLED_DISPLAY_OFFSET_QUIRK (needs the non-zero orientation-dependent offset). These are two independent concerns — another model could hit either issue on its own — and are no longer coupled to a specific MODEL_ macro in the driver itself. No behaviour change for TS101 or any other model.
  • Other information:

Verified end-to-end (EN and FR builds) on two physical TS101 units, on different bootloader/DFU versions (1.06 and 1.08): before this change the screen stayed on the boot splash with the menu confined to the top-left; after, the full 128x32 panel renders correctly in both orientations on both units, with brightness/inverse-display/rotation all working.

Test plan:

  • Builds cleanly for model=TS101 firmware-EN and firmware-FR

  • Builds cleanly for model=TS100 firmware-EN (regression check — new flags not defined, code path unchanged)

  • Confirmed fixes the partial-screen/offset issue on two physical TS101 units (DFU 1.06 and DFU 1.08), both orientations

  • Not tested on other model

  • An LLM (AI) was used for some of this code

  • An LLM was used, and I have manually audited the code. I understand what changes it made and why.

setRotation() only ever sent the segment-remap (0xA0/0xA1) and
COM-scan-direction (0xC0/0xC8) commands, never touching the SSD1306
"Set Display Offset" (0xD3) command that already existed in
OLED_Setup_Array (always left at 0x00).

Disassembling the official Miniware TS101 firmware (TS101AppV220.hex)
shows that after the remap/scan commands it also sends 0xD3 with an
orientation-dependent value (0x10 / 0x30). Without it, the 128x32
panel can render shifted by half its screen height depending on the
physical OLED panel batch. Confirmed on real TS101 hardware.
@Edrig
Edrig marked this pull request as ready for review August 25, 2026 14:06
Confirmed on hardware that stock TS101 OLED handling (the bulk 0x80-continuation
transmission shared by every other model, via I2C_CLASS::Transmit/writeRegistersBulk)
leaves the panel stuck on the boot splash with the menu confined to a small area at
the top-left: the 128x32 SSD1306 on the TS101 only reliably updates when each command/
data chunk is sent as its own I2C transaction.

This ports the working per-byte transmission path (i2c_send_command_byte /
oled_bulk_write) from the community fork IronOS-TS101-FullOLED into refresh(),
setDisplayState(), initialize(), setRotation(), setBrightness(), setInverseDisplay()
and maskScrollIndicatorOnOLED(), gated behind #ifdef MODEL_TS101 so no other model
(TS100/TS80/TS80P/Pinecil/Sequre S60/S60P/T55/...) is affected - they keep using the
existing Transmit()/writeRegistersBulk() path unchanged.

Also narrows the previous commit's segment-remap/offset triplet from
`#ifdef OLED_SEGMENT_MAP_REVERSED` to `#ifdef MODEL_TS101`: that macro is also set by
Sequre's 128x32 models, which would otherwise have received TS101-specific hardware
values they were never verified against.

Verified working end-to-end (EN and FR builds) on physical TS101 hardware.
@Edrig Edrig changed the title TS101: fix vertical image offset on the 128x32 OLED panel TS101: fix OLED partial-screen rendering and vertical offset Aug 25, 2026
@Ralim

Ralim commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Please ensure the PR follows the PR template in the repository. Ping me once your using the template and I'll do a review pass.

Please avoid using MODEL_ #ifdef's in the main drivers where possible and use a descriptive flag instead so that if we have other models with this issue it can be enabled in their configuration files instead.

Edrig and others added 2 commits August 26, 2026 14:11
Per review feedback: gate the per-byte I2C transmission path and the
non-zero orientation-dependent display offset behind two descriptive
flags (OLED_I2C_PER_BYTE_TRANSFERS, OLED_DISPLAY_OFFSET_QUIRK) defined
in TS101's configuration.h, instead of raw MODEL_TS101 checks in the
shared OLED driver. No behaviour change for TS101 or any other model.
@Edrig

Edrig commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Hello,
I've updated the PR based on your comments.
All code changes created with Claude were manually audited.

Comment thread source/Core/Drivers/OLED.hpp Outdated
static void initialize(); // Startup the I2C coms (brings screen out of reset etc)
static bool isInitDone();
// Draw the buffer out to the LCD if any content has changed.
#ifdef OLED_I2C_PER_BYTE_TRANSFERS

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

It would be nicer to pull both of these into the cpp file so that we have have the only #ifdef mask in the cpp file

Comment thread source/Core/Drivers/OLED.cpp Outdated
#ifdef OLED_I2C_PER_BYTE_TRANSFERS
// Sent as individual command-byte writes; the bulk 0x80-continuation path below
// is what leaves this panel stuck on a partial refresh.
i2c_send_command_byte(OLED_Setup_Array[9].val);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Is there a reason for these being sent out of order?

Comment thread source/Core/Drivers/OLED.cpp Outdated
static void i2c_send_bulk(const uint8_t *buf, int len) { I2C_CLASS::Mem_Write(DEVICEADDR_OLED, 0x40, buf, len); }

static void oled_bulk_write(uint32_t posx, uint32_t posy, int sizex, int sizey, const uint8_t *buf) {
uint32_t page = (posy & 7) == 0 ? (posy >> 3) : (posy >> 3) + 1;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This will need to be reformatted to pass the cpp check, ensure you are are running the style fix in the docker image.

Edrig added 2 commits August 27, 2026 10:49
- Move the OLED_I2C_PER_BYTE_TRANSFERS branching for refresh()/setDisplayState()
  out of the header and into OLED.cpp, so the header only has plain
  declarations and the ifdef mask lives in one place.
- Fix setRotation()'s per-byte command order to match OLED_Setup_Array's
  layout order instead of an arbitrary order.
- Run clang-format over both files.
Comment thread source/Core/Drivers/OLED.cpp Outdated
@Ralim

Ralim commented Aug 27, 2026

Copy link
Copy Markdown
Owner

If you could touch up that comment I think the reset is good 🙇🏼 Will merge after that

Edrig added 2 commits August 27, 2026 13:24
The OLED_DISPLAY_OFFSET_QUIRK branch duplicated the #else branch
(both wrote 0xA0 with the same comment), so it can be dropped.
CI flagged the padded spacing on the 0xA0 line; align it with the
adjacent 0x00 line's single-space formatting.
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.

2 participants