From 2a3bdf8fe6c8c3ded81faea90ddbed8cdd98a75d Mon Sep 17 00:00:00 2001
From: Unknown <gijs.noorlander@gmail.com>
Date: Sat, 3 Feb 2018 20:42:07 +0100
Subject: [PATCH] Merged lots of PR + fix some memory leaks

Library did some allocs without free and lots of un-initialized variables.

Applied pull requests from esp8266-oled-ssd1306:

squix78/esp8266-oled-ssd1306#169
squix78/esp8266-oled-ssd1306#157
squix78/esp8266-oled-ssd1306#144

And applied the suggestions from issues:
squix78/esp8266-oled-ssd1306#170
squix78/esp8266-oled-ssd1306#161

Support multiple display resolutions

Applied PR from esp8266-oled-ssd1306:
squix78/esp8266-oled-ssd1306#133

Better Brightness / Contrast control

Suggestions taken from this issue:
squix78/esp8266-oled-ssd1306#134

It is now the same version as being used in ESPeasy.
---
 OLEDDisplay.cpp   | 87 +++++++++++++++++++++++++++++------------------
 OLEDDisplay.h     | 33 +++++++++++++-----
 OLEDDisplayUi.cpp | 34 +++++++++---------
 SSD1306Wire.h     | 24 +++++++------
 4 files changed, 108 insertions(+), 70 deletions(-)

diff --git a/OLEDDisplay.cpp b/OLEDDisplay.cpp
index f8fc226..41af7c2 100644
--- a/OLEDDisplay.cpp
+++ b/OLEDDisplay.cpp
@@ -27,24 +27,32 @@
 
 #include "OLEDDisplay.h"
 
+OLEDDisplay::~OLEDDisplay() {
+  end();
+}
+
 bool OLEDDisplay::init() {
   if (!this->connect()) {
     DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n");
     return false;
   }
+  if(this->buffer==NULL) {
   this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
   if(!this->buffer) {
     DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n");
     return false;
   }
+  }
 
   #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+  if(this->buffer_back==NULL) {
   this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
   if(!this->buffer_back) {
     DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n");
     free(this->buffer);
     return false;
   }
+  }
   #endif
 
   sendInitCommands();
@@ -54,10 +62,11 @@ bool OLEDDisplay::init() {
 }
 
 void OLEDDisplay::end() {
-  if (this->buffer) free(this->buffer);
+  if (this->buffer) { free(this->buffer); this->buffer = NULL; }
   #ifdef OLEDDISPLAY_DOUBLE_BUFFER
-  if (this->buffer_back) free(this->buffer_back);
+  if (this->buffer_back) { free(this->buffer_back); this->buffer_back = NULL; }
   #endif
+  if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; }
 }
 
 void OLEDDisplay::resetDisplay(void) {
@@ -73,11 +82,11 @@ void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) {
 }
 
 void OLEDDisplay::setPixel(int16_t x, int16_t y) {
-  if (x >= 0 && x < 128 && y >= 0 && y < 64) {
+  if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) {
     switch (color) {
-      case WHITE:   buffer[x + (y / 8) * DISPLAY_WIDTH] |=  (1 << (y & 7)); break;
-      case BLACK:   buffer[x + (y / 8) * DISPLAY_WIDTH] &= ~(1 << (y & 7)); break;
-      case INVERSE: buffer[x + (y / 8) * DISPLAY_WIDTH] ^=  (1 << (y & 7)); break;
+      case WHITE:   buffer[x + (y / 8) * this->width()] |=  (1 << (y & 7)); break;
+      case BLACK:   buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break;
+      case INVERSE: buffer[x + (y / 8) * this->width()] ^=  (1 << (y & 7)); break;
     }
   }
 }
@@ -222,21 +231,21 @@ void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) {
 }
 
 void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) {
-  if (y < 0 || y >= DISPLAY_HEIGHT) { return; }
+  if (y < 0 || y >= this->height()) { return; }
 
   if (x < 0) {
     length += x;
     x = 0;
   }
 
-  if ( (x + length) > DISPLAY_WIDTH) {
-    length = (DISPLAY_WIDTH - x);
+  if ( (x + length) > this->width()) {
+    length = (this->width() - x);
   }
 
   if (length <= 0) { return; }
 
   uint8_t * bufferPtr = buffer;
-  bufferPtr += (y >> 3) * DISPLAY_WIDTH;
+  bufferPtr += (y >> 3) * this->width();
   bufferPtr += x;
 
   uint8_t drawBit = 1 << (y & 7);
@@ -255,15 +264,15 @@ void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) {
 }
 
 void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
-  if (x < 0 || x >= DISPLAY_WIDTH) return;
+  if (x < 0 || x >= this->width()) return;
 
   if (y < 0) {
     length += y;
     y = 0;
   }
 
-  if ( (y + length) > DISPLAY_HEIGHT) {
-    length = (DISPLAY_HEIGHT - y);
+  if ( (y + length) > this->height()) {
+    length = (this->height() - y);
   }
 
   if (length <= 0) return;
@@ -273,7 +282,7 @@ void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
   uint8_t drawBit;
   uint8_t *bufferPtr = buffer;
 
-  bufferPtr += (y >> 3) * DISPLAY_WIDTH;
+  bufferPtr += (y >> 3) * this->width();
   bufferPtr += x;
 
   if (yOffset) {
@@ -293,7 +302,7 @@ void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
     if (length < yOffset) return;
 
     length -= yOffset;
-    bufferPtr += DISPLAY_WIDTH;
+    bufferPtr += this->width();
   }
 
   if (length >= 8) {
@@ -303,14 +312,14 @@ void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
         drawBit = (color == WHITE) ? 0xFF : 0x00;
         do {
           *bufferPtr = drawBit;
-          bufferPtr += DISPLAY_WIDTH;
+          bufferPtr += this->width();
           length -= 8;
         } while (length >= 8);
         break;
       case INVERSE:
         do {
           *bufferPtr = ~(*bufferPtr);
-          bufferPtr += DISPLAY_WIDTH;
+          bufferPtr += this->width();
           length -= 8;
         } while (length >= 8);
         break;
@@ -353,7 +362,7 @@ void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int
 
 void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *xbm) {
   int16_t widthInXbm = (width + 7) / 8;
-  uint8_t data;
+  uint8_t data = 0;
 
   for(int16_t y = 0; y < height; y++) {
     for(int16_t x = 0; x < width; x++ ) {
@@ -388,11 +397,13 @@ void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, u
     case TEXT_ALIGN_RIGHT:
       xMove -= textWidth;
       break;
+    case TEXT_ALIGN_LEFT:
+      break;
   }
 
   // Don't draw anything if it is not on the screen.
-  if (xMove + textWidth  < 0 || xMove > DISPLAY_WIDTH ) {return;}
-  if (yMove + textHeight < 0 || yMove > DISPLAY_HEIGHT) {return;}
+  if (xMove + textWidth  < 0 || xMove > this->width() ) {return;}
+  if (yMove + textHeight < 0 || yMove > this->width() ) {return;}
 
   for (uint16_t j = 0; j < textLength; j++) {
     int16_t xPos = xMove + cursorX;
@@ -545,9 +556,16 @@ void OLEDDisplay::normalDisplay(void) {
   sendCommand(NORMALDISPLAY);
 }
 
-void OLEDDisplay::setContrast(char contrast) {
+void OLEDDisplay::setContrast(char contrast, char precharge, char comdetect) {
+  sendCommand(SETPRECHARGE); //0xD9
+  sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F
   sendCommand(SETCONTRAST);
-  sendCommand(contrast);
+  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);
 }
 
 void OLEDDisplay::flipScreenVertically() {
@@ -596,6 +614,7 @@ bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){
   uint16_t size = lines * chars;
   if (size > 0) {
     this->logBufferLine     = 0;      // Lines printed
+    this->logBufferFilled   = 0;      // Nothing stored yet
     this->logBufferMaxLines = lines;  // Lines max printable
     this->logBufferSize     = size;   // Total number of characters the buffer can hold
     this->logBuffer         = (char *) malloc(size * sizeof(uint8_t));
@@ -670,7 +689,7 @@ void OLEDDisplay::sendInitCommands(void) {
   sendCommand(SETDISPLAYCLOCKDIV);
   sendCommand(0xF0); // Increase speed of the display max ~96Hz
   sendCommand(SETMULTIPLEX);
-  sendCommand(0x3F);
+  sendCommand(this->height() - 1);
   sendCommand(SETDISPLAYOFFSET);
   sendCommand(0x00);
   sendCommand(SETSTARTLINE);
@@ -681,11 +700,13 @@ void OLEDDisplay::sendInitCommands(void) {
   sendCommand(SEGREMAP);
   sendCommand(COMSCANINC);
   sendCommand(SETCOMPINS);
-  sendCommand(0x12);
+  sendCommand(0x12); // according to the adafruit lib, sometimes this may need to be 0x02
   sendCommand(SETCONTRAST);
   sendCommand(0xCF);
   sendCommand(SETPRECHARGE);
   sendCommand(0xF1);
+  sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast)
+  sendCommand(0x40);	        //0x40 default, to lower the contrast, put 0
   sendCommand(DISPLAYALLON_RESUME);
   sendCommand(NORMALDISPLAY);
   sendCommand(0x2e);            // stop scroll
@@ -694,8 +715,8 @@ void OLEDDisplay::sendInitCommands(void) {
 
 void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) {
   if (width < 0 || height < 0) return;
-  if (yMove + height < 0 || yMove > DISPLAY_HEIGHT)  return;
-  if (xMove + width  < 0 || xMove > DISPLAY_WIDTH)   return;
+  if (yMove + height < 0 || yMove > this->height())  return;
+  if (xMove + width  < 0 || xMove > this->width())   return;
 
   uint8_t  rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0)
   int8_t   yOffset      = yMove & 7;
@@ -717,13 +738,13 @@ void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t widt
     byte currentByte = pgm_read_byte(data + offset + i);
 
     int16_t xPos = xMove + (i / rasterHeight);
-    int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * DISPLAY_WIDTH;
+    int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width();
 
-    int16_t yScreenPos = yMove + yOffset;
+//    int16_t yScreenPos = yMove + yOffset;
     int16_t dataPos    = xPos  + yPos;
 
     if (dataPos >=  0  && dataPos < DISPLAY_BUFFER_SIZE &&
-        xPos    >=  0  && xPos    < DISPLAY_WIDTH ) {
+        xPos    >=  0  && xPos    < this->width() ) {
 
       if (yOffset >= 0) {
         switch (this->color) {
@@ -731,11 +752,11 @@ void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t widt
           case BLACK:   buffer[dataPos] &= ~(currentByte << yOffset); break;
           case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break;
         }
-        if (dataPos < (DISPLAY_BUFFER_SIZE - DISPLAY_WIDTH)) {
+        if (dataPos < (DISPLAY_BUFFER_SIZE - this->width())) {
           switch (this->color) {
-            case WHITE:   buffer[dataPos + DISPLAY_WIDTH] |= currentByte >> (8 - yOffset); break;
-            case BLACK:   buffer[dataPos + DISPLAY_WIDTH] &= ~(currentByte >> (8 - yOffset)); break;
-            case INVERSE: buffer[dataPos + DISPLAY_WIDTH] ^= currentByte >> (8 - yOffset); break;
+            case WHITE:   buffer[dataPos + this->width()] |= currentByte >> (8 - yOffset); break;
+            case BLACK:   buffer[dataPos + this->width()] &= ~(currentByte >> (8 - yOffset)); break;
+            case INVERSE: buffer[dataPos + this->width()] ^= currentByte >> (8 - yOffset); break;
           }
         }
       } else {
diff --git a/OLEDDisplay.h b/OLEDDisplay.h
index 81537a2..acd18ea 100644
--- a/OLEDDisplay.h
+++ b/OLEDDisplay.h
@@ -44,9 +44,13 @@
 
 
 // Display settings
-#define DISPLAY_WIDTH 128
-#define DISPLAY_HEIGHT 64
-#define DISPLAY_BUFFER_SIZE 1024
+#ifndef DISPLAY_WIDTH
+  #define DISPLAY_WIDTH 128
+#endif
+#ifndef DISPLAY_HEIGHT
+  #define DISPLAY_HEIGHT 64
+#endif
+#define DISPLAY_BUFFER_SIZE DISPLAY_WIDTH * DISPLAY_HEIGHT / 8
 
 // Header Values
 #define JUMPTABLE_BYTES 4
@@ -109,7 +113,16 @@ enum OLEDDISPLAY_TEXT_ALIGNMENT {
 
 
 class OLEDDisplay : public Print {
+  private:
+    const int _width, _height;
+
   public:
+    OLEDDisplay(const int width = DISPLAY_WIDTH, const int height = DISPLAY_HEIGHT) : _width(width), _height(height){ };
+    virtual ~OLEDDisplay();
+
+    const int width(void) const { return _width; };
+    const int height(void) const { return _height; };
+
     // Initialize the display
     bool init();
 
@@ -150,7 +163,7 @@ class OLEDDisplay : public Print {
     // Draw a lin vertically
     void drawVerticalLine(int16_t x, int16_t y, int16_t length);
 
-    // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is 
+    // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is
     // a unsigned byte value between 0 and 100
     void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
 
@@ -201,7 +214,9 @@ class OLEDDisplay : public Print {
     void normalDisplay(void);
 
     // Set display contrast
-    void setContrast(char contrast);
+    // really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
+    // normal brightness & contrast:  contrast = 100
+    void setContrast(char contrast, char precharge = 241, char comdetect = 64);
 
     // Turn the display upside down
     void flipScreenVertically();
@@ -226,10 +241,10 @@ class OLEDDisplay : public Print {
     size_t write(uint8_t c);
     size_t write(const char* s);
 
-    uint8_t            *buffer;
+    uint8_t            *buffer = NULL;
 
     #ifdef OLEDDISPLAY_DOUBLE_BUFFER
-    uint8_t            *buffer_back;
+    uint8_t            *buffer_back = NULL;
     #endif
 
   protected:
@@ -247,10 +262,10 @@ class OLEDDisplay : public Print {
     char      *logBuffer                       = NULL;
 
     // Send a command to the display (low level function)
-    virtual void sendCommand(uint8_t com) {};
+    virtual void sendCommand(uint8_t com) {(void)com;};
 
     // Connect to the display
-    virtual bool connect() {};
+    virtual bool connect() { return false; };
 
     // Send all the init commands
     void sendInitCommands();
diff --git a/OLEDDisplayUi.cpp b/OLEDDisplayUi.cpp
index 3fb4326..976bbac 100644
--- a/OLEDDisplayUi.cpp
+++ b/OLEDDisplayUi.cpp
@@ -251,31 +251,31 @@ void OLEDDisplayUi::drawFrame(){
   switch (this->state.frameState){
      case IN_TRANSITION: {
        float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition;
-       int16_t x, y, x1, y1;
+       int16_t x = 0, y = 0, x1 = 0, y1 = 0; 
        switch(this->frameAnimationDirection){
         case SLIDE_LEFT:
-          x = -128 * progress;
+          x = -this->display->width() * progress;
           y = 0;
-          x1 = x + 128;
+          x1 = x + this->display->width();
           y1 = 0;
           break;
         case SLIDE_RIGHT:
-          x = 128 * progress;
+          x = this->display->width() * progress;
           y = 0;
-          x1 = x - 128;
+          x1 = x - this->display->width();
           y1 = 0;
           break;
         case SLIDE_UP:
           x = 0;
-          y = -64 * progress;
+          y = -this->display->height() * progress;
           x1 = 0;
-          y1 = y + 64;
+          y1 = y + this->display->height();
           break;
         case SLIDE_DOWN:
           x = 0;
-          y = 64 * progress;
+          y = this->display->height() * progress;
           x1 = 0;
-          y1 = y - 64;
+          y1 = y - this->display->height();
           break;
        }
 
@@ -331,7 +331,7 @@ void OLEDDisplayUi::drawIndicator() {
       return;
     }
 
-    uint8_t posOfHighlightFrame;
+    uint8_t posOfHighlightFrame = 0;
     float indicatorFadeProgress = 0;
 
     // if the indicator needs to be slided in we want to
@@ -362,25 +362,25 @@ void OLEDDisplayUi::drawIndicator() {
 
     uint16_t frameStartPos = (12 * frameCount / 2);
     const char *image;
-    uint16_t x,y;
+    uint16_t x = 0, y = 0;
     for (byte i = 0; i < this->frameCount; i++) {
 
       switch (this->indicatorPosition){
         case TOP:
           y = 0 - (8 * indicatorFadeProgress);
-          x = 64 - frameStartPos + 12 * i;
+          x = (this->display->width() / 2) - frameStartPos + 12 * i;
           break;
         case BOTTOM:
-          y = 56 + (8 * indicatorFadeProgress);
-          x = 64 - frameStartPos + 12 * i;
+          y = (this->display->height() - 8) + (8 * indicatorFadeProgress);
+          x = (this->display->width() / 2) - frameStartPos + 12 * i;
           break;
         case RIGHT:
-          x = 120 + (8 * indicatorFadeProgress);
-          y = 32 - frameStartPos + 2 + 12 * i;
+          x = (this->display->width() - 8) + (8 * indicatorFadeProgress);
+          y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i;
           break;
         case LEFT:
           x = 0 - (8 * indicatorFadeProgress);
-          y = 32 - frameStartPos + 2 + 12 * i;
+          y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i;
           break;
       }
 
diff --git a/SSD1306Wire.h b/SSD1306Wire.h
index ac12bec..c22b714 100644
--- a/SSD1306Wire.h
+++ b/SSD1306Wire.h
@@ -38,7 +38,8 @@ class SSD1306Wire : public OLEDDisplay {
       uint8_t             _scl;
 
   public:
-    SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+    SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl, int width = DISPLAY_WIDTH, int height = DISPLAY_HEIGHT)
+    : OLEDDisplay(width, height) {
       this->_address = _address;
       this->_sda = _sda;
       this->_scl = _scl;
@@ -53,6 +54,7 @@ class SSD1306Wire : public OLEDDisplay {
     }
 
     void display(void) {
+      const int x_offset = (128 - this->width()) / 2;
       #ifdef OLEDDISPLAY_DOUBLE_BUFFER
         uint8_t minBoundY = ~0;
         uint8_t maxBoundY = 0;
@@ -63,9 +65,9 @@ class SSD1306Wire : public OLEDDisplay {
 
         // Calculate the Y bounding box of changes
         // and copy buffer[pos] to buffer_back[pos];
-        for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
-          for (x = 0; x < DISPLAY_WIDTH; x++) {
-           uint16_t pos = x + y * DISPLAY_WIDTH;
+        for (y = 0; y < (this->height() / 8); y++) {
+          for (x = 0; x < this->width(); x++) {
+           uint16_t pos = x + y * this->width();
            if (buffer[pos] != buffer_back[pos]) {
              minBoundY = _min(minBoundY, y);
              maxBoundY = _max(maxBoundY, y);
@@ -80,11 +82,11 @@ class SSD1306Wire : public OLEDDisplay {
         // If the minBoundY wasn't updated
         // we can savely assume that buffer_back[pos] == buffer[pos]
         // holdes true for all values of pos
-        if (minBoundY == ~0) return;
+         if (minBoundY == (uint8_t)(~0)) return;
 
         sendCommand(COLUMNADDR);
-        sendCommand(minBoundX);
-        sendCommand(maxBoundX);
+        sendCommand(x_offset + minBoundX);
+        sendCommand(x_offset + maxBoundX);
 
         sendCommand(PAGEADDR);
         sendCommand(minBoundY);
@@ -97,7 +99,7 @@ class SSD1306Wire : public OLEDDisplay {
               Wire.beginTransmission(_address);
               Wire.write(0x40);
             }
-            Wire.write(buffer[x + y * DISPLAY_WIDTH]);
+            Wire.write(buffer[x + y * this->width()]);
             k++;
             if (k == 16)  {
               Wire.endTransmission();
@@ -113,12 +115,12 @@ class SSD1306Wire : public OLEDDisplay {
       #else
 
         sendCommand(COLUMNADDR);
-        sendCommand(0x0);
-        sendCommand(0x7F);
+        sendCommand(x_offset);
+        sendCommand(x_offset + (this->width() - 1));
 
         sendCommand(PAGEADDR);
         sendCommand(0x0);
-        sendCommand(0x7);
+        sendCommand((this->height() / 8) - 1);
 
         for (uint16_t i=0; i < DISPLAY_BUFFER_SIZE; i++) {
           Wire.beginTransmission(this->_address);
-- 
GitLab