Skip to content

Calling setBrightness() reverses invertDisplay() #426

@jasonacox

Description

@jasonacox

Describe the bug

The setContrast() and setBrightness() functions essentially always call normalDisplay(). This means that if you are using invertDisplay() (e.g. black on white background), a call to adjust contrast or brightness will reverse the display (back to white on black background). You can see the sendCommand(NORMALDISPLAY) as part of the contrast function here:

void OLEDDisplay::invertDisplay(void) {
sendCommand(INVERTDISPLAY);
}
void OLEDDisplay::normalDisplay(void) {
sendCommand(NORMALDISPLAY);
}
void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect) {
sendCommand(SETPRECHARGE); //0xD9
sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F
sendCommand(SETCONTRAST);
sendCommand(contrast); // 0-255
sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast)
sendCommand(comdetect); //0x40 default, to lower the contrast, put 0
sendCommand(DISPLAYALLON_RESUME);
sendCommand(NORMALDISPLAY);
sendCommand(DISPLAYON);
}

To Reproduce

This generally works but you will see a flicker as it toggles between normal and inverted mode, especially if this is part of a loop:

#include "SSD1306Wire.h"

SSD1306Wire display(0x3c, D3, D4);

display.setBrightness(100);
display.invertDisplay();

Expected behavior

The expectation is that the setBrightness() call would adjust the inverted Display's brightness level rather than reverting it to normal display.

Workaround

Bypassing the private methods, you can reproduce setContrast() without the call to set NORMALDISPLAY:

#define private public
#include "SSD1306Wire.h"
#undef private

int brightness = 30;
display.sendCommand(SETPRECHARGE);  //0xD9
display.sendCommand(241);           //0xF1 default, to lower the contrast, put 1-1F
display.sendCommand(SETCONTRAST);
display.sendCommand(brightness * 1.171);     // 0-255
display.sendCommand(SETVCOMDETECT);  //0xDB, (additionally needed to lower the contrast)
display.sendCommand(brightness / 8);         //0x40 default, to lower the contrast, put 0
display.sendCommand(DISPLAYALLON_RESUME);
// sendCommand(NORMALDISPLAY);
display.sendCommand(DISPLAYON);

Proposed Solution

I would submit a PR to remove the sendCommand(NORMALDISPLAY) but while an edge case, it could be a breaking change for anyone how coded their projects to use setBrightness() or setContrast() to switch the display to "normal" mode.

I'll submit a PR for an alternative approach to preserve the API by adding a boolean true defaulted parameter to setContrast() and setBrightness() so that a person could override the call to NORMALDISPLAY - e.g.:

class OLEDDisplay {
public:
  void setContrast(
    uint8_t contrast,
    uint8_t precharge,
    uint8_t comdetect,
    bool normalDisplay = true   //  optional, defaults to true
  );
};
void OLEDDisplay::setContrast(
  uint8_t contrast,
  uint8_t precharge,
  uint8_t comdetect,
  bool normalDisplay
) {
  sendCommand(SETPRECHARGE);      // 0xD9
  sendCommand(precharge);         // 0xF1 default, to lower contrast put 0x1–0x1F
  sendCommand(SETCONTRAST);
  sendCommand(contrast);          // 0–255
  sendCommand(SETVCOMDETECT);     // 0xDB
  sendCommand(comdetect);         // 0x40 default
  sendCommand(DISPLAYALLON_RESUME);
  if (normalDisplay) {
    sendCommand(NORMALDISPLAY);
  }
  sendCommand(DISPLAYON);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions