diff --git a/OLEDDisplay.cpp b/OLEDDisplay.cpp
index 3a949906e7196135c855fb44a9af923f33e416db..5fc15dbec766c85728e0859b1e73bc3f63726a6f 100644
--- a/OLEDDisplay.cpp
+++ b/OLEDDisplay.cpp
@@ -32,14 +32,14 @@ bool OLEDDisplay::init() {
     DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n");
     return false;
   }
-  this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
+  this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize);
   if(!this->buffer) {
     DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n");
     return false;
   }
 
   #ifdef OLEDDISPLAY_DOUBLE_BUFFER
-  this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
+  this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize);
   if(!this->buffer_back) {
     DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n");
     free(this->buffer);
@@ -63,7 +63,7 @@ void OLEDDisplay::end() {
 void OLEDDisplay::resetDisplay(void) {
   clear();
   #ifdef OLEDDISPLAY_DOUBLE_BUFFER
-  memset(buffer_back, 1, DISPLAY_BUFFER_SIZE);
+  memset(buffer_back, 1, displayBufferSize);
   #endif
   display();
 }
@@ -72,12 +72,17 @@ void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) {
   this->color = color;
 }
 
+OLEDDISPLAY_COLOR OLEDDisplay::getColor() {
+  return this->color;
+}
+
 void OLEDDisplay::setPixel(int16_t x, int16_t y) {
-  if (x >= 0 && x < 128 && y >= 0 && y < 64) {
+  if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight) {
+
     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) * displayWidth] |=  (1 << (y & 7)); break;
+      case BLACK:   buffer[x + (y / 8) * displayWidth] &= ~(1 << (y & 7)); break;
+      case INVERSE: buffer[x + (y / 8) * displayWidth] ^=  (1 << (y & 7)); break;
     }
   }
 }
@@ -222,21 +227,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 >= displayHeight) { return; }
 
   if (x < 0) {
     length += x;
     x = 0;
   }
 
-  if ( (x + length) > DISPLAY_WIDTH) {
-    length = (DISPLAY_WIDTH - x);
+  if ( (x + length) > displayWidth) {
+    length = (displayWidth - x);
   }
 
   if (length <= 0) { return; }
 
   uint8_t * bufferPtr = buffer;
-  bufferPtr += (y >> 3) * DISPLAY_WIDTH;
+  bufferPtr += (y >> 3) * displayWidth;
   bufferPtr += x;
 
   uint8_t drawBit = 1 << (y & 7);
@@ -255,15 +260,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 > displayWidth) return;
 
   if (y < 0) {
     length += y;
     y = 0;
   }
 
-  if ( (y + length) > DISPLAY_HEIGHT) {
-    length = (DISPLAY_HEIGHT - y);
+  if ( (y + length) > displayHeight) {
+    length = (displayHeight - y);
   }
 
   if (length <= 0) return;
@@ -273,7 +278,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) * displayWidth;
   bufferPtr += x;
 
   if (yOffset) {
@@ -293,7 +298,7 @@ void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
     if (length < yOffset) return;
 
     length -= yOffset;
-    bufferPtr += DISPLAY_WIDTH;
+    bufferPtr += displayWidth;
   }
 
   if (length >= 8) {
@@ -303,14 +308,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 += displayWidth;
           length -= 8;
         } while (length >= 8);
         break;
       case INVERSE:
         do {
           *bufferPtr = ~(*bufferPtr);
-          bufferPtr += DISPLAY_WIDTH;
+          bufferPtr += displayWidth;
           length -= 8;
         } while (length >= 8);
         break;
@@ -347,11 +352,11 @@ void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16
   fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius);
 }
 
-void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *image) {
+void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *image) {
   drawInternal(xMove, yMove, width, height, image, 0, 0);
 }
 
-void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *xbm) {
+void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *xbm) {
   int16_t widthInXbm = (width + 7) / 8;
   uint8_t data = 0;
 
@@ -389,12 +394,12 @@ void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, u
       xMove -= textWidth;
       break;
     case TEXT_ALIGN_LEFT:
-        break;
+      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 > displayWidth ) {return;}
+  if (yMove + textHeight < 0 || yMove > displayHeight) {return;}
 
   for (uint16_t j = 0; j < textLength; j++) {
     int16_t xPos = xMove + cursorX;
@@ -527,7 +532,7 @@ void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) {
   this->textAlignment = textAlignment;
 }
 
-void OLEDDisplay::setFont(const char *fontData) {
+void OLEDDisplay::setFont(const uint8_t *fontData) {
   this->fontData = fontData;
 }
 
@@ -552,13 +557,23 @@ void OLEDDisplay::setContrast(char contrast) {
   sendCommand(contrast);
 }
 
+void OLEDDisplay::resetOrientation() {
+  sendCommand(SEGREMAP);
+  sendCommand(COMSCANINC);           //Reset screen rotation or mirroring
+}
+
 void OLEDDisplay::flipScreenVertically() {
   sendCommand(SEGREMAP | 0x01);
   sendCommand(COMSCANDEC);           //Rotate screen 180 Deg
 }
 
+void OLEDDisplay::mirrorScreen() {
+  sendCommand(SEGREMAP);
+  sendCommand(COMSCANDEC);           //Mirror screen
+}
+
 void OLEDDisplay::clear(void) {
-  memset(buffer, 0, DISPLAY_BUFFER_SIZE);
+  memset(buffer, 0, displayBufferSize);
 }
 
 void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
@@ -593,6 +608,14 @@ void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
   }
 }
 
+uint16_t OLEDDisplay::getWidth(void) {
+  return displayWidth;
+}
+
+uint16_t OLEDDisplay::getHeight(void) {
+  return displayHeight;
+}
+
 bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){
   if (logBuffer != NULL) free(logBuffer);
   uint16_t size = lines * chars;
@@ -614,12 +637,17 @@ size_t OLEDDisplay::write(uint8_t c) {
     // Don't waste space on \r\n line endings, dropping \r
     if (c == 13) return 1;
 
+    // convert UTF-8 character to font table index
+    c = (this->fontTableLookupFunction)(c);
+    // drop unknown character
+    if (c == 0) return 1;
+
     bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines;
     bool bufferNotFull = this->logBufferFilled < this->logBufferSize;
 
     // Can we write to the buffer?
     if (bufferNotFull && maxLineNotReached) {
-      this->logBuffer[logBufferFilled] = utf8ascii(c);
+      this->logBuffer[logBufferFilled] = c;
       this->logBufferFilled++;
       // Keep track of lines written
       if (c == 10) this->logBufferLine++;
@@ -672,7 +700,8 @@ void OLEDDisplay::sendInitCommands(void) {
   sendCommand(SETDISPLAYCLOCKDIV);
   sendCommand(0xF0); // Increase speed of the display max ~96Hz
   sendCommand(SETMULTIPLEX);
-  sendCommand(0x3F);
+  //sendCommand(0x3F);
+  sendCommand(displayHeight - 1);
   sendCommand(SETDISPLAYOFFSET);
   sendCommand(0x00);
   sendCommand(SETSTARTLINE);
@@ -683,9 +712,21 @@ void OLEDDisplay::sendInitCommands(void) {
   sendCommand(SEGREMAP);
   sendCommand(COMSCANINC);
   sendCommand(SETCOMPINS);
-  sendCommand(0x12);
+  
+  if (geometry == GEOMETRY_128_64) {
+    sendCommand(0x12);
+  } else if (geometry == GEOMETRY_128_32) {
+    sendCommand(0x02);
+  }
+  
   sendCommand(SETCONTRAST);
-  sendCommand(0xCF);
+  
+  if (geometry == GEOMETRY_128_64) {
+    sendCommand(0xCF);
+  } else if (geometry == GEOMETRY_128_32) {
+    sendCommand(0x8F);
+  }
+  
   sendCommand(SETPRECHARGE);
   sendCommand(0xF1);
   sendCommand(DISPLAYALLON_RESUME);
@@ -694,10 +735,10 @@ void OLEDDisplay::sendInitCommands(void) {
   sendCommand(DISPLAYON);
 }
 
-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) {
+void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *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 > displayHeight)  return;
+  if (xMove + width  < 0 || xMove > displayWidth)   return;
 
   uint8_t  rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0)
   int8_t   yOffset      = yMove & 7;
@@ -719,12 +760,12 @@ 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)) * displayWidth;
 
     int16_t dataPos    = xPos  + yPos;
 
-    if (dataPos >=  0  && dataPos < DISPLAY_BUFFER_SIZE &&
-        xPos    >=  0  && xPos    < DISPLAY_WIDTH ) {
+    if (dataPos >=  0  && dataPos < displayBufferSize &&
+        xPos    >=  0  && xPos    < displayWidth ) {
 
       if (yOffset >= 0) {
         switch (this->color) {
@@ -732,11 +773,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 < (displayBufferSize - displayWidth)) {
           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 + displayWidth] |= currentByte >> (8 - yOffset); break;
+            case BLACK:   buffer[dataPos + displayWidth] &= ~(currentByte >> (8 - yOffset)); break;
+            case INVERSE: buffer[dataPos + displayWidth] ^= currentByte >> (8 - yOffset); break;
           }
         }
       } else {
@@ -761,27 +802,6 @@ void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t widt
   }
 }
 
-// Code form http://playground.arduino.cc/Main/Utf8ascii
-uint8_t OLEDDisplay::utf8ascii(byte ascii) {
-  static uint8_t LASTCHAR;
-
-  if ( ascii < 128 ) { // Standard ASCII-set 0..0x7F handling
-    LASTCHAR = 0;
-    return ascii;
-  }
-
-  uint8_t last = LASTCHAR;   // get last char
-  LASTCHAR = ascii;
-
-  switch (last) {    // conversion depnding on first UTF8-character
-    case 0xC2: return  (ascii);  break;
-    case 0xC3: return  (ascii | 0xC0);  break;
-    case 0x82: if (ascii == 0xAC) return (0x80);    // special case Euro-symbol
-  }
-
-  return  0; // otherwise: return zero, if character has to be ignored
-}
-
 // You need to free the char!
 char* OLEDDisplay::utf8ascii(String str) {
   uint16_t k = 0;
@@ -798,7 +818,7 @@ char* OLEDDisplay::utf8ascii(String str) {
   length--;
 
   for (uint16_t i=0; i < length; i++) {
-    char c = utf8ascii(s[i]);
+    char c = (this->fontTableLookupFunction)(s[i]);
     if (c!=0) {
       s[k++]=c;
     }
@@ -809,3 +829,7 @@ char* OLEDDisplay::utf8ascii(String str) {
   // This will leak 's' be sure to free it in the calling function.
   return s;
 }
+
+void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) {
+  this->fontTableLookupFunction = function;
+}
\ No newline at end of file
diff --git a/OLEDDisplay.h b/OLEDDisplay.h
index f072b1aa438510f260ecc914bb13824ea5e617bf..83c966c8809bbf127275a08270c6b12780d480c3 100644
--- a/OLEDDisplay.h
+++ b/OLEDDisplay.h
@@ -42,12 +42,6 @@
 #define OLEDDISPLAY_DOUBLE_BUFFER
 #endif
 
-
-// Display settings
-#define DISPLAY_WIDTH 128
-#define DISPLAY_HEIGHT 64
-#define DISPLAY_BUFFER_SIZE 1024
-
 // Header Values
 #define JUMPTABLE_BYTES 4
 
@@ -108,8 +102,18 @@ enum OLEDDISPLAY_TEXT_ALIGNMENT {
 };
 
 
+enum OLEDDISPLAY_GEOMETRY {
+  GEOMETRY_128_64   = 0,
+  GEOMETRY_128_32   = 1
+};
+
+typedef byte (*FontTableLookupFunction)(const byte ch);
+
+
 class OLEDDisplay : public Print {
   public:
+    virtual ~OLEDDisplay() {}
+    
     // Initialize the display
     bool init();
 
@@ -123,6 +127,9 @@ class OLEDDisplay : public Print {
     // Sets the color of all pixel operations
     void setColor(OLEDDISPLAY_COLOR color);
 
+    // Returns the current color.
+    OLEDDISPLAY_COLOR getColor();
+
     // Draw a pixel at given position
     void setPixel(int16_t x, int16_t y);
 
@@ -147,18 +154,18 @@ class OLEDDisplay : public Print {
     // Draw a line horizontally
     void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
 
-    // Draw a lin vertically
+    // Draw a line 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);
 
     // Draw a bitmap in the internal image format
-    void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const char *image);
+    void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image);
 
     // Draw a XBM
-    void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char *xbm);
+    void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm);
 
     /* Text functions */
 
@@ -184,7 +191,10 @@ class OLEDDisplay : public Print {
 
     // Sets the current font. Available default fonts
     // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
-    void setFont(const char *fontData);
+    void setFont(const uint8_t *fontData);
+
+    // Set the function that will convert utf-8 to font table index
+    void setFontTableLookupFunction(FontTableLookupFunction function);
 
     /* Display functions */
 
@@ -203,9 +213,15 @@ class OLEDDisplay : public Print {
     // Set display contrast
     void setContrast(char contrast);
 
+    // Reset display rotation or mirroring
+    void resetOrientation();
+
     // Turn the display upside down
     void flipScreenVertically();
 
+    // Mirror the display (to be used in a mirror or as a projector)
+    void mirrorScreen();
+
     // Write the buffer to the display memory
     virtual void display(void) = 0;
 
@@ -222,7 +238,11 @@ class OLEDDisplay : public Print {
     // Draw the log buffer at position (x, y)
     void drawLogBuffer(uint16_t x, uint16_t y);
 
-    // Implementent needed function to be compatible with Print class
+    // Get screen geometry
+    uint16_t getWidth(void);
+    uint16_t getHeight(void);
+
+    // Implement needed function to be compatible with Print class
     size_t write(uint8_t c);
     size_t write(const char* s);
 
@@ -234,10 +254,16 @@ class OLEDDisplay : public Print {
 
   protected:
 
+    OLEDDISPLAY_GEOMETRY geometry              = GEOMETRY_128_64;
+
+    uint16_t  displayWidth                     = 128;
+    uint16_t  displayHeight                    = 64;
+    uint16_t  displayBufferSize                = 1024;
+
     OLEDDISPLAY_TEXT_ALIGNMENT   textAlignment = TEXT_ALIGN_LEFT;
     OLEDDISPLAY_COLOR            color         = WHITE;
 
-    const char          *fontData              = ArialMT_Plain_10;
+    const uint8_t          *fontData     = ArialMT_Plain_10;
 
     // State values for logBuffer
     uint16_t   logBufferSize                   = 0;
@@ -256,13 +282,33 @@ class OLEDDisplay : public Print {
     void sendInitCommands();
 
     // converts utf8 characters to extended ascii
-    static char* utf8ascii(String s);
-    static byte utf8ascii(byte ascii);
+    char* utf8ascii(String s);
 
-    void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline));
+    void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline));
 
     void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth);
 
+    // UTF-8 to font table index converter
+    // Code form http://playground.arduino.cc/Main/Utf8ascii
+    FontTableLookupFunction fontTableLookupFunction = [](const byte ch) {
+      static uint8_t LASTCHAR;
+
+      if (ch < 128) { // Standard ASCII-set 0..0x7F handling
+        LASTCHAR = 0;
+        return ch;
+      }
+
+      uint8_t last = LASTCHAR;   // get last char
+      LASTCHAR = ch;
+
+      switch (last) {    // conversion depnding on first UTF8-character
+        case 0xC2: return (uint8_t) ch;  break;
+        case 0xC3: return (uint8_t) (ch | 0xC0);  break;
+        case 0x82: if (ch == 0xAC) return (uint8_t) 0x80;    // special case Euro-symbol
+      }
+
+      return (uint8_t) 0; // otherwise: return zero, if character has to be ignored
+    };
 };
 
 #endif
diff --git a/OLEDDisplayFonts.h b/OLEDDisplayFonts.h
index 6dd21ef60b9919ca2dd24935340f3a5f518735a8..3544edb80d1721237febea01c3f6389ee6e2ad60 100644
--- a/OLEDDisplayFonts.h
+++ b/OLEDDisplayFonts.h
@@ -1,7 +1,7 @@
 #ifndef OLEDDISPLAYFONTS_h
 #define OLEDDISPLAYFONTS_h
 
-const char ArialMT_Plain_10[] PROGMEM = {
+const uint8_t ArialMT_Plain_10[] PROGMEM = {
   0x0A, // Width: 10
   0x0D, // Height: 13
   0x20, // First Char: 32
@@ -425,7 +425,7 @@ const char ArialMT_Plain_10[] PROGMEM = {
   0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20  // 255
 };
 
-const char ArialMT_Plain_16[] PROGMEM = {
+const uint8_t ArialMT_Plain_16[] PROGMEM = {
   0x10, // Width: 16
   0x13, // Height: 19
   0x20, // First Char: 32
@@ -848,7 +848,7 @@ const char ArialMT_Plain_16[] PROGMEM = {
   0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254
   0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0  // 255
 };
-const char ArialMT_Plain_24[] PROGMEM = {
+const uint8_t ArialMT_Plain_24[] PROGMEM = {
   0x18, // Width: 24
   0x1C, // Height: 28
   0x20, // First Char: 32
@@ -1271,4 +1271,4 @@ const char ArialMT_Plain_24[] PROGMEM = {
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254
   0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255
 };
-#endif 
+#endif
diff --git a/OLEDDisplayUi.cpp b/OLEDDisplayUi.cpp
index b3fdfdb820cb396fa70fb1f6243786a8242e39e5..b29b8b55799ab6cb9ffcce7d216e5a3ebe8f1d0c 100644
--- a/OLEDDisplayUi.cpp
+++ b/OLEDDisplayUi.cpp
@@ -90,10 +90,10 @@ void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) {
 void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) {
   this->indicatorDirection = dir;
 }
-void OLEDDisplayUi::setActiveSymbol(const char* symbol) {
+void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) {
   this->activeSymbol = symbol;
 }
-void OLEDDisplayUi::setInactiveSymbol(const char* symbol) {
+void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) {
   this->inactiveSymbol = symbol;
 }
 
@@ -254,28 +254,29 @@ void OLEDDisplayUi::drawFrame(){
        int16_t x = 0, y = 0, x1 = 0, y1 = 0;
        switch(this->frameAnimationDirection){
         case SLIDE_LEFT:
-          x = -128 * progress;
+          x = -(this->display->getWidth()) * progress;
           y = 0;
-          x1 = x + 128;
+          x1 = x + this->display->getWidth();
           y1 = 0;
           break;
         case SLIDE_RIGHT:
-          x = 128 * progress;
+          x = this->display->getWidth() * progress;
           y = 0;
-          x1 = x - 128;
+          x1 = x - this->display->getWidth();
           y1 = 0;
           break;
         case SLIDE_UP:
           x = 0;
-          y = -64 * progress;
+          y = -(this->display->getHeight()) * progress;
           x1 = 0;
-          y1 = y + 64;
+          y1 = y + (this->display->getHeight());
           break;
         case SLIDE_DOWN:
+        default:
           x = 0;
-          y = 64 * progress;
+          y = (this->display->getHeight()) * progress;
           x1 = 0;
-          y1 = y - 64;
+          y1 = y - (this->display->getHeight());
           break;
        }
 
@@ -345,6 +346,7 @@ void OLEDDisplayUi::drawIndicator() {
         posOfHighlightFrame = frameToHighlight;
         break;
       case RIGHT_LEFT:
+      default:
         posOfHighlightFrame = this->frameCount - frameToHighlight;
         break;
     }
@@ -360,27 +362,36 @@ void OLEDDisplayUi::drawIndicator() {
         break;
     }
 
-    uint16_t frameStartPos = (12 * frameCount / 2);
-    const char *image;
-    uint16_t x = 0, y = 0;
+    //Space between indicators - reduce for small screen sizes
+    uint16_t indicatorSpacing = 12;
+    if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) {
+      indicatorSpacing = 6;
+    }
+    
+    uint16_t frameStartPos = (indicatorSpacing * frameCount / 2);
+    const uint8_t *image;
+
+    uint16_t x,y;
+
     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->getWidth() / 2) - frameStartPos + indicatorSpacing * i;
           break;
         case BOTTOM:
-          y = 56 + (8 * indicatorFadeProgress);
-          x = 64 - frameStartPos + 12 * i;
+          y = (this->display->getHeight() - 8) + (8 * indicatorFadeProgress);
+          x = (this->display->getWidth() / 2) - frameStartPos + indicatorSpacing * i;
           break;
         case RIGHT:
-          x = 120 + (8 * indicatorFadeProgress);
-          y = 32 - frameStartPos + 2 + 12 * i;
+          x = (this->display->getWidth() - 8) + (8 * indicatorFadeProgress);
+          y = (this->display->getHeight() / 2) - frameStartPos + indicatorSpacing * i;
           break;
         case LEFT:
+        default:
           x = 0 - (8 * indicatorFadeProgress);
-          y = 32 - frameStartPos + 2 + 12 * i;
+          y = (this->display->getHeight() / 2) - frameStartPos + indicatorSpacing * i;
           break;
       }
 
diff --git a/OLEDDisplayUi.h b/OLEDDisplayUi.h
index 35a1e99bcf449c3d2f5f4405f223949005fce497..a5f6c90083de0b8281fca8787eff19446ebde65b 100644
--- a/OLEDDisplayUi.h
+++ b/OLEDDisplayUi.h
@@ -61,11 +61,11 @@ enum FrameState {
 };
 
 
-const char ANIMATION_activeSymbol[] PROGMEM = {
+const uint8_t ANIMATION_activeSymbol[] PROGMEM = {
   0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00
 };
 
-const char ANIMATION_inactiveSymbol[] PROGMEM = {
+const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = {
   0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00
 };
 
@@ -106,8 +106,8 @@ class OLEDDisplayUi {
     IndicatorPosition   indicatorPosition         = BOTTOM;
     IndicatorDirection  indicatorDirection        = LEFT_RIGHT;
 
-    const char*         activeSymbol              = ANIMATION_activeSymbol;
-    const char*         inactiveSymbol            = ANIMATION_inactiveSymbol;
+    const uint8_t*         activeSymbol              = ANIMATION_activeSymbol;
+    const uint8_t*         inactiveSymbol            = ANIMATION_inactiveSymbol;
 
     bool                shouldDrawIndicators      = true;
 
@@ -240,12 +240,12 @@ class OLEDDisplayUi {
     /**
      * Set the symbol to indicate an active frame in the indicator bar.
      */
-    void setActiveSymbol(const char* symbol);
+    void setActiveSymbol(const uint8_t* symbol);
 
     /**
      * Set the symbol to indicate an inactive frame in the indicator bar.
      */
-    void setInactiveSymbol(const char* symbol);
+    void setInactiveSymbol(const uint8_t* symbol);
 
 
     // Frame settings
diff --git a/README.md b/README.md
index 01c134c3506041cdd547a76b78b673f1d2659b49..28c11b55ac8c2c5e4ea8e5d58051802ebae25473 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
-esp8266-oled-ssd1306 [![Build Status](https://travis-ci.org/squix78/esp8266-oled-ssd1306.svg?branch=dev-branch-3.0.0)](https://travis-ci.org/squix78/esp8266-oled-ssd1306)
-============
+[![Build Status](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306.svg?branch=master)](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306)
+
+# ESP8266 OLED SSD1306 
 
 > We just released version 3.0.0. Please have a look at our [upgrade guide](UPGRADE-3.0.md)
 
@@ -19,7 +20,7 @@ The init sequence for the SSD1306 was inspired by Adafruit's library for the sam
 
 ## Usage
 
-Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the ESP8266 Weather Station library (https://github.com/squix78/esp8266-weather-station) which uses the OLED library to display beautiful weather information.
+Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information.
 
 ## Upgrade
 
@@ -141,6 +142,9 @@ void setContrast(char contrast);
 
 // Turn the display upside down
 void flipScreenVertically();
+
+// Draw the screen mirrored
+void mirrorScreen();
 ```
 
 ## Pixel drawing
diff --git a/SH1106Brzo.h b/SH1106Brzo.h
index 385630b3267a7191fe0c81a21fe34d4b5de0a2e4..a747cb68ebe2994cc1ad2a13eedf58335db765ba 100644
--- a/SH1106Brzo.h
+++ b/SH1106Brzo.h
@@ -44,7 +44,18 @@ class SH1106Brzo : public OLEDDisplay {
       uint8_t             _scl;
 
   public:
-    SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+    SH1106Brzo(OLEDDISPLAY_GEOMETRY g, uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->geometry = g;
+      if (g == GEOMETRY_128_64) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 64;
+        this->displayBufferSize                = 1024;
+      } else if (g == GEOMETRY_128_32) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 32;
+        this->displayBufferSize                = 512;    
+      }
+
       this->_address = _address;
       this->_sda = _sda;
       this->_scl = _scl;
@@ -66,9 +77,9 @@ class SH1106Brzo : 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 < (displayHeight / 8); y++) {
+         for (x = 0; x < displayWidth; x++) {
+          uint16_t pos = x + y * displayWidth;
           if (buffer[pos] != buffer_back[pos]) {
             minBoundY = _min(minBoundY, y);
             maxBoundY = _max(maxBoundY, y);
@@ -101,7 +112,7 @@ class SH1106Brzo : public OLEDDisplay {
          sendCommand(minBoundXp2L);
          for (x = minBoundX; x <= maxBoundX; x++) {
              k++;
-             sendBuffer[k] = buffer[x + y * DISPLAY_WIDTH];
+             sendBuffer[k] = buffer[x + y * displayWidth];
              if (k == 16)  {
                brzo_i2c_write(sendBuffer, 17, true);
                k = 0;
diff --git a/SH1106Spi.h b/SH1106Spi.h
index 0a64e27568ff9fc1270e13ec0eec221ac9337a67..d81d90d38a2a4668ecca1329256d9f0bf22df788 100644
--- a/SH1106Spi.h
+++ b/SH1106Spi.h
@@ -38,7 +38,18 @@ class SH1106Spi : public OLEDDisplay {
 
   public:
 
-    SH1106Spi(uint8_t _rst, uint8_t _dc) {
+    SH1106Spi(OLEDDISPLAY_GEOMETRY g, uint8_t _rst, uint8_t _dc) {      
+      this->geometry = g;
+      if (g == GEOMETRY_128_64) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 64;
+        this->displayBufferSize                = 1024;
+      } else if (g == GEOMETRY_128_32) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 32;
+        this->displayBufferSize                = 512;    
+      }
+
       this->_rst = _rst;
       this->_dc  = _dc;
     }
@@ -71,9 +82,9 @@ class SH1106Spi : 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 < (displayHeight / 8); y++) {
+         for (x = 0; x < displayWidth; x++) {
+          uint16_t pos = x + y * displayWidth;
           if (buffer[pos] != buffer_back[pos]) {
             minBoundY = _min(minBoundY, y);
             maxBoundY = _max(maxBoundY, y);
@@ -100,18 +111,18 @@ class SH1106Spi : public OLEDDisplay {
          sendCommand(minBoundXp2L);
          digitalWrite(_dc, HIGH);   // data mode
          for (x = minBoundX; x <= maxBoundX; x++) {
-           SPI.transfer(buffer[x + y * DISPLAY_WIDTH]);
+           SPI.transfer(buffer[x + y * displayWidth]);
          }
          yield();
        }
      #else
-      for (uint8_t y=0; y<DISPLAY_HEIGHT/8; y++) {
+      for (uint8_t y=0; y<displayHeight/8; y++) {
         sendCommand(0xB0 + y);
         sendCommand(0x02);
         sendCommand(0x10);
         digitalWrite(_dc, HIGH);   // data mode
-        for( uint8_t x=0; x < DISPLAY_WIDTH; x++) {
-          SPI.transfer(buffer[x + y * DISPLAY_WIDTH]);
+        for( uint8_t x=0; x < displayWidth; x++) {
+          SPI.transfer(buffer[x + y * displayWidth]);
         }
         yield();
       }
diff --git a/SH1106Wire.h b/SH1106Wire.h
index f0784b9423051a4e91bf2e237b38347a2f6b6ede..3205ac06ad09ecdff37c63d4564c7a2022786e09 100644
--- a/SH1106Wire.h
+++ b/SH1106Wire.h
@@ -44,7 +44,18 @@ class SH1106Wire : public OLEDDisplay {
       uint8_t             _scl;
 
   public:
-    SH1106Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+    SH1106Wire(OLEDDISPLAY_GEOMETRY g, uint8_t _address, uint8_t _sda, uint8_t _scl) {      
+      this->geometry = g;
+      if (g == GEOMETRY_128_64) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 64;
+        this->displayBufferSize                = 1024;
+      } else if (g == GEOMETRY_128_32) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 32;
+        this->displayBufferSize                = 512;    
+      }
+
       this->_address = _address;
       this->_sda = _sda;
       this->_scl = _scl;
@@ -70,9 +81,9 @@ class SH1106Wire : 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 < (displayHeight / 8); y++) {
+          for (x = 0; x < displayWidth; x++) {
+           uint16_t pos = x + y * displayWidth;
            if (buffer[pos] != buffer_back[pos]) {
              minBoundY = _min(minBoundY, y);
              maxBoundY = _max(maxBoundY, y);
@@ -103,7 +114,7 @@ class SH1106Wire : public OLEDDisplay {
               Wire.beginTransmission(_address);
               Wire.write(0x40);
             }
-            Wire.write(buffer[x + y * DISPLAY_WIDTH]);
+            Wire.write(buffer[x + y * displayWidth]);
             k++;
             if (k == 16)  {
               Wire.endTransmission();
diff --git a/SSD1306Brzo.h b/SSD1306Brzo.h
index 3b99d82d2a196dbc0836b8e4ad13d7fa46b60813..a9a2aa0cccb0079b727a312c3be97171d88d7e84 100644
--- a/SSD1306Brzo.h
+++ b/SSD1306Brzo.h
@@ -44,7 +44,18 @@ class SSD1306Brzo : public OLEDDisplay {
       uint8_t             _scl;
 
   public:
-    SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+    SSD1306Brzo(OLEDDISPLAY_GEOMETRY g, uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->geometry = g;
+      if (g == GEOMETRY_128_64) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 64;
+        this->displayBufferSize                = 1024;
+      } else if (g == GEOMETRY_128_32) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 32;
+        this->displayBufferSize                = 512;    
+      }
+
       this->_address = _address;
       this->_sda = _sda;
       this->_scl = _scl;
@@ -67,9 +78,9 @@ class SSD1306Brzo : 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 < (displayHeight / 8); y++) {
+         for (x = 0; x < displayWidth; x++) {
+          uint16_t pos = x + y * displayWidth;
           if (buffer[pos] != buffer_back[pos]) {
             minBoundY = _min(minBoundY, y);
             maxBoundY = _max(maxBoundY, y);
@@ -101,7 +112,7 @@ class SSD1306Brzo : public OLEDDisplay {
        for (y = minBoundY; y <= maxBoundY; y++) {
            for (x = minBoundX; x <= maxBoundX; x++) {
                k++;
-               sendBuffer[k] = buffer[x + y * DISPLAY_WIDTH];
+               sendBuffer[k] = buffer[x + y * displayWidth];
                if (k == 16)  {
                  brzo_i2c_write(sendBuffer, 17, true);
                  k = 0;
@@ -119,12 +130,17 @@ class SSD1306Brzo : public OLEDDisplay {
 
        sendCommand(PAGEADDR);
        sendCommand(0x0);
-       sendCommand(0x7);
+       
+       if (geometry == GEOMETRY_128_64) {
+         sendCommand(0x7);
+       } else if (geometry == GEOMETRY_128_32) {
+         sendCommand(0x3);
+       }
 
        uint8_t sendBuffer[17];
        sendBuffer[0] = 0x40;
        brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
-       for (uint16_t i=0; i<DISPLAY_BUFFER_SIZE; i++) {
+       for (uint16_t i=0; i<displayBufferSize; i++) {
          for (uint8_t x=1; x<17; x++) {
            sendBuffer[x] = buffer[i];
            i++;
diff --git a/SSD1306Spi.h b/SSD1306Spi.h
index 21794587ac612cb951706546dadb2d2bb0c23593..9a41a0a264dbb1bca4a01d4dcf6fb28eb6e28fe4 100644
--- a/SSD1306Spi.h
+++ b/SSD1306Spi.h
@@ -44,7 +44,18 @@ class SSD1306Spi : public OLEDDisplay {
       uint8_t             _cs;
 
   public:
-    SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs) {
+    SSD1306Spi(OLEDDISPLAY_GEOMETRY g,  uint8_t _rst, uint8_t _dc, uint8_t _cs) {
+      this->geometry = g;
+      if (g == GEOMETRY_128_64) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 64;
+        this->displayBufferSize                = 1024;
+      } else if (g == GEOMETRY_128_32) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 32;
+        this->displayBufferSize                = 512;    
+      }
+      
       this->_rst = _rst;
       this->_dc  = _dc;
       this->_cs  = _cs;
@@ -79,9 +90,9 @@ class SSD1306Spi : 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 < (displayHeight / 8); y++) {
+         for (x = 0; x < displayWidth; x++) {
+          uint16_t pos = x + y * displayWidth;
           if (buffer[pos] != buffer_back[pos]) {
             minBoundY = _min(minBoundY, y);
             maxBoundY = _max(maxBoundY, y);
@@ -111,7 +122,7 @@ class SSD1306Spi : public OLEDDisplay {
        digitalWrite(_cs, LOW);
        for (y = minBoundY; y <= maxBoundY; y++) {
          for (x = minBoundX; x <= maxBoundX; x++) {
-           SPI.transfer(buffer[x + y * DISPLAY_WIDTH]);
+           SPI.transfer(buffer[x + y * displayWidth]);
          }
          yield();
        }
@@ -124,12 +135,17 @@ class SSD1306Spi : public OLEDDisplay {
 
        sendCommand(PAGEADDR);
        sendCommand(0x0);
-       sendCommand(0x7);
+
+       if (geometry == GEOMETRY_128_64) {
+         sendCommand(0x7);
+       } else if (geometry == GEOMETRY_128_32) {
+         sendCommand(0x3);
+       }
 
         digitalWrite(_cs, HIGH);
         digitalWrite(_dc, HIGH);   // data mode
         digitalWrite(_cs, LOW);
-        for (uint16_t i=0; i<DISPLAY_BUFFER_SIZE; i++) {
+        for (uint16_t i=0; i<displayBufferSize; i++) {
           SPI.transfer(buffer[i]);
           yield();
         }
diff --git a/SSD1306Wire.h b/SSD1306Wire.h
index d558da844e2dc7d1e9749031e708d7a7e1fb5d39..2c92327e00e0379e6675609e211c8ca52952c870 100644
--- a/SSD1306Wire.h
+++ b/SSD1306Wire.h
@@ -38,7 +38,18 @@ class SSD1306Wire : public OLEDDisplay {
       uint8_t             _scl;
 
   public:
-    SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+    SSD1306Wire(OLEDDISPLAY_GEOMETRY g, uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->geometry = g;
+      if (g == GEOMETRY_128_64) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 64;
+        this->displayBufferSize                = 1024;
+      } else if (g == GEOMETRY_128_32) {
+        this->displayWidth                     = 128;
+        this->displayHeight                    = 32;
+        this->displayBufferSize                = 512;    
+      }
+
       this->_address = _address;
       this->_sda = _sda;
       this->_scl = _scl;
@@ -63,9 +74,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 < (displayHeight / 8); y++) {
+          for (x = 0; x < displayWidth; x++) {
+           uint16_t pos = x + y * displayWidth;
            if (buffer[pos] != buffer_back[pos]) {
              minBoundY = _min(minBoundY, y);
              maxBoundY = _max(maxBoundY, y);
@@ -97,7 +108,7 @@ class SSD1306Wire : public OLEDDisplay {
               Wire.beginTransmission(_address);
               Wire.write(0x40);
             }
-            Wire.write(buffer[x + y * DISPLAY_WIDTH]);
+            Wire.write(buffer[x + y * displayWidth]);
             k++;
             if (k == 16)  {
               Wire.endTransmission();
@@ -118,9 +129,14 @@ class SSD1306Wire : public OLEDDisplay {
 
         sendCommand(PAGEADDR);
         sendCommand(0x0);
-        sendCommand(0x7);
+        
+        if (geometry == GEOMETRY_128_64) {
+          sendCommand(0x7);
+        } else if (geometry == GEOMETRY_128_32) {
+          sendCommand(0x3);
+        }
 
-        for (uint16_t i=0; i < DISPLAY_BUFFER_SIZE; i++) {
+        for (uint16_t i=0; i < displayBufferSize; i++) {
           Wire.beginTransmission(this->_address);
           Wire.write(0x40);
           for (uint8_t x = 0; x < 16; x++) {
diff --git a/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino b/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino
index e9db7d645a610f8c42d857ba20a774b2fe9cd89e..1f1a8c968575c8c5b9ee7e2a8e6aa6afe7dca68b 100644
--- a/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino
+++ b/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino
@@ -66,7 +66,7 @@
 // SH1106Brzo  display(0x3c, D3, D5);
 
 // Initialize the OLED display using Wire library
-SSD1306  display(0x3c, D3, D5);
+SSD1306  display(GEOMETRY_128_64, 0x3c, D3, D5);
 // SH1106 display(0x3c, D3, D5);
 
 OLEDDisplayUi ui ( &display );
diff --git a/examples/SSD1306ClockDemo/images.h b/examples/SSD1306ClockDemo/images.h
index a220a27c2b6637946f5df26f500f0eb88998a2c8..1889188fd489e49ef1a6dd8ce6a7ffaa4f956be0 100644
--- a/examples/SSD1306ClockDemo/images.h
+++ b/examples/SSD1306ClockDemo/images.h
@@ -1,4 +1,4 @@
-const char activeSymbol[] PROGMEM = {
+const unsigned char activeSymbol[] PROGMEM = {
     B00000000,
     B00000000,
     B00011000,
@@ -9,7 +9,7 @@ const char activeSymbol[] PROGMEM = {
     B00011000
 };
 
-const char inactiveSymbol[] PROGMEM = {
+const unsigned char inactiveSymbol[] PROGMEM = {
     B00000000,
     B00000000,
     B00000000,
diff --git a/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
index cf37fb0412002492388ed3d71fbb1ec58866c233..3f6e0a4f1c90ee597abf6faf213980e29a088368 100644
--- a/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
+++ b/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
@@ -58,56 +58,56 @@
  // SH1106Brzo  display(0x3c, D3, D5);
 
  // Initialize the OLED display using Wire library
- SSD1306  display(0x3c, D3, D5);
+ SSD1306  display(GEOMETRY_128_64, 0x3c, D3, D5);
  // SH1106 display(0x3c, D3, D5);
 
 // Adapted from Adafruit_SSD1306
 void drawLines() {
-  for (int16_t i=0; i<DISPLAY_WIDTH; i+=4) {
-    display.drawLine(0, 0, i, DISPLAY_HEIGHT-1);
+  for (int16_t i=0; i<display.getWidth(); i+=4) {
+    display.drawLine(0, 0, i, display.getHeight()-1);
     display.display();
     delay(10);
   }
-  for (int16_t i=0; i<DISPLAY_HEIGHT; i+=4) {
-    display.drawLine(0, 0, DISPLAY_WIDTH-1, i);
+  for (int16_t i=0; i<display.getHeight(); i+=4) {
+    display.drawLine(0, 0, display.getWidth()-1, i);
     display.display();
     delay(10);
   }
   delay(250);
 
   display.clear();
-  for (int16_t i=0; i<DISPLAY_WIDTH; i+=4) {
-    display.drawLine(0, DISPLAY_HEIGHT-1, i, 0);
+  for (int16_t i=0; i<display.getWidth(); i+=4) {
+    display.drawLine(0, display.getHeight()-1, i, 0);
     display.display();
     delay(10);
   }
-  for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) {
-    display.drawLine(0, DISPLAY_HEIGHT-1, DISPLAY_WIDTH-1, i);
+  for (int16_t i=display.getHeight()-1; i>=0; i-=4) {
+    display.drawLine(0, display.getHeight()-1, display.getWidth()-1, i);
     display.display();
     delay(10);
   }
   delay(250);
 
   display.clear();
-  for (int16_t i=DISPLAY_WIDTH-1; i>=0; i-=4) {
-    display.drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, i, 0);
+  for (int16_t i=display.getWidth()-1; i>=0; i-=4) {
+    display.drawLine(display.getWidth()-1, display.getHeight()-1, i, 0);
     display.display();
     delay(10);
   }
-  for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) {
-    display.drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0, i);
+  for (int16_t i=display.getHeight()-1; i>=0; i-=4) {
+    display.drawLine(display.getWidth()-1, display.getHeight()-1, 0, i);
     display.display();
     delay(10);
   }
   delay(250);
   display.clear();
-  for (int16_t i=0; i<DISPLAY_HEIGHT; i+=4) {
-    display.drawLine(DISPLAY_WIDTH-1, 0, 0, i);
+  for (int16_t i=0; i<display.getHeight(); i+=4) {
+    display.drawLine(display.getWidth()-1, 0, 0, i);
     display.display();
     delay(10);
   }
-  for (int16_t i=0; i<DISPLAY_WIDTH; i+=4) {
-    display.drawLine(DISPLAY_WIDTH-1, 0, i, DISPLAY_HEIGHT-1);
+  for (int16_t i=0; i<display.getWidth(); i+=4) {
+    display.drawLine(display.getWidth()-1, 0, i, display.getHeight()-1);
     display.display();
     delay(10);
   }
@@ -116,8 +116,8 @@ void drawLines() {
 
 // Adapted from Adafruit_SSD1306
 void drawRect(void) {
-  for (int16_t i=0; i<DISPLAY_HEIGHT/2; i+=2) {
-    display.drawRect(i, i, DISPLAY_WIDTH-2*i, DISPLAY_HEIGHT-2*i);
+  for (int16_t i=0; i<display.getHeight()/2; i+=2) {
+    display.drawRect(i, i, display.getWidth()-2*i, display.getHeight()-2*i);
     display.display();
     delay(10);
   }
@@ -126,9 +126,9 @@ void drawRect(void) {
 // Adapted from Adafruit_SSD1306
 void fillRect(void) {
   uint8_t color = 1;
-  for (int16_t i=0; i<DISPLAY_HEIGHT/2; i+=3) {
+  for (int16_t i=0; i<display.getHeight()/2; i+=3) {
     display.setColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors
-    display.fillRect(i, i, DISPLAY_WIDTH - i*2, DISPLAY_HEIGHT - i*2);
+    display.fillRect(i, i, display.getWidth() - i*2, display.getHeight() - i*2);
     display.display();
     delay(10);
     color++;
@@ -139,8 +139,8 @@ void fillRect(void) {
 
 // Adapted from Adafruit_SSD1306
 void drawCircle(void) {
-  for (int16_t i=0; i<DISPLAY_HEIGHT; i+=2) {
-    display.drawCircle(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, i);
+  for (int16_t i=0; i<display.getHeight(); i+=2) {
+    display.drawCircle(display.getWidth()/2, display.getHeight()/2, i);
     display.display();
     delay(10);
   }
@@ -153,16 +153,16 @@ void drawCircle(void) {
   //  ------|-----
   //   0100 | 1000
   //
-  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000001);
+  display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000001);
   display.display();
   delay(200);
-  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000011);
+  display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000011);
   display.display();
   delay(200);
-  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000111);
+  display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000111);
   display.display();
   delay(200);
-  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00001111);
+  display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00001111);
   display.display();
 }
 
diff --git a/examples/SSD1306OTADemo/SSD1306OTADemo.ino b/examples/SSD1306OTADemo/SSD1306OTADemo.ino
index 6460ff9e4a308dffa396d0db473c6708e0c14f34..856917a958d22b8fc5f2ee1e8a5286b40a8fe95e 100644
--- a/examples/SSD1306OTADemo/SSD1306OTADemo.ino
+++ b/examples/SSD1306OTADemo/SSD1306OTADemo.ino
@@ -69,7 +69,7 @@
 // SH1106Brzo  display(0x3c, D3, D5);
 
 // Initialize the OLED display using Wire library
-SSD1306  display(0x3c, D3, D5);
+SSD1306  display(GEOMETRY_128_64, 0x3c, D3, D5);
 // SH1106 display(0x3c, D3, D5);
 
 
@@ -90,7 +90,7 @@ void setup() {
     display.clear();
     display.setFont(ArialMT_Plain_10);
     display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
-    display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2 - 10, "OTA Update");
+    display.drawString(display.getWidth()/2, display.getHeight()/2 - 10, "OTA Update");
     display.display();
   });
 
@@ -103,14 +103,14 @@ void setup() {
     display.clear();
     display.setFont(ArialMT_Plain_10);
     display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
-    display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, "Restart");
+    display.drawString(display.getWidth()/2, display.getHeight()/2, "Restart");
     display.display();
   });
 
   // Align text vertical/horizontal center
   display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
   display.setFont(ArialMT_Plain_10);
-  display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, "Ready for OTA:\n" + WiFi.localIP().toString());
+  display.drawString(display.getWidth()/2, display.getHeight()/2, "Ready for OTA:\n" + WiFi.localIP().toString());
   display.display();
 }
 
diff --git a/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino
index ed7401915861f89d9a1f4dd6f697bc82b44f3069..12ad6b111c57830117422b76bc9e28d86141cebd 100644
--- a/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino
+++ b/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino
@@ -58,7 +58,7 @@
 // SH1106Brzo  display(0x3c, D3, D5);
 
 // Initialize the OLED display using Wire library
-SSD1306  display(0x3c, D3, D5);
+SSD1306  display(GEOMETRY_128_64, 0x3c, D3, D5);
 // SH1106 display(0x3c, D3, D5);
 
 
diff --git a/examples/SSD1306SimpleDemo/images.h b/examples/SSD1306SimpleDemo/images.h
index 9daf8c1a25b9d006fc67f49c6868563d13c7ae77..50417990fd0d3d0854b4385afac1334d45223214 100644
--- a/examples/SSD1306SimpleDemo/images.h
+++ b/examples/SSD1306SimpleDemo/images.h
@@ -1,6 +1,6 @@
 #define WiFi_Logo_width 60
 #define WiFi_Logo_height 36
-const char WiFi_Logo_bits[] PROGMEM = {
+const uint8_t WiFi_Logo_bits[] PROGMEM = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
diff --git a/examples/SSD1306UiDemo/SSD1306UiDemo.ino b/examples/SSD1306UiDemo/SSD1306UiDemo.ino
index 31357569ff3849b6c1098165aa96f9880129327f..f19617af86301391786b15bbc80b23080556d865 100644
--- a/examples/SSD1306UiDemo/SSD1306UiDemo.ino
+++ b/examples/SSD1306UiDemo/SSD1306UiDemo.ino
@@ -64,7 +64,7 @@
 // SH1106Brzo  display(0x3c, D3, D5);
 
 // Initialize the OLED display using Wire library
-SSD1306  display(0x3c, D3, D5);
+SSD1306  display(GEOMETRY_128_64, 0x3c, D3, D5);
 // SH1106 display(0x3c, D3, D5);
 
 OLEDDisplayUi ui     ( &display );
diff --git a/examples/SSD1306UiDemo/images.h b/examples/SSD1306UiDemo/images.h
index 8b876a369e6f2609700798da9da47431e5e0db21..2489d1e5de658663f1bf16b456b18743193cf1c0 100644
--- a/examples/SSD1306UiDemo/images.h
+++ b/examples/SSD1306UiDemo/images.h
@@ -1,6 +1,6 @@
 #define WiFi_Logo_width 60
 #define WiFi_Logo_height 36
-const char WiFi_Logo_bits[] PROGMEM = {
+const uint8_t WiFi_Logo_bits[] PROGMEM = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
@@ -27,7 +27,7 @@ const char WiFi_Logo_bits[] PROGMEM = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   };
 
-const char activeSymbol[] PROGMEM = {
+const uint8_t activeSymbol[] PROGMEM = {
     B00000000,
     B00000000,
     B00011000,
@@ -38,7 +38,7 @@ const char activeSymbol[] PROGMEM = {
     B00011000
 };
 
-const char inactiveSymbol[] PROGMEM = {
+const uint8_t inactiveSymbol[] PROGMEM = {
     B00000000,
     B00000000,
     B00000000,
diff --git a/resources/glyphEditor.html b/resources/glyphEditor.html
new file mode 100644
index 0000000000000000000000000000000000000000..f253a807b0cd971b2493b7469fe1a2a50c737be5
--- /dev/null
+++ b/resources/glyphEditor.html
@@ -0,0 +1,633 @@
+<!--The MIT License (MIT)
+
+Copyright (c) 2017 by Xavier Grosjean
+
+Based on work 
+Copyright (c) 2016 by Daniel Eichhorn
+Copyright (c) 2016 by Fabrice Weinberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+-->
+<!--
+ Standalone page to draw icons in matrix and generates the map definition with the format required by 
+ library for OLED SD1306 screen: https://github.com/squix78/esp8266-oled-ssd1306
+ 100% quick and dirty vanilla ECS6, no framework or library was injured for this project.
+
+-->
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <style>
+      #form, #chars table {
+        user-select: none; 
+        -moz-user-select: none; 
+        margin-top: 5px;
+      }
+      #chars table, tr, td, th {
+        border-collapse: collapse;
+      }
+      #chars td, th {
+        border: 1px solid #CCC;
+        width: 12px;
+        height: 15px;
+      }
+      #chars th[action] {
+        cursor:pointer;
+      }
+      #chars th[action="up"], #chars th[action="down"], #chars th[action="left"], #chars th[action="right"] {
+        font-size: 10px;
+      }
+      #chars td.on {
+        background-color: #00F;
+      }
+      
+      #addChar, #generate {
+        display: none;
+      }
+      body.started #addChar, body.started #generate {
+        display: inline;
+      }
+      body.started #create {
+        display: none;
+      }
+      #page {
+        position:relative;
+      }
+      #page>div {
+        position: relative;
+        float: left;
+      }
+
+      #output {
+        margin-left: 20px;
+        margin-top: 12px;
+        font-size:12px;
+        user-select: all;
+        -moz-user-select: all;
+        max-width:60%;
+      }
+      
+      #header {
+        margin-top: 10px;
+      }
+      #inputText {
+        width: 100%;
+        height:120px;
+      }
+      
+    </style>
+  </head>
+  <body>
+    <div id="form">
+      <table width="100%">
+        <tr>
+          <td>Font array name:</td><td><input placeholder="Font array name" type="text" id="name" value="My_Font"/></td>
+          <td width="75%" rowspan="5">
+            <textarea id="inputText" placeholder="Or paste a char array font definition here">
+const char My_Font[] PROGMEM = {
+  0x0A, // Width: 10
+  0x0D, // Height: 13
+  0x01, // First char: 1
+  0x02, // Number of chars: 2
+  // Jump Table:
+  0x00, 0x00, 0x13, 0x0A, // 1
+  0x00, 0x13, 0x14, 0x0A, // 2
+  // Font Data:
+  0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0xC0, // 1
+  0x00, 0x0C, 0x80, 0x0B, 0x70, 0x08, 0x0C, 0x08, 0xF3, 0x0A, 0xF3, 0x0A, 0x0C, 0x08, 0x70, 0x08, 0x80, 0x0B, 0x00, 0x0C, // 2
+};              
+            </textarea></td>
+        </tr>
+        <tr>
+          <td>First char code:</td><td><input placeholder="First char code" type="number" id="code" value="1" min="1"/></td>
+        </tr>
+        <tr>
+          <td>Width:</td><td><input placeholder="Font width" type="number" id="width" value="10"/></td>
+        </tr>
+        <tr>
+          <td>Height:</td><td><input placeholder="Font height" type="number" id="height" value="13" max="64"/></td>
+        </tr>
+        <tr>
+          <td colspan="3"> </td>     <!--slightly improves layout-->
+        </tr>
+        <tr>
+          <td colspan="2"><button id="create">Create</button> <button id="generate">Generate</button> <button id="savetoFile">Save To File</button> </td>
+          <td><input type="file" id="fileinput" />  <button id="parse">Parse</button></td>
+        </tr>
+      </table>
+      <br/>
+    </div>
+    <div id="page">
+      <div id="charxContainer" ondragstart="return false;">
+        <div id="chars"></div><br/>
+        <button id="addChar">Add a character</button>
+      </div>
+      <div id="output">
+        <div class="output" id="header"> </div>
+        <div class="output" id="jump"> </div>
+        <div class="output" id="data"> </div>
+      </div>
+    </div>
+    <script>
+    (function() {
+      let font;
+      
+      class Font {
+        constructor() {
+          this.height = parseInt(document.getElementById('height').value);
+          this.width = parseInt(document.getElementById('width').value);
+          this.currentCharCode = parseInt(document.getElementById('code').value);
+          this.fontContainer = document.getElementById('chars');
+          this.bytesForHeight = (1 + ((this.height - 1) >> 3));  // number of bytes needed for this font height
+          document.body.className = "started";
+          document.getElementById('height').disabled = true;
+          document.getElementById('width').disabled = true;          
+        }
+        
+        // Should we have a Char and a Pixel class ? not sure it's worth it.
+        // Let's use the DOM to store everything for now
+        
+        static updateCaption(element, code) {
+          element.textContent = `Char #${code}`;
+        }
+        
+        // Add a pixel matrix to draw a new character
+        // jumpaData not used for now: some day, use the last byte for optimisation 
+        addChar(jumpData, charData) {
+          let charContainer = this.fontContainer.appendChild(document.createElement("table"));
+          charContainer.setAttribute("code", this.currentCharCode);
+          let caption = charContainer.appendChild(document.createElement("caption"));
+          Font.updateCaption(caption, this.currentCharCode);
+          let header = charContainer.appendChild(document.createElement("tr"));
+          header.innerHTML = '<th  title="Delete this char" action="delete">&cross;</th>'
+              + '<th title="Add a char above" action="add">+</th>'
+              + '<th title="Shift pixels to the left" action="left">&larr;</th>'
+              + '<th title="Shift pixels down" action="down">&darr;</th>'
+              + '<th title="Shift pixels up" action="up">&uarr;</th>'
+              + '<th title="Shift pixels to the right" action="right">&rarr;</th>'
+              + '<th title="Copy from another character" action="copy">&copy;</th>'
+              + '<th title="Reset all pixels" action="clean">&empty;</th>';
+          // If data is provided, decode it to pixel initialization friendly structure
+          let pixelInit = [];
+          if (charData && charData.length) {
+            // charData byte count needs to be a multiple of bytesForHeight. End bytes with value 0 may have been trimmed
+            let missingBytes = charData.length % this.bytesForHeight;
+            for(let b = 0; b < missingBytes ; b++) {
+              charData.push(0);
+            }
+            while(charData.length) {
+              let row = charData.splice(0, this.bytesForHeight).reverse();
+              let pixelRow = [];
+              for (let b = 0; b < row.length; b++) {
+                let mask = 0x80;
+                let byte = row[b];
+                for (let bit = 0; bit < 8; bit++) {
+                  if (byte & mask) {
+                    pixelRow.push(1);
+                  } else {
+                    pixelRow.push(0);
+                  }
+                  mask = mask >> 1;
+                }
+              }
+              pixelRow.splice(0, pixelRow.length - this.height);
+              //Font.output('data', pixelRow);
+              pixelInit.push(pixelRow.reverse());
+            }
+          }
+          
+          for(let r = 0; r < this.height; r++) {
+          	let rowContainer = charContainer.appendChild(document.createElement("tr"));
+            for(let c = 0; c < this.width; c++) {
+          		let pixContainer = rowContainer.appendChild(document.createElement("td"));
+              pixContainer.setAttribute('action', 'toggle');
+          		// If there is some init data, set the pixel accordingly
+              if (pixelInit.length && pixelInit[c]) {
+                if (pixelInit[c][r]) {
+                  pixContainer.className = 'on';
+                }
+              }          		
+            }
+          }
+          this.currentCharCode++;
+          return charContainer;
+        }
+        
+        static togglePixel(pixel) {
+          pixel.className = pixel.className === 'on' ? '': 'on';
+        }
+        
+        // Return anInt as hex string
+        static toHexString(aByte) {
+          let zero = aByte < 16?'0':'';
+          return `0x${zero}${aByte.toString(16).toUpperCase()}`
+        }
+        
+        // Return least significant byte as hex string
+        static getLsB(anInt) {
+          return Font.toHexString(anInt & 0xFF);      
+        }
+        // Return most significant byte as hex string
+        static getMsB(anInt) {
+          return Font.toHexString(anInt>>>8);      
+        }
+       
+        static output(targetId, msg) {
+          let output = document.getElementById(targetId);
+          let line = output.appendChild(document.createElement('div'));
+          line.textContent = msg;
+        }
+        static emptyChars() {
+          document.getElementById('chars').textContent = '';
+        }
+        static emptyOutput() {
+          document.getElementById('header').textContent = '';
+          document.getElementById('jump').textContent = '';
+          document.getElementById('data').textContent = '';
+        }
+		
+        saveFile() {
+          let filename = document.getElementById('name').value.replace(/[^a-zA-Z0-9_$]/g, '_') + ".h";
+          let data = document.getElementById("output").innerText;
+          if(data.length < 10) return;
+          let blobObject = new Blob([data], {type:'text/plain'}); 
+    
+          if(window.navigator.msSaveBlob) {
+            window.navigator.msSaveBlob(blobObject, filename);
+          } else {
+            let a = document.createElement("a");
+            a.setAttribute("href", URL.createObjectURL(blobObject));
+            a.setAttribute("download", filename);
+            if (document.createEvent) {
+              let event = document.createEvent('MouseEvents');
+              event.initEvent('click', true, true);
+              a.dispatchEvent(event);
+            } else {
+              a.click();
+            }
+          }  
+        }
+			
+        // Read from the <td> css class the pixels that need to be on
+        // generates the jump table and font data 
+        generate() {
+          Font.emptyOutput();
+          let chars = this.fontContainer.getElementsByTagName('table');
+          let firstCharCode = parseInt(document.getElementById('code').value);
+          let name = document.getElementById('name').value.replace(/[^a-zA-Z0-9_$]/g, '_');
+          
+          let bits2add = this.bytesForHeight*8 - this.height;  // number of missing bits to fill leftmost byte
+          let charCount = chars.length;
+          let charAddr = 0;
+          // Comments are used when parsing back a generated font
+          Font.output('jump', '  // Jump Table:');
+          Font.output('data', '  // Font Data:');
+          // Browse each character
+          for(let ch = 0; ch < charCount; ch++) {
+            // Fix renumbering in case first char code was modified  
+            Font.updateCaption(chars[ch].getElementsByTagName('caption')[0], ch + firstCharCode);
+            let charBytes = [];
+            let charCode = ch + firstCharCode;
+            let rows = chars[ch].getElementsByTagName('tr');
+            let notZero = false;
+            // Browse each column
+            for(let col = 0; col < this.width ; col++) {
+              let bits = ""; // using string because js uses 32b ints when performing bit operations
+              // Browse each row starting from the bottom one, going up, and accumulate pixels in
+              // a string: this rotates the glyph
+              // Beware, row 0 is table headers.
+              for(let r = rows.length-1; r >=1 ; r--) {
+                let pixelState = rows[r].children[col].className;
+                bits += (pixelState === 'on' ? '1': '0');           
+              }
+              // Need to complete missing bits to have a sizeof byte multiple number of bits
+              for(let b = 0; b < bits2add; b++) {
+                bits =  '0' + bits;
+              }
+              // Font.output('data', `  // ${bits}`);  // Debugging help: rotated bitmap
+
+              // read bytes from the end
+              for(let b = bits.length - 1; b >= 7; b -= 8) {
+                //Font.output('data', `  // ${bits.substr(b-7, 8)}`);  // Debugging help: rotated bitmap
+                let byte = parseInt(bits.substr(b-7, 8), 2);
+                if (byte !== 0) {
+                  notZero = true;
+                }
+                charBytes.push(Font.toHexString(byte));
+              } 
+            }
+            // Remove bytes with value 0 at the end of the array.
+            while(parseInt(charBytes[charBytes.length-1]) === 0 && charBytes.length !== 1) {
+              charBytes.pop();
+            }
+            
+            if (notZero) {
+              Font.output('data', `  ${charBytes.join(', ')},  // ${charCode}`);
+              // TODO: last param width is not the best value. Need to compute the actual occupied width
+              Font.output('jump', `  ${Font.getMsB(charAddr)}, ${Font.getLsB(charAddr)}, ${Font.toHexString(charBytes.length)}, ${Font.toHexString(this.width)},  // ${charCode} `);
+              charAddr += charBytes.length;
+            } else {
+              Font.output('jump', `  0xFF, 0xFF, 0x00, ${Font.toHexString(this.width)},  // ${charCode} `);
+            }
+          }
+          Font.output('data', '};');
+          
+          Font.output('header', "// Font generated or edited with the glyphEditor");
+          Font.output('header', `const char ${name}[] PROGMEM = {`);
+          // Comments are used when parsing back a generated font
+          Font.output('header', `  ${Font.toHexString(this.width)}, // Width: ${this.width}`);
+          Font.output('header', `  ${Font.toHexString(this.height)}, // Height: ${this.height}`);          
+          Font.output('header', `  ${Font.toHexString(firstCharCode)}, // First char: ${firstCharCode}`);
+          Font.output('header', `  ${Font.toHexString(charCount)}, // Number of chars: ${charCount}`);
+        }
+      }
+	  
+      document.getElementById('fileinput').addEventListener('change', function(e) {
+        let f = e.target.files[0]; 
+        if (f) {
+          let r = new FileReader();
+          r.onload = function(e) { 
+            let contents = e.target.result;
+                alert( "Got the file.\n" 
+                +"name: " + f.name + "\n"
+                +"type: " + f.type + "\n"
+                +"size: " + f.size + " bytes\n"
+                +"starts with: " + contents.substr(0, contents.indexOf("\n")) 
+            );
+              document.getElementById("inputText").value = contents;
+          };
+          r.readAsText(f);
+        } else { 
+          alert("Failed to load file");
+        }
+        });
+      
+      document.getElementById('savetoFile').addEventListener('click', function() {
+        font.saveFile();
+      });
+      
+      document.getElementById('generate').addEventListener('click', function() {
+        font.generate();
+      });
+      document.getElementById('addChar').addEventListener('click', function() {
+        font.addChar();
+      });
+      document.getElementById('inputText').addEventListener('click', function(e) {
+        let target = e.target;
+        target.select();
+      });
+      document.getElementById('chars').addEventListener('mousedown', function(e) {
+        if (e.button !== 0) return;
+        let target = e.target;
+        let action = target.getAttribute('action') || '';
+        if (action === '') return;
+        let result, code, nextContainer, previousContainer, pixels ;
+        let currentContainer = target.parentNode.parentNode;
+        switch(action) {
+          case 'add':
+            code = currentContainer.getAttribute('code');
+            nextContainer = font.addChar();
+            currentContainer.parentNode.insertBefore(nextContainer, currentContainer);
+            do {
+              nextContainer.setAttribute('code', code);
+              Font.updateCaption(nextContainer.getElementsByTagName('caption')[0], code);
+              code ++;
+            } while (nextContainer = nextContainer.nextSibling);
+            break;
+
+          case 'delete':
+            result = confirm("Delete this character ?");
+            if (!result) return;
+            code = currentContainer.getAttribute('code');
+            nextContainer = currentContainer;
+            while (nextContainer = nextContainer.nextSibling) {
+              nextContainer.setAttribute('code', code);
+              Font.updateCaption(nextContainer.getElementsByTagName('caption')[0], code);
+              code ++;
+            }
+            currentContainer.parentNode.removeChild(currentContainer);
+            break;
+
+          // Shift pixels to the left  
+          case 'left':
+            pixels = currentContainer.getElementsByTagName('td');
+            for(p = 0; p < pixels.length; p++) {
+              if((p + 1) % font.width) {
+                pixels[p].className = pixels[p + 1].className;
+              } else {
+                pixels[p].className = '';
+              }
+            }
+            break;
+          
+          // Shift pixels to the right
+          case 'right':
+            pixels = currentContainer.getElementsByTagName('td');
+            for(p = pixels.length-1; p >=0 ; p--) {
+              if(p % font.width) {
+                pixels[p].className = pixels[p - 1].className;
+              } else {
+                pixels[p].className = '';
+              }
+            }
+            break;
+
+          // Shift pixels down
+          case 'down':
+            pixels = currentContainer.getElementsByTagName('td');
+            for(p = pixels.length-1; p >=0 ; p--) {
+              if(p >= font.width) {
+                pixels[p].className = pixels[p - font.width].className;
+              } else {
+                pixels[p].className = '';
+              }
+            }            break;
+
+          // Shift pixels up
+          case 'up':
+            pixels = currentContainer.getElementsByTagName('td');
+            for(p = 0; p < pixels.length; p++) {
+              if(p < font.width*(font.height -1)) {
+                pixels[p].className = pixels[p + font.width].className;
+              } else {
+                pixels[p].className = '';
+              }
+            }            
+            break;
+
+          case 'toggle':
+            Font.togglePixel(target);
+            break;
+
+          case 'clean':
+            result = confirm("Delete the pixels ?");
+            if (!result) return;
+            pixels = currentContainer.getElementsByTagName('td');
+            for (let p = 0; p < pixels.length; p++) {
+              pixels[p].className = '';
+            }
+            break;
+          
+          case 'copy':
+             let charNumber = parseInt(prompt("Source char #: "));
+             let chars = font.fontContainer.getElementsByTagName('table');
+             let tableOffset = charNumber - parseInt(document.getElementById('code').value);
+             let srcPixels = chars[tableOffset].getElementsByTagName('td');
+             let targetPixels = currentContainer.getElementsByTagName('td');
+             for(let i=0; i < srcPixels.length; i++) {
+               // First tds are in the th row, for editing actions. Skip them
+               if (targetPixels[i].parentNode.localName === 'th') continue; // In case we give them css at some point...
+               targetPixels[i].className = srcPixels[i].className;
+             }
+            break;
+          default:
+            // no.
+        }
+        
+      });
+      document.getElementById('chars').addEventListener('mouseover', function(e) {
+        let target = e.target;
+        let action = target.getAttribute('action');
+        if (action !== 'toggle' || e.buttons !== 1) return;
+        Font.togglePixel(target);
+      });
+      document.getElementById('chars').addEventListener('dragstart', function() {
+        return false;
+      });
+
+      document.getElementById('create').addEventListener('click', function() {
+      	font = new Font();
+        font.addChar();
+      });
+
+      // parse a char array declaration for an existing font.
+      // parsing heavily relies on comments.
+      document.getElementById('parse').addEventListener('click', function() {
+        if (document.getElementById('chars').childElementCount) {
+          let result = confirm("Confirm you want to overwrite the existing grids ?");
+          if (!result) return;          
+        } 
+        let lines = document.getElementById('inputText').value.split('\n');
+        let name = '';
+        let height = 0;
+        let width = 0;
+        let firstCharCode = 0;
+        let jumpTable = [];
+        let charData = [];
+        let readingJumpTable = false;
+        let readingData = false;
+        
+        for(let l = 0 ; l < lines.length; l++) {
+          // TODO ? keep C compilation directives to copy them (not lowercased :)) to newly generated char array
+          let line = lines[l].trim();
+          //alert(line);
+          let fields;
+
+          // Declaration line: grab the name
+          if (line.indexOf('PROGMEM') > 0) {
+            fields = line.split(' ');
+            name = fields[2].replace(/[\[\]]/g, '');
+            continue;
+          }
+          line = line.toLowerCase();
+          // Todo: could rely on line order...
+          // width line: grab the width
+          if (line.indexOf('width') > 0) {
+            fields = line.split(',');
+            width = fields[0];
+            continue;
+          }
+          // height line: grab the height 
+          if (line.indexOf('height') > 0) {
+            fields = line.split(',');
+            height = fields[0];
+            continue;
+          }
+          // first char code line: grab the first char code 
+          if (line.indexOf('first char') > 0) {
+            fields = line.split(',');
+            firstCharCode = fields[0];
+            continue;
+          }
+          // End of array declaration
+          // TODO warn if more lines: next fonts are ignored
+          if (line.indexOf('};') === 0) {
+            break;
+          }
+          
+          if (readingJumpTable || readingData) {
+            if (line.indexOf('#') !== 0 && line.length !== 0 && line.indexOf('//') !== 0) {
+              line = line.replace(/\/\/.*/, ''); // get rid of end of line comments
+              fields = line.split(',');
+              let newEntry = [];
+              for(let f=0; f < fields.length; f++) {
+                let value = parseInt(fields[f]);
+                if (isNaN(value)) continue;
+                if (readingData) {
+                  charData.push(value);
+                }
+                if (readingJumpTable) {
+                  newEntry.push(value);
+                }
+              }
+              if (readingJumpTable) {
+                jumpTable.push(newEntry);
+              }
+            }            
+          }
+          
+          // Begining of data
+          if (line.indexOf('font data') > 0) {
+            readingData = true;
+            readingJumpTable = false;
+          }
+          // Begining of jump table
+          if (line.indexOf('jump table') > 0) {
+            readingJumpTable = true;
+          }
+        }
+        if (!name || !height || !width || !firstCharCode) {
+          alert("Does not look like a parsable font. Try again.");
+          return;
+        }
+        
+        Font.emptyChars();
+        Font.emptyOutput();
+        document.getElementById('name').value = name;
+        document.getElementById('height').value = parseInt(height);
+        document.getElementById('width').value = parseInt(width);
+        document.getElementById('code').value = parseInt(firstCharCode);
+      	font = new Font();
+      	for(let c = 0 ; c < jumpTable.length; c++) {
+      	  let jumpEntry = jumpTable[c];
+      	  let charEntry = [];
+      	  // displayable character 
+      	  if (jumpEntry[0] !== 255 || jumpEntry[1] !== 255) {
+      	    charEntry = charData.splice(0, jumpEntry[2]);
+          }
+      	  font.addChar(jumpEntry, charEntry);
+        }
+        document.body.className = "started";
+      });        
+      
+      })();
+    </script>  
+  </body>
+</html>
diff --git a/resources/glyphEditor.png b/resources/glyphEditor.png
new file mode 100644
index 0000000000000000000000000000000000000000..800efc56f4bd50ba347eab57f3205ec8d0c0dd31
Binary files /dev/null and b/resources/glyphEditor.png differ