Ver Fonte

preliminary

ZinggJM há 8 anos atrás
pai
commit
0081c5f363

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
 # GxEPD2
 # GxEPD2
-New Simplified Version of GxEPD for 32 Bit Arduino
+New Simplified Version of GxEPD for Arduino
 
 
 ## under construction
 ## under construction

+ 39 - 62
examples/GxEPD2_Example/GxEPD2_Example.ino

@@ -26,6 +26,9 @@
 // new mapping suggestion for STM32F1, e.g. STM32F103C8T6 "BluePill"
 // new mapping suggestion for STM32F1, e.g. STM32F103C8T6 "BluePill"
 // BUSY -> A1, RST -> A2, DC -> A3, CS-> A4, CLK -> A5, DIN -> A7
 // BUSY -> A1, RST -> A2, DC -> A3, CS-> A4, CLK -> A5, DIN -> A7
 
 
+// mapping suggestion for AVR, UNO, NANO etc.
+// BUSY -> 7, RST -> 9, DC -> 8, CS-> 10, CLK -> 13, DIN -> 11
+
 #include <GxEPD2_BW.h>
 #include <GxEPD2_BW.h>
 #include <GxEPD2_3C.h>
 #include <GxEPD2_3C.h>
 #include <Fonts/FreeMonoBold9pt7b.h>
 #include <Fonts/FreeMonoBold9pt7b.h>
@@ -38,6 +41,7 @@
 //GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> display(GxEPD2_270(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> display(GxEPD2_270(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 // can use only half buffer size
 // can use only half buffer size
+//GxEPD2_BW < GxEPD2_583, GxEPD2_583::HEIGHT / 2 > display(GxEPD2_583(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 // 3-color e-papers
 // 3-color e-papers
 //GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
@@ -47,6 +51,7 @@
 // can use only half buffer size
 // can use only half buffer size
 //GxEPD2_3C<GxEPD2_420c, GxEPD2_420c::HEIGHT / 2> display(GxEPD2_420c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_420c, GxEPD2_420c::HEIGHT / 2> display(GxEPD2_420c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 // can use only quarter buffer size
 // can use only quarter buffer size
+//GxEPD2_3C<GxEPD2_583c, GxEPD2_583c::HEIGHT / 4> display(GxEPD2_583c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_750c, GxEPD2_750c::HEIGHT / 4> display(GxEPD2_750c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_750c, GxEPD2_750c::HEIGHT / 4> display(GxEPD2_750c(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 #endif
 #endif
 
 
@@ -86,6 +91,25 @@
 //GxEPD2_3C<GxEPD2_750c, MAX_HEIGHT(GxEPD2_750c)> display(GxEPD2_750c(/*CS=4*/ SS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_750c, MAX_HEIGHT(GxEPD2_750c)> display(GxEPD2_750c(/*CS=4*/ SS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 #endif
 #endif
 
 
+#if defined(__AVR)
+#define MAX_DISPAY_BUFFER_SIZE 1000 // half of available RAM as a compromise
+#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPAY_BUFFER_SIZE / 8 / EPD::WIDTH ? EPD::HEIGHT : MAX_DISPAY_BUFFER_SIZE / 8 / EPD::WIDTH)
+// select one and adapt to your mapping
+//GxEPD2_BW<GxEPD2_154, MAX_HEIGHT(GxEPD2_154)> display(GxEPD2_154(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_BW<GxEPD2_213, MAX_HEIGHT(GxEPD2_213)> display(GxEPD2_213(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_BW<GxEPD2_290, MAX_HEIGHT(GxEPD2_290)> display(GxEPD2_290(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_BW<GxEPD2_270, MAX_HEIGHT(GxEPD2_270)> display(GxEPD2_270(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_BW<GxEPD2_420, MAX_HEIGHT(GxEPD2_420)> display(GxEPD2_420(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_BW<GxEPD2_750, MAX_HEIGHT(GxEPD2_750)> display(GxEPD2_750(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+// 3-color e-papers
+//GxEPD2_3C<GxEPD2_154c, MAX_HEIGHT(GxEPD2_154c)> display(GxEPD2_154c(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_3C<GxEPD2_213c, MAX_HEIGHT(GxEPD2_213c)> display(GxEPD2_213c(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_3C<GxEPD2_290c, MAX_HEIGHT(GxEPD2_290c)> display(GxEPD2_290c(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_3C<GxEPD2_270c, MAX_HEIGHT(GxEPD2_270c)> display(GxEPD2_270c(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_3C<GxEPD2_420c, MAX_HEIGHT(GxEPD2_420c)> display(GxEPD2_420c(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+//GxEPD2_3C<GxEPD2_750c, MAX_HEIGHT(GxEPD2_750c)> display(GxEPD2_750c(/*CS=10*/ SS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
+#endif
+
 void setup()
 void setup()
 {
 {
   Serial.begin(115200);
   Serial.begin(115200);
@@ -319,56 +343,6 @@ void showPartialUpdate()
     while (display.nextPage());
     while (display.nextPage());
     delay(1000);
     delay(1000);
   }
   }
-  // check box on right side corner
-  box_x = display.height() - box_x - box_w - 1; // not valid for all corners
-  // should show on right side of long side
-  // show where the update box is
-  for (uint16_t r = 0; r < 4; r++)
-  {
-    display.setRotation(r);
-    display.setPartialWindow(box_x, box_y, box_w, box_h);
-    display.firstPage();
-    do
-    {
-      display.fillRect(box_x, box_y, box_w, box_h, GxEPD_BLACK);
-    }
-    while (display.nextPage());
-    delay(1000);
-    display.firstPage();
-    do
-    {
-      display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE);
-    }
-    while (display.nextPage());
-    delay(1000);
-  }
-  // show updates in the update box
-  for (uint16_t r = 0; r < 4; r++)
-  {
-    display.setRotation(r);
-    display.setPartialWindow(box_x, box_y, box_w, box_h);
-    if (box_x >= display.width()) continue; // avoid delay
-    for (uint16_t i = 1; i <= 10; i += incr)
-    {
-      display.firstPage();
-      do
-      {
-        display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE);
-        display.setCursor(box_x, cursor_y);
-        display.print(value * i, 2);
-      }
-      while (display.nextPage());
-      delay(500);
-    }
-    delay(1000);
-    display.firstPage();
-    do
-    {
-      display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE);
-    }
-    while (display.nextPage());
-    delay(1000);
-  }
 }
 }
 
 
 // comment out unused bitmaps to reduce code space used
 // comment out unused bitmaps to reduce code space used
@@ -388,9 +362,7 @@ void showPartialUpdate()
 
 
 void drawBitmaps()
 void drawBitmaps()
 {
 {
-#ifdef _GxBitmaps200x200_H_
-  drawBitmaps200x200();
-#endif
+  display.setFullWindow();
 #ifdef _GxBitmaps128x250_H_
 #ifdef _GxBitmaps128x250_H_
   drawBitmaps128x250();
   drawBitmaps128x250();
 #endif
 #endif
@@ -407,9 +379,6 @@ void drawBitmaps()
   drawBitmaps640x384();
   drawBitmaps640x384();
 #endif
 #endif
   // 3-color
   // 3-color
-#ifdef _GxBitmaps3c200x200_H_
-  drawBitmaps3c200x200();
-#endif
 #ifdef _GxBitmaps3c104x212_H_
 #ifdef _GxBitmaps3c104x212_H_
   drawBitmaps3c104x212();
   drawBitmaps3c104x212();
 #endif
 #endif
@@ -422,6 +391,14 @@ void drawBitmaps()
 #ifdef _GxBitmaps3c400x300_H_
 #ifdef _GxBitmaps3c400x300_H_
   drawBitmaps3c400x300();
   drawBitmaps3c400x300();
 #endif
 #endif
+// show these after the specific bitmaps
+#ifdef _GxBitmaps200x200_H_
+  drawBitmaps200x200();
+#endif
+  // 3-color
+#ifdef _GxBitmaps3c200x200_H_
+  drawBitmaps3c200x200();
+#endif
 }
 }
 
 
 #ifdef _GxBitmaps200x200_H_
 #ifdef _GxBitmaps200x200_H_
@@ -450,7 +427,7 @@ void drawBitmaps200x200()
   //else
   //else
   {
   {
     bool mirror_y = (display.epd2.panel != GxEPD2::GDE0213B1);
     bool mirror_y = (display.epd2.panel != GxEPD2::GDE0213B1);
-    display.clearScreen(0xFF);
+    display.clearScreen(); // use default for white
     uint16_t x = (display.epd2.WIDTH - 200) / 2;
     uint16_t x = (display.epd2.WIDTH - 200) / 2;
     uint16_t y = (display.epd2.HEIGHT - 200) / 2;
     uint16_t y = (display.epd2.HEIGHT - 200) / 2;
     for (uint16_t i = 0; i < sizeof(bitmaps) / sizeof(char*); i++)
     for (uint16_t i = 0; i < sizeof(bitmaps) / sizeof(char*); i++)
@@ -466,7 +443,7 @@ void drawBitmaps200x200()
     int16_t y = -60;
     int16_t y = -60;
     for (uint16_t j = 0; j < 10; j++)
     for (uint16_t j = 0; j < 10; j++)
     {
     {
-      display.writeScreenBuffer(0xFF);
+      display.writeScreenBuffer(); // use default for white
       display.writeImage(bitmaps[i], x, y, 200, 200, false, mirror_y, true);
       display.writeImage(bitmaps[i], x, y, 200, 200, false, mirror_y, true);
       display.refresh(true);
       display.refresh(true);
       delay(2000);
       delay(2000);
@@ -477,7 +454,7 @@ void drawBitmaps200x200()
     if (!display.epd2.hasFastPartialUpdate) break; // comment out for full show
     if (!display.epd2.hasFastPartialUpdate) break; // comment out for full show
     break; // comment out for full show
     break; // comment out for full show
   }
   }
-  display.writeScreenBuffer(0xFF);
+  display.writeScreenBuffer(); // use default for white
   display.writeImage(bitmaps[0], 0, 0, 200, 200, false, mirror_y, true);
   display.writeImage(bitmaps[0], 0, 0, 200, 200, false, mirror_y, true);
   display.writeImage(bitmaps[0], display.epd2.WIDTH - 200, display.epd2.HEIGHT - 200, 200, 200, false, mirror_y, true);
   display.writeImage(bitmaps[0], display.epd2.WIDTH - 200, display.epd2.HEIGHT - 200, 200, 200, false, mirror_y, true);
   display.refresh(true);
   display.refresh(true);
@@ -671,7 +648,7 @@ void drawBitmaps3c200x200()
   }
   }
   if (display.epd2.hasColor)
   if (display.epd2.hasColor)
   {
   {
-    display.clearScreen(0xFF);
+    display.clearScreen(); // use default for white
     uint16_t x = (display.epd2.WIDTH - 200) / 2;
     uint16_t x = (display.epd2.WIDTH - 200) / 2;
     uint16_t y = (display.epd2.HEIGHT - 200) / 2;
     uint16_t y = (display.epd2.HEIGHT - 200) / 2;
     for (uint16_t i = 0; i < sizeof(bitmap_pairs) / sizeof(bitmap_pair); i++)
     for (uint16_t i = 0; i < sizeof(bitmap_pairs) / sizeof(bitmap_pair); i++)
@@ -685,7 +662,7 @@ void drawBitmaps3c200x200()
       int16_t y = -60;
       int16_t y = -60;
       for (uint16_t j = 0; j < 10; j++)
       for (uint16_t j = 0; j < 10; j++)
       {
       {
-        display.writeScreenBuffer(0xFF);
+        display.writeScreenBuffer(); // use default for white
         display.writeImage(bitmap_pairs[i].black, bitmap_pairs[i].red, x, y, 200, 200, false, false, true);
         display.writeImage(bitmap_pairs[i].black, bitmap_pairs[i].red, x, y, 200, 200, false, false, true);
         display.refresh();
         display.refresh();
         delay(2000);
         delay(2000);
@@ -694,7 +671,7 @@ void drawBitmaps3c200x200()
         if ((x >= display.epd2.WIDTH) || (y >= display.epd2.HEIGHT)) break;
         if ((x >= display.epd2.WIDTH) || (y >= display.epd2.HEIGHT)) break;
       }
       }
     }
     }
-    display.writeScreenBuffer(0xFF);
+    display.writeScreenBuffer(); // use default for white
     display.writeImage(bitmap_pairs[0].black, bitmap_pairs[0].red, 0, 0, 200, 200, false, false, true);
     display.writeImage(bitmap_pairs[0].black, bitmap_pairs[0].red, 0, 0, 200, 200, false, false, true);
     display.writeImage(bitmap_pairs[0].black, bitmap_pairs[0].red, display.epd2.WIDTH - 200, display.epd2.HEIGHT - 200, 200, 200, false, false, true);
     display.writeImage(bitmap_pairs[0].black, bitmap_pairs[0].red, display.epd2.WIDTH - 200, display.epd2.HEIGHT - 200, 200, 200, false, false, true);
     display.refresh();
     display.refresh();

+ 1 - 0
src/GxEPD2_3C.h

@@ -19,6 +19,7 @@
 #include "epd3c/GxEPD2_290c.h"
 #include "epd3c/GxEPD2_290c.h"
 #include "epd3c/GxEPD2_270c.h"
 #include "epd3c/GxEPD2_270c.h"
 #include "epd3c/GxEPD2_420c.h"
 #include "epd3c/GxEPD2_420c.h"
+#include "epd3c/GxEPD2_583c.h"
 #include "epd3c/GxEPD2_750c.h"
 #include "epd3c/GxEPD2_750c.h"
 
 
 template<typename GxEPD2_Type, const uint16_t page_height>
 template<typename GxEPD2_Type, const uint16_t page_height>

+ 1 - 0
src/GxEPD2_BW.h

@@ -19,6 +19,7 @@
 #include "epd/GxEPD2_290.h"
 #include "epd/GxEPD2_290.h"
 #include "epd/GxEPD2_270.h"
 #include "epd/GxEPD2_270.h"
 #include "epd/GxEPD2_420.h"
 #include "epd/GxEPD2_420.h"
+#include "epd/GxEPD2_583.h"
 #include "epd/GxEPD2_750.h"
 #include "epd/GxEPD2_750.h"
 
 
 template<typename GxEPD2_Type, const uint16_t page_height>
 template<typename GxEPD2_Type, const uint16_t page_height>

+ 23 - 19
src/GxEPD2_EPD.cpp

@@ -61,32 +61,36 @@ void GxEPD2_EPD::init(uint32_t serial_diag_bitrate)
 #endif
 #endif
 }
 }
 
 
-void GxEPD2_EPD::_waitWhileBusy(const char* comment)
+void GxEPD2_EPD::_waitWhileBusy(const char* comment, uint16_t busy_time)
 {
 {
-  unsigned long start = micros();
-  while (1)
+  if (_busy >= 0)
   {
   {
-    if (digitalRead(_busy) != _busy_level) break;
-    delay(1);
-    if (micros() - start > _busy_timeout)
+    unsigned long start = micros();
+    while (1)
     {
     {
-      Serial.println("Busy Timeout!");
-      break;
+      if (digitalRead(_busy) != _busy_level) break;
+      delay(1);
+      if (micros() - start > _busy_timeout)
+      {
+        Serial.println("Busy Timeout!");
+        break;
+      }
     }
     }
-  }
-  if (comment)
-  {
-#if !defined(DISABLE_DIAGNOSTIC_OUTPUT)
-    if (_diag_enabled)
+    if (comment)
     {
     {
-      unsigned long elapsed = micros() - start;
-      Serial.print(comment);
-      Serial.print(" : ");
-      Serial.println(elapsed);
-    }
+#if !defined(DISABLE_DIAGNOSTIC_OUTPUT)
+      if (_diag_enabled)
+      {
+        unsigned long elapsed = micros() - start;
+        Serial.print(comment);
+        Serial.print(" : ");
+        Serial.println(elapsed);
+      }
 #endif
 #endif
+    }
+    (void) start;
   }
   }
-  (void) start;
+  else delay(busy_time);
 }
 }
 
 
 void GxEPD2_EPD::_writeCommand(uint8_t c)
 void GxEPD2_EPD::_writeCommand(uint8_t c)

+ 11 - 9
src/GxEPD2_EPD.h

@@ -29,18 +29,20 @@ class GxEPD2
   public:
   public:
     enum Panel
     enum Panel
     {
     {
-      GDEP015OC1, WS_1_54_bw = GDEP015OC1,
-      GDE0213B1,  WS_2_13_bw = GDE0213B1,
-      GDEH029A1,  WS_2_9_bw = GDEH029A1,
-      GDEW027W3,  WS_2_7_bw = GDEW027W3,
-      GDEW042T2,  WS_4_2_bw = GDEW042T2,
-      GDEW075T8,  WS_7_5_bw = GDEW075T8,
+      GDEP015OC1, Waveshare_1_54_bw = GDEP015OC1,
+      GDE0213B1,  Waveshare_2_13_bw = GDE0213B1,
+      GDEH029A1,  Waveshare_2_9_bw = GDEH029A1,
+      GDEW027W3,  Waveshare_2_7_bw = GDEW027W3,
+      GDEW042T2,  Waveshare_4_2_bw = GDEW042T2,
+      GDEW0583T7, Waveshare_5_83_bw = GDEW0583T7,
+      GDEW075T8,  Waveshare_7_5_bw = GDEW075T8,
       // 3-color
       // 3-color
       GDEW0154Z04, Waveshare_1_54_bwr = GDEW0154Z04,
       GDEW0154Z04, Waveshare_1_54_bwr = GDEW0154Z04,
       GDEW0213Z16, Waveshare_2_13_bwr = GDEW0213Z16,
       GDEW0213Z16, Waveshare_2_13_bwr = GDEW0213Z16,
       GDEW029Z10,  Waveshare_2_9_bwr = GDEW029Z10,
       GDEW029Z10,  Waveshare_2_9_bwr = GDEW029Z10,
       GDEW027C44,  Waveshare_2_7_bwr = GDEW027C44,
       GDEW027C44,  Waveshare_2_7_bwr = GDEW027C44,
       GDEW042Z15,  Waveshare_4_2_bwr = GDEW042Z15,
       GDEW042Z15,  Waveshare_4_2_bwr = GDEW042Z15,
+      GDEW0583Z21, Waveshare_5_83_bwr = GDEW0583Z21,
       GDEW075Z09,  Waveshare_7_5_bwr = GDEW075Z09
       GDEW075Z09,  Waveshare_7_5_bwr = GDEW075Z09
     };
     };
 };
 };
@@ -51,8 +53,8 @@ class GxEPD2_EPD
     GxEPD2_EPD(int8_t cs, int8_t dc, int8_t rst, int8_t busy, int8_t busy_level, uint32_t busy_timeout);
     GxEPD2_EPD(int8_t cs, int8_t dc, int8_t rst, int8_t busy, int8_t busy_level, uint32_t busy_timeout);
     virtual void init(uint32_t serial_diag_bitrate = 0) = 0; // serial_diag_bitrate = 0 : disabled
     virtual void init(uint32_t serial_diag_bitrate = 0) = 0; // serial_diag_bitrate = 0 : disabled
     //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
     //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
-    virtual void clearScreen(uint8_t value = 0xFF) = 0; // init controller memory and screen (default white)
-    virtual void writeScreenBuffer(uint8_t value = 0xFF) = 0; // init controller memory (default white)
+    virtual void clearScreen(uint8_t value) = 0; // init controller memory and screen (default white)
+    virtual void writeScreenBuffer(uint8_t value) = 0; // init controller memory (default white)
     // write to controller memory, without screen refresh; x and w should be multiple of 8
     // write to controller memory, without screen refresh; x and w should be multiple of 8
     virtual void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
     virtual void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
     virtual void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
     virtual void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) = 0;
@@ -70,7 +72,7 @@ class GxEPD2_EPD
       return (a > b ? a : b);
       return (a > b ? a : b);
     };
     };
   protected:
   protected:
-    void _waitWhileBusy(const char* comment = 0);
+    void _waitWhileBusy(const char* comment = 0, uint16_t busy_time = 5000);
     void _writeCommand(uint8_t c);
     void _writeCommand(uint8_t c);
     void _writeData(uint8_t d);
     void _writeData(uint8_t d);
     void _writeData(const uint8_t* data, uint16_t n);
     void _writeData(const uint8_t* data, uint16_t n);

+ 14 - 11
src/epd/GxEPD2_154.cpp

@@ -34,7 +34,7 @@ void GxEPD2_154::clearScreen(uint8_t value)
     _Init_Full();
     _Init_Full();
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _writeCommand(0x24);
     _writeCommand(0x24);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -45,7 +45,7 @@ void GxEPD2_154::clearScreen(uint8_t value)
     if (!_using_partial_mode) _Init_Part();
     if (!_using_partial_mode) _Init_Part();
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _writeCommand(0x24);
     _writeCommand(0x24);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -54,7 +54,7 @@ void GxEPD2_154::clearScreen(uint8_t value)
   if (!_using_partial_mode) _Init_Part();
   if (!_using_partial_mode) _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x24);
   _writeCommand(0x24);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -73,7 +73,7 @@ void GxEPD2_154::_writeScreenBuffer(uint8_t value)
   if (!_using_partial_mode) _Init_Part();
   if (!_using_partial_mode) _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x24);
   _writeCommand(0x24);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -81,8 +81,10 @@ void GxEPD2_154::_writeScreenBuffer(uint8_t value)
 
 
 void GxEPD2_154::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_154::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -100,8 +102,8 @@ void GxEPD2_154::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     for (int16_t j = 0; j < w1 / 8; j++)
     for (int16_t j = 0; j < w1 / 8; j++)
     {
     {
       uint8_t data;
       uint8_t data;
-      // use w, h of bitmap for index!
-      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
       if (pgm)
       if (pgm)
       {
       {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -118,6 +120,7 @@ void GxEPD2_154::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
       else _writeData(data);
       else _writeData(data);
     }
     }
   }
   }
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_154::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_154::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -210,7 +213,7 @@ void GxEPD2_154::_PowerOn()
     _writeCommand(0x22);
     _writeCommand(0x22);
     _writeData(0xc0);
     _writeData(0xc0);
     _writeCommand(0x20);
     _writeCommand(0x20);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -220,7 +223,7 @@ void GxEPD2_154::_PowerOff()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0xc3);
   _writeData(0xc3);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
   _using_partial_mode = false;
   _using_partial_mode = false;
 }
 }
@@ -265,7 +268,7 @@ void GxEPD2_154::_Update_Full()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0xc4);
   _writeData(0xc4);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
   _writeCommand(0xff);
   _writeCommand(0xff);
 }
 }
 
 
@@ -274,7 +277,7 @@ void GxEPD2_154::_Update_Part()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0x04);
   _writeData(0x04);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
   _writeCommand(0xff);
   _writeCommand(0xff);
 }
 }
 
 

+ 4 - 0
src/epd/GxEPD2_154.h

@@ -24,6 +24,10 @@ class GxEPD2_154 : public GxEPD2_EPD
     static const bool hasColor = false;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
+    static const uint16_t power_on_time = 80; // ms, e.g. 73508us
+    static const uint16_t power_off_time = 80; // ms, e.g. 68982us
+    static const uint16_t full_refresh_time = 1200; // ms, e.g. 1113273us
+    static const uint16_t partial_refresh_time = 300; // ms, e.g. 290867us
     // constructor
     // constructor
     GxEPD2_154(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_154(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 15 - 12
src/epd/GxEPD2_213.cpp

@@ -34,7 +34,7 @@ void GxEPD2_213::clearScreen(uint8_t value)
     _Init_Full();
     _Init_Full();
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _writeCommand(0x24);
     _writeCommand(0x24);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -45,7 +45,7 @@ void GxEPD2_213::clearScreen(uint8_t value)
     if (!_using_partial_mode) _Init_Part();
     if (!_using_partial_mode) _Init_Part();
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _writeCommand(0x24);
     _writeCommand(0x24);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -54,7 +54,7 @@ void GxEPD2_213::clearScreen(uint8_t value)
   if (!_using_partial_mode) _Init_Part();
   if (!_using_partial_mode) _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x24);
   _writeCommand(0x24);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -73,7 +73,7 @@ void GxEPD2_213::_writeScreenBuffer(uint8_t value)
   if (!_using_partial_mode) _Init_Part();
   if (!_using_partial_mode) _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x24);
   _writeCommand(0x24);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -81,8 +81,10 @@ void GxEPD2_213::_writeScreenBuffer(uint8_t value)
 
 
 void GxEPD2_213::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_213::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -100,8 +102,8 @@ void GxEPD2_213::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     for (int16_t j = 0; j < w1 / 8; j++)
     for (int16_t j = 0; j < w1 / 8; j++)
     {
     {
       uint8_t data;
       uint8_t data;
-      // use w, h of bitmap for index!
-      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
       if (pgm)
       if (pgm)
       {
       {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -118,6 +120,7 @@ void GxEPD2_213::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
       else _writeData(data);
       else _writeData(data);
     }
     }
   }
   }
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_213::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_213::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -157,7 +160,7 @@ void GxEPD2_213::drawNative(const uint8_t* data1, const uint8_t* data2, int16_t
 void GxEPD2_213::refresh(bool partial_update_mode)
 void GxEPD2_213::refresh(bool partial_update_mode)
 {
 {
   if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
   if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
-  else 
+  else
   {
   {
     if (_using_partial_mode) _Init_Full();
     if (_using_partial_mode) _Init_Full();
     _Update_Full();
     _Update_Full();
@@ -210,7 +213,7 @@ void GxEPD2_213::_PowerOn()
     _writeCommand(0x22);
     _writeCommand(0x22);
     _writeData(0xc0);
     _writeData(0xc0);
     _writeCommand(0x20);
     _writeCommand(0x20);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -220,7 +223,7 @@ void GxEPD2_213::_PowerOff()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0xc3);
   _writeData(0xc3);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
   _using_partial_mode = false;
   _using_partial_mode = false;
 }
 }
@@ -265,7 +268,7 @@ void GxEPD2_213::_Update_Full()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0xc4);
   _writeData(0xc4);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
   _writeCommand(0xff);
   _writeCommand(0xff);
 }
 }
 
 
@@ -274,7 +277,7 @@ void GxEPD2_213::_Update_Part()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0x04);
   _writeData(0x04);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
   _writeCommand(0xff);
   _writeCommand(0xff);
 }
 }
 
 

+ 4 - 0
src/epd/GxEPD2_213.h

@@ -24,6 +24,10 @@ class GxEPD2_213 : public GxEPD2_EPD
     static const bool hasColor = false;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
+    static const uint16_t power_on_time = 80; // ms, e.g. 72961us
+    static const uint16_t power_off_time = 140; // ms, e.g. 135839us
+    static const uint16_t full_refresh_time = 4000; // ms, e.g. 3883686us
+    static const uint16_t partial_refresh_time = 300; // ms, e.g. 268173us
     // constructor
     // constructor
     GxEPD2_213(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_213(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 16 - 13
src/epd/GxEPD2_270.cpp

@@ -31,12 +31,12 @@ void GxEPD2_270::clearScreen(uint8_t value)
 {
 {
   _Init_Part();
   _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
   _refreshWindow(0, 0, WIDTH, HEIGHT);
   _refreshWindow(0, 0, WIDTH, HEIGHT);
-  _waitWhileBusy("clearScreen");
+  _waitWhileBusy("clearScreen", full_refresh_time);
   _initial = false;
   _initial = false;
 }
 }
 
 
@@ -52,7 +52,7 @@ void GxEPD2_270::_writeScreenBuffer(uint8_t value)
   _Init_Part();
   _Init_Part();
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -61,8 +61,10 @@ void GxEPD2_270::_writeScreenBuffer(uint8_t value)
 
 
 void GxEPD2_270::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_270::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -79,8 +81,8 @@ void GxEPD2_270::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     for (int16_t j = 0; j < w1 / 8; j++)
     for (int16_t j = 0; j < w1 / 8; j++)
     {
     {
       uint8_t data;
       uint8_t data;
-      // use w, h of bitmap for index!
-      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
       if (pgm)
       if (pgm)
       {
       {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -97,6 +99,7 @@ void GxEPD2_270::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
       else _writeData(data);
       else _writeData(data);
     }
     }
   }
   }
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_270::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_270::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -154,7 +157,7 @@ void GxEPD2_270::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
   w1 -= x1 - x;
   w1 -= x1 - x;
   h1 -= y1 - y;
   h1 -= y1 - y;
   _refreshWindow(x1, y1, w1, h1);
   _refreshWindow(x1, y1, w1, h1);
-  _waitWhileBusy("refresh");
+  _waitWhileBusy("refresh", full_refresh_time);
 }
 }
 
 
 void GxEPD2_270::powerOff(void)
 void GxEPD2_270::powerOff(void)
@@ -196,7 +199,7 @@ void GxEPD2_270::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -204,7 +207,7 @@ void GxEPD2_270::_PowerOn()
 void GxEPD2_270::_PowerOff()
 void GxEPD2_270::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
   _using_partial_mode = false;
   _using_partial_mode = false;
 }
 }
@@ -296,14 +299,14 @@ void GxEPD2_270::_Init_Part()
 
 
 void GxEPD2_270::_Update_Full()
 void GxEPD2_270::_Update_Full()
 {
 {
-  _writeCommand(0x12);      //display refresh
-  _waitWhileBusy("_Update_Full");
+  _writeCommand(0x12); //display refresh
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_270::_Update_Part()
 void GxEPD2_270::_Update_Part()
 {
 {
-  _writeCommand(0x12);      //display refresh
-  _waitWhileBusy("_Update_Part");
+  _writeCommand(0x12); //display refresh
+  _waitWhileBusy("_Update_Part", full_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd/GxEPD2_270.h

@@ -24,6 +24,10 @@ class GxEPD2_270 : public GxEPD2_EPD
     static const bool hasColor = false;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 100; // ms, e.g. 98877us
+    static const uint16_t power_off_time = 30; // ms, e.g. 28405us
+    static const uint16_t full_refresh_time = 4000; // ms, e.g. 3776419us
+    static const uint16_t partial_refresh_time = 4000; // ms, e.g. 3776419us
     // constructor
     // constructor
     GxEPD2_270(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_270(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 14 - 11
src/epd/GxEPD2_290.cpp

@@ -34,7 +34,7 @@ void GxEPD2_290::clearScreen(uint8_t value)
     _Init_Full();
     _Init_Full();
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _writeCommand(0x24);
     _writeCommand(0x24);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -45,7 +45,7 @@ void GxEPD2_290::clearScreen(uint8_t value)
     if (!_using_partial_mode) _Init_Part();
     if (!_using_partial_mode) _Init_Part();
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _setPartialRamArea(0, 0, WIDTH, HEIGHT);
     _writeCommand(0x24);
     _writeCommand(0x24);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -54,7 +54,7 @@ void GxEPD2_290::clearScreen(uint8_t value)
   if (!_using_partial_mode) _Init_Part();
   if (!_using_partial_mode) _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x24);
   _writeCommand(0x24);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -73,7 +73,7 @@ void GxEPD2_290::_writeScreenBuffer(uint8_t value)
   if (!_using_partial_mode) _Init_Part();
   if (!_using_partial_mode) _Init_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x24);
   _writeCommand(0x24);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -81,8 +81,10 @@ void GxEPD2_290::_writeScreenBuffer(uint8_t value)
 
 
 void GxEPD2_290::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_290::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -100,8 +102,8 @@ void GxEPD2_290::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     for (int16_t j = 0; j < w1 / 8; j++)
     for (int16_t j = 0; j < w1 / 8; j++)
     {
     {
       uint8_t data;
       uint8_t data;
-      // use w, h of bitmap for index!
-      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
       if (pgm)
       if (pgm)
       {
       {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -118,6 +120,7 @@ void GxEPD2_290::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
       else _writeData(data);
       else _writeData(data);
     }
     }
   }
   }
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_290::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_290::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -210,7 +213,7 @@ void GxEPD2_290::_PowerOn()
     _writeCommand(0x22);
     _writeCommand(0x22);
     _writeData(0xc0);
     _writeData(0xc0);
     _writeCommand(0x20);
     _writeCommand(0x20);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -220,7 +223,7 @@ void GxEPD2_290::_PowerOff()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0xc3);
   _writeData(0xc3);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
   _using_partial_mode = false;
   _using_partial_mode = false;
 }
 }
@@ -265,7 +268,7 @@ void GxEPD2_290::_Update_Full()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0xc4);
   _writeData(0xc4);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
   _writeCommand(0xff);
   _writeCommand(0xff);
 }
 }
 
 
@@ -274,7 +277,7 @@ void GxEPD2_290::_Update_Part()
   _writeCommand(0x22);
   _writeCommand(0x22);
   _writeData(0x04);
   _writeData(0x04);
   _writeCommand(0x20);
   _writeCommand(0x20);
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
   _writeCommand(0xff);
   _writeCommand(0xff);
 }
 }
 
 

+ 4 - 0
src/epd/GxEPD2_290.h

@@ -24,6 +24,10 @@ class GxEPD2_290 : public GxEPD2_EPD
     static const bool hasColor = false;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
+    static const uint16_t power_on_time = 80; // ms, e.g. 72498us
+    static const uint16_t power_off_time = 100; // ms, e.g. 93329us
+    static const uint16_t full_refresh_time = 1600; // ms, e.g. 1575016us
+    static const uint16_t partial_refresh_time = 420; // ms, e.g. 412493us
     // constructor
     // constructor
     GxEPD2_290(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_290(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 14 - 11
src/epd/GxEPD2_420.cpp

@@ -33,7 +33,7 @@ void GxEPD2_420::clearScreen(uint8_t value)
   {
   {
     _Init_Full();
     _Init_Full();
     _writeCommand(0x13);
     _writeCommand(0x13);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
     {
     {
       _writeData(value);
       _writeData(value);
     }
     }
@@ -44,14 +44,14 @@ void GxEPD2_420::clearScreen(uint8_t value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
   _Update_Part();
   _Update_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -72,7 +72,7 @@ void GxEPD2_420::_writeScreenBuffer(uint8_t value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(value);
     _writeData(value);
   }
   }
@@ -81,8 +81,10 @@ void GxEPD2_420::_writeScreenBuffer(uint8_t value)
 
 
 void GxEPD2_420::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_420::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -101,8 +103,8 @@ void GxEPD2_420::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     for (int16_t j = 0; j < w1 / 8; j++)
     for (int16_t j = 0; j < w1 / 8; j++)
     {
     {
       uint8_t data;
       uint8_t data;
-      // use w, h of bitmap for index!
-      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
       if (pgm)
       if (pgm)
       {
       {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -120,6 +122,7 @@ void GxEPD2_420::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     }
     }
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_420::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_420::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -211,7 +214,7 @@ void GxEPD2_420::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -219,7 +222,7 @@ void GxEPD2_420::_PowerOn()
 void GxEPD2_420::_PowerOff()
 void GxEPD2_420::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
   _using_partial_mode = false;
   _using_partial_mode = false;
 }
 }
@@ -272,13 +275,13 @@ void GxEPD2_420::_Init_Part()
 void GxEPD2_420::_Update_Full()
 void GxEPD2_420::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_420::_Update_Part()
 void GxEPD2_420::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd/GxEPD2_420.h

@@ -24,6 +24,10 @@ class GxEPD2_420 : public GxEPD2_EPD
     static const bool hasColor = false;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
     static const bool hasFastPartialUpdate = true;
+    static const uint16_t power_on_time = 40; // ms, e.g. 36996us
+    static const uint16_t power_off_time = 42; // ms, e.g. 40026us
+    static const uint16_t full_refresh_time = 4200; // ms, e.g. 4108541us
+    static const uint16_t partial_refresh_time = 1000; // ms, e.g. 995320us
     // constructor
     // constructor
     GxEPD2_420(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_420(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 353 - 0
src/epd/GxEPD2_583.cpp

@@ -0,0 +1,353 @@
+// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
+// Requires HW SPI and Adafruit_GFX. Caution: these e-papers require 3.3V supply AND data lines!
+//
+// based on Demo Example from Good Display: http://www.good-display.com/download_list/downloadcategoryid=34&isMode=false.html
+//
+// Author: Jean-Marc Zingg
+//
+// Version: see library.properties
+//
+// Library: https://github.com/ZinggJM/GxEPD2
+
+#include "GxEPD2_583.h"
+#include "WaveTables.h"
+
+GxEPD2_583::GxEPD2_583(int8_t cs, int8_t dc, int8_t rst, int8_t busy) : GxEPD2_EPD(cs, dc, rst, busy, LOW, 20000000)
+{
+  _initial = true;
+  _power_is_on = false;
+  _using_partial_mode = false;
+}
+
+void GxEPD2_583::init(uint32_t serial_diag_bitrate)
+{
+  GxEPD2_EPD::init(serial_diag_bitrate);
+  _initial = true;
+  _power_is_on = false;
+  _using_partial_mode = false;
+}
+
+void GxEPD2_583::clearScreen(uint8_t value)
+{
+  if (value == 0xFF) value = 0x33; // white value for this controller
+  if (_initial)
+  {
+    _Init_Full();
+    _writeCommand(0x10);
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+    {
+      _writeData(value);
+    }
+    _Update_Full();
+    _initial = false;
+  }
+  if (!_using_partial_mode) _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _Update_Part();
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _Update_Part();
+  _writeCommand(0x92); // partial out
+  _initial = false;
+}
+
+void GxEPD2_583::writeScreenBuffer(uint8_t value)
+{
+  if (value == 0xFF) value = 0x33; // white value for this controller
+  if (_initial) clearScreen(value);
+  else _writeScreenBuffer(value);
+}
+
+void GxEPD2_583::_writeScreenBuffer(uint8_t value)
+{
+  if (!_using_partial_mode) _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _writeCommand(0x92); // partial out
+}
+
+void GxEPD2_583::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
+  x -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
+  int16_t x1 = x < 0 ? 0 : x; // limit
+  int16_t y1 = y < 0 ? 0 : y; // limit
+  int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+  int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+  int16_t dx = x1 - x;
+  int16_t dy = y1 - y;
+  w1 -= dx;
+  h1 -= dy;
+  if ((w1 <= 0) || (h1 <= 0)) return;
+  if (!_using_partial_mode) _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(x1, y1, w1, h1);
+  _writeCommand(0x10);
+  for (int16_t i = 0; i < h1; i++)
+  {
+    for (int16_t j = 0; j < w1 / 8; j++)
+    {
+      uint8_t data;
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
+      if (pgm)
+      {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+        data = pgm_read_byte(&bitmap[idx]);
+#else
+        data = bitmap[idx];
+#endif
+      }
+      else
+      {
+        data = bitmap[idx];
+      }
+      if (invert) data = ~data;
+      _send8pixel(~data);
+    }
+  }
+  _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+}
+
+void GxEPD2_583::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  if (black)
+  {
+    writeImage(black, x, y, w, h, invert, mirror_y, pgm);
+  }
+}
+
+void GxEPD2_583::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  if (data1)
+  {
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+    int16_t wb = (w + 1) / 2; // width bytes, bitmaps are padded
+    x -= x % 2; // byte boundary
+    w = wb * 2; // byte boundary
+    int16_t x1 = x < 0 ? 0 : x; // limit
+    int16_t y1 = y < 0 ? 0 : y; // limit
+    int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+    int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+    int16_t dx = x1 - x;
+    int16_t dy = y1 - y;
+    w1 -= dx;
+    h1 -= dy;
+    if ((w1 <= 0) || (h1 <= 0)) return;
+    if (!_using_partial_mode) _Init_Part();
+    _writeCommand(0x91); // partial in
+    _setPartialRamArea(x1, y1, w1, h1);
+    _writeCommand(0x10);
+    for (int16_t i = 0; i < h1; i++)
+    {
+      for (int16_t j = 0; j < w1 / 2; j++)
+      {
+        uint8_t data;
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 2 + ((h - 1 - (i + dy))) * wb : j + dx / 2 + (i + dy) * wb;
+        if (pgm)
+        {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+          data = pgm_read_byte(&data1[idx]);
+#else
+          data = data1[idx];
+#endif
+        }
+        else
+        {
+          data = data1[idx];
+        }
+        if (invert) data = ~data;
+        _writeData(data);
+      }
+    }
+    _writeCommand(0x92); // partial out
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  }
+}
+
+void GxEPD2_583::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
+  refresh(x, y, w, h);
+}
+
+void GxEPD2_583::drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
+  refresh(x, y, w, h);
+}
+
+void GxEPD2_583::drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
+  refresh(x, y, w, h);
+}
+
+void GxEPD2_583::refresh(bool partial_update_mode)
+{
+  if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
+  else
+  {
+    if (_using_partial_mode) _Init_Full();
+    _Update_Full();
+  }
+}
+
+void GxEPD2_583::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
+{
+  x -= x % 8; // byte boundary
+  w -= x % 8; // byte boundary
+  int16_t x1 = x < 0 ? 0 : x; // limit
+  int16_t y1 = y < 0 ? 0 : y; // limit
+  int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+  int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+  w1 -= x1 - x;
+  h1 -= y1 - y;
+  if (!_using_partial_mode) _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(x1, y1, w1, h1);
+  _Update_Part();
+  _writeCommand(0x92); // partial out
+}
+
+void GxEPD2_583::powerOff(void)
+{
+  _PowerOff();
+}
+
+void GxEPD2_583::_send8pixel(uint8_t data)
+{
+  for (uint8_t j = 0; j < 8; j++)
+  {
+    uint8_t t = data & 0x80 ? 0x00 : 0x03;
+    t <<= 4;
+    data <<= 1;
+    j++;
+    t |= data & 0x80 ? 0x00 : 0x03;
+    data <<= 1;
+    _writeData(t);
+  }
+}
+
+void GxEPD2_583::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
+{
+  uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
+  uint16_t ye = y + h - 1;
+  x &= 0xFFF8; // byte boundary
+  _writeCommand(0x90); // partial window
+  _writeData(x / 256);
+  _writeData(x % 256);
+  _writeData(xe / 256);
+  _writeData(xe % 256);
+  _writeData(y / 256);
+  _writeData(y % 256);
+  _writeData(ye / 256);
+  _writeData(ye % 256);
+  _writeData(0x01); // don't see any difference
+  //_writeData(0x00); // don't see any difference
+}
+
+void GxEPD2_583::_PowerOn()
+{
+  if (!_power_is_on)
+  {
+    _writeCommand(0x04);
+    _waitWhileBusy("_PowerOn", power_on_time);
+  }
+  _power_is_on = true;
+}
+
+void GxEPD2_583::_PowerOff()
+{
+  _writeCommand(0x02); // power off
+  _waitWhileBusy("_PowerOff", power_off_time);
+  _power_is_on = false;
+  _using_partial_mode = false;
+}
+
+void GxEPD2_583::_InitDisplay()
+{
+  if (!_power_is_on && (_rst >= 0))
+  {
+    digitalWrite(_rst, 0);
+    delay(10);
+    digitalWrite(_rst, 1);
+    delay(10);
+  }
+  _writeCommand(0x01);
+  _writeData (0x37); // POWER SETTING
+  _writeData (0x00);
+  _writeCommand(0X00); // PANNEL SETTING
+  _writeData(0xCF);
+  _writeData(0x08);
+  _writeCommand(0x06); // boost
+  _writeData (0xc7);
+  _writeData (0xcc);
+  _writeData (0x28);
+  _writeCommand(0x30); // PLL setting
+  //_writeData (0x39);   // PLL: 7s refresh
+  _writeData (0x3a); // PLL: 15s refresh
+  //_writeData (0x3c); // PLL: 30s refresh
+  _writeCommand(0X41); // TEMPERATURE SETTING
+  _writeData(0x00);
+  _writeCommand(0X50); // VCOM AND DATA INTERVAL SETTING
+  _writeData(0x77);
+  _writeCommand(0X60); // TCON SETTING
+  _writeData(0x22);
+  _writeCommand(0x61); // 600*448
+  _writeData (0x02);   // source 600
+  _writeData (0x58);
+  _writeData (0x01);   // gate 448
+  _writeData (0xc0);
+  _writeCommand(0X82); // VCOM VOLTAGE SETTING
+  _writeData(0x28);    // all temperature  range
+  _writeCommand(0xe5); // FLASH MODE
+  _writeData(0x03);
+}
+
+void GxEPD2_583::_Init_Full()
+{
+  _InitDisplay();
+  _PowerOn();
+  _using_partial_mode = false;
+}
+
+void GxEPD2_583::_Init_Part()
+{
+  _InitDisplay();
+  _PowerOn();
+  _using_partial_mode = true;
+}
+
+void GxEPD2_583::_Update_Full()
+{
+  _writeCommand(0x12); //display refresh
+  _waitWhileBusy("_Update_Full", full_refresh_time);
+}
+
+void GxEPD2_583::_Update_Part()
+{
+  _writeCommand(0x12); //display refresh
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
+}
+
+

+ 67 - 0
src/epd/GxEPD2_583.h

@@ -0,0 +1,67 @@
+// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
+// Requires HW SPI and Adafruit_GFX. Caution: these e-papers require 3.3V supply AND data lines!
+//
+// based on Demo Example from Good Display: http://www.good-display.com/download_list/downloadcategoryid=34&isMode=false.html
+//
+// Author: Jean-Marc Zingg
+//
+// Version: see library.properties
+//
+// Library: https://github.com/ZinggJM/GxEPD2
+
+#ifndef _GxEPD2_583_H_
+#define _GxEPD2_583_H_
+
+#include "../GxEPD2_EPD.h"
+
+class GxEPD2_583 : public GxEPD2_EPD
+{
+  public:
+    // attributes
+    static const uint16_t WIDTH = 600;
+    static const uint16_t HEIGHT = 448;
+    static const GxEPD2::Panel panel = GxEPD2::GDEW0583T7;
+    static const bool hasColor = false;
+    static const bool hasPartialUpdate = true;
+    static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 60; // ms, e.g. 56728us
+    static const uint16_t power_off_time = 30; // ms, e.g. 20291us
+    static const uint16_t full_refresh_time = 15000; // ms, e.g. 14598868us
+    static const uint16_t partial_refresh_time = 15000; // ms, e.g. 14598868us
+    // constructor
+    GxEPD2_583(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
+    // methods (virtual)
+    void init(uint32_t serial_diag_bitrate = 0); // = 0 : disabled
+    //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
+    void clearScreen(uint8_t value = 0x33); // init controller memory and screen (default white)
+    void writeScreenBuffer(uint8_t value = 0x33); // init controller memory (default white)
+    // write to controller memory, without screen refresh; x and w should be multiple of 8
+    void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
+    void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // write to controller memory, with screen refresh; x and w should be multiple of 8
+    void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
+    void drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    void refresh(bool partial_update_mode = false); // screen refresh from controller memory to full screen
+    void refresh(int16_t x, int16_t y, int16_t w, int16_t h); // screen refresh from controller memory, partial screen
+    void powerOff();
+  private:
+    void _writeScreenBuffer(uint8_t value);
+    void _send8pixel(uint8_t data);
+    void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
+    void _PowerOn();
+    void _PowerOff();
+    void _InitDisplay();
+    void _Init_Full();
+    void _Init_Part();
+    void _Update_Full();
+    void _Update_Part();
+  protected:
+    bool _initial, _power_is_on, _using_partial_mode;
+};
+
+#endif
+

+ 62 - 16
src/epd/GxEPD2_750.cpp

@@ -29,13 +29,14 @@ void GxEPD2_750::init(uint32_t serial_diag_bitrate)
 
 
 void GxEPD2_750::clearScreen(uint8_t value)
 void GxEPD2_750::clearScreen(uint8_t value)
 {
 {
+  if (value == 0xFF) value = 0x33; // white value for this controller
   if (_initial)
   if (_initial)
   {
   {
     _Init_Full();
     _Init_Full();
     _writeCommand(0x10);
     _writeCommand(0x10);
-    for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+    for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
     {
     {
-      _send8pixel(~value);
+      _writeData(value);
     }
     }
     _Update_Full();
     _Update_Full();
     _initial = false;
     _initial = false;
@@ -44,16 +45,16 @@ void GxEPD2_750::clearScreen(uint8_t value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
   {
   {
-    _send8pixel(~value);
+    _writeData(value);
   }
   }
   _Update_Part();
   _Update_Part();
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
   {
   {
-    _send8pixel(~value);
+    _writeData(value);
   }
   }
   _Update_Part();
   _Update_Part();
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
@@ -62,6 +63,7 @@ void GxEPD2_750::clearScreen(uint8_t value)
 
 
 void GxEPD2_750::writeScreenBuffer(uint8_t value)
 void GxEPD2_750::writeScreenBuffer(uint8_t value)
 {
 {
+  if (value == 0xFF) value = 0x33; // white value for this controller
   if (_initial) clearScreen(value);
   if (_initial) clearScreen(value);
   else _writeScreenBuffer(value);
   else _writeScreenBuffer(value);
 }
 }
@@ -72,17 +74,19 @@ void GxEPD2_750::_writeScreenBuffer(uint8_t value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
   {
   {
-    _send8pixel(~value);
+    _writeData(value);
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
 }
 }
 
 
 void GxEPD2_750::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_750::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -101,8 +105,8 @@ void GxEPD2_750::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     for (int16_t j = 0; j < w1 / 8; j++)
     for (int16_t j = 0; j < w1 / 8; j++)
     {
     {
       uint8_t data;
       uint8_t data;
-      // use w, h of bitmap for index!
-      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+      // use wb, h of bitmap for index!
+      int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
       if (pgm)
       if (pgm)
       {
       {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -120,6 +124,7 @@ void GxEPD2_750::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_
     }
     }
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_750::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_750::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -134,7 +139,48 @@ void GxEPD2_750::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t
 {
 {
   if (data1)
   if (data1)
   {
   {
-    writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+    int16_t wb = (w + 1) / 2; // width bytes, bitmaps are padded
+    x -= x % 2; // byte boundary
+    w = wb * 2; // byte boundary
+    int16_t x1 = x < 0 ? 0 : x; // limit
+    int16_t y1 = y < 0 ? 0 : y; // limit
+    int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+    int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+    int16_t dx = x1 - x;
+    int16_t dy = y1 - y;
+    w1 -= dx;
+    h1 -= dy;
+    if ((w1 <= 0) || (h1 <= 0)) return;
+    if (!_using_partial_mode) _Init_Part();
+    _writeCommand(0x91); // partial in
+    _setPartialRamArea(x1, y1, w1, h1);
+    _writeCommand(0x10);
+    for (int16_t i = 0; i < h1; i++)
+    {
+      for (int16_t j = 0; j < w1 / 2; j++)
+      {
+        uint8_t data;
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 2 + ((h - 1 - (i + dy))) * wb : j + dx / 2 + (i + dy) * wb;
+        if (pgm)
+        {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+          data = pgm_read_byte(&data1[idx]);
+#else
+          data = data1[idx];
+#endif
+        }
+        else
+        {
+          data = data1[idx];
+        }
+        if (invert) data = ~data;
+        _writeData(data);
+      }
+    }
+    _writeCommand(0x92); // partial out
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
   }
   }
 }
 }
 
 
@@ -225,7 +271,7 @@ void GxEPD2_750::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -233,7 +279,7 @@ void GxEPD2_750::_PowerOn()
 void GxEPD2_750::_PowerOff()
 void GxEPD2_750::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
   _using_partial_mode = false;
   _using_partial_mode = false;
 }
 }
@@ -302,13 +348,13 @@ void GxEPD2_750::_Init_Part()
 void GxEPD2_750::_Update_Full()
 void GxEPD2_750::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_750::_Update_Part()
 void GxEPD2_750::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 6 - 2
src/epd/GxEPD2_750.h

@@ -24,13 +24,17 @@ class GxEPD2_750 : public GxEPD2_EPD
     static const bool hasColor = false;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 80; // ms, e.g. 69914us
+    static const uint16_t power_off_time = 50; // ms, e.g. 40578us
+    static const uint16_t full_refresh_time = 4500; // ms, e.g. 4273474us
+    static const uint16_t partial_refresh_time = 4500; // ms, e.g. 4273474us
     // constructor
     // constructor
     GxEPD2_750(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_750(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)
     void init(uint32_t serial_diag_bitrate = 0); // = 0 : disabled
     void init(uint32_t serial_diag_bitrate = 0); // = 0 : disabled
     //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
     //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
-    void clearScreen(uint8_t value = 0xFF); // init controller memory and screen (default white)
-    void writeScreenBuffer(uint8_t value = 0xFF); // init controller memory (default white)
+    void clearScreen(uint8_t value = 0x33); // init controller memory and screen (default white)
+    void writeScreenBuffer(uint8_t value = 0x33); // init controller memory (default white)
     // write to controller memory, without screen refresh; x and w should be multiple of 8
     // write to controller memory, without screen refresh; x and w should be multiple of 8
     void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
     void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
     void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
     void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);

+ 30 - 31
src/epd3c/GxEPD2_154c.cpp

@@ -42,13 +42,13 @@ void GxEPD2_154c::clearScreen(uint8_t black_value, uint8_t color_value)
 {
 {
   _Init_Full();
   _Init_Full();
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(bw2grey[(black_value & 0xF0) >> 4]);
     _writeData(bw2grey[(black_value & 0xF0) >> 4]);
     _writeData(bw2grey[black_value & 0x0F]);
     _writeData(bw2grey[black_value & 0x0F]);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -65,13 +65,13 @@ void GxEPD2_154c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
 {
 {
   _Init_Full();
   _Init_Full();
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(bw2grey[(black_value & 0xF0) >> 4]);
     _writeData(bw2grey[(black_value & 0xF0) >> 4]);
     _writeData(bw2grey[black_value & 0x0F]);
     _writeData(bw2grey[black_value & 0x0F]);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -84,17 +84,11 @@ void GxEPD2_154c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16
 
 
 void GxEPD2_154c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_154c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
-  int16_t x1 = x < 0 ? 0 : x; // limit
-  int16_t y1 = y < 0 ? 0 : y; // limit
-  int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
-  int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
-  int16_t dx = x1 - x;
-  int16_t dy = y1 - y;
-  w1 -= dx;
-  h1 -= dy;
-  if ((w1 <= 0) || (h1 <= 0)) return;
+  w = wb * 8; // byte boundary
+  if ((w <= 0) || (h <= 0)) return;
   _Init_Full();
   _Init_Full();
   _writeCommand(0x10);
   _writeCommand(0x10);
   for (int16_t i = 0; i < HEIGHT; i++)
   for (int16_t i = 0; i < HEIGHT; i++)
@@ -104,11 +98,9 @@ void GxEPD2_154c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (black)
       if (black)
       {
       {
-        // use w, h of bitmap for index!
-        //if ((i + dy < h) && (j + dx < w))
-        if (((x + j) >= 0) && ((x + j) < w) && ((y + i) >= 0) && ((y + i) < h))
+        if ((j >= x) && (j <= x + w) && (i >= y) && (i < y + h))
         {
         {
-          int16_t idx = mirror_y ? (x + j) / 8 + ((h - 1 - (y + i))) * (w / 8) : (x + j) / 8 + (y + i) * (w / 8);
+          int16_t idx = mirror_y ? (j - x) / 8 + ((h - 1 - (i - y))) * wb : (j - x) / 8 + (i - y) * wb;
           if (pgm)
           if (pgm)
           {
           {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -137,11 +129,9 @@ void GxEPD2_154c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (color)
       if (color)
       {
       {
-        // use w, h of bitmap for index!
-        //if ((i + dy < h) && (j + dx < w))
-        if (((x + j) >= 0) && ((x + j) < w) && ((y + i) >= 0) && ((y + i) < h))
+        if ((j >= x) && (j <= x + w) && (i >= y) && (i < y + h))
         {
         {
-          int16_t idx = mirror_y ? (x + j) / 8 + ((h - 1 - (y + i))) * (w / 8) : (x + j) / 8 + (y + i) * (w / 8);
+          int16_t idx = mirror_y ? (j - x) / 8 + ((h - 1 - (i - y))) * wb : (j - x) / 8 + (i - y) * wb;
           if (pgm)
           if (pgm)
           {
           {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -160,6 +150,7 @@ void GxEPD2_154c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       _writeData(data);
       _writeData(data);
     }
     }
   }
   }
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_154c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_154c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -212,15 +203,25 @@ void GxEPD2_154c::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
 
 
 void GxEPD2_154c::_PowerOff()
 void GxEPD2_154c::_PowerOff()
 {
 {
+  _writeCommand(0x50);
+  _writeData(0x17);    //BD floating
+  _writeCommand(0x82);     //to solve Vcom drop
+  _writeData(0x00);
+  _writeCommand(0x01);     //power setting
+  _writeData(0x02);    //gate switch to external
+  _writeData(0x00);
+  _writeData(0x00);
+  _writeData(0x00);
+  delay(1500);     //delay 1.5S
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  //_waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
 }
 }
 
 
@@ -243,12 +244,10 @@ void GxEPD2_154c::_InitDisplay()
   _writeData(0x07);
   _writeData(0x07);
   _writeData(0x07);
   _writeData(0x07);
   _writeData(0x07);
   _writeData(0x07);
-  _writeCommand(0x04);
-  // power on needed here!
-  _waitWhileBusy("Power On");
-  _writeCommand(0X00);
+  _PowerOn(); //power on needed here!
+  _writeCommand(0x00);
   _writeData(0xcf);
   _writeData(0xcf);
-  _writeCommand(0X50);
+  _writeCommand(0x50);
   _writeData(0x37);
   _writeData(0x37);
   _writeCommand(0x30);
   _writeCommand(0x30);
   _writeData(0x39);
   _writeData(0x39);
@@ -291,13 +290,13 @@ void GxEPD2_154c::_Init_Part()
 void GxEPD2_154c::_Update_Full()
 void GxEPD2_154c::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_154c::_Update_Part()
 void GxEPD2_154c::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd3c/GxEPD2_154c.h

@@ -24,6 +24,10 @@ class GxEPD2_154c : public GxEPD2_EPD
     static const bool hasColor = true;
     static const bool hasColor = true;
     static const bool hasPartialUpdate = false;
     static const bool hasPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 20; // ms, e.g. 10157us
+    static const uint16_t power_off_time = 20; // ms, e.g. 10157us
+    static const uint16_t full_refresh_time = 7500; // ms, e.g. 7135635us
+    static const uint16_t partial_refresh_time = 7500; // ms, e.g. 7135635us
     // constructor
     // constructor
     GxEPD2_154c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_154c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 16 - 13
src/epd3c/GxEPD2_213c.cpp

@@ -36,12 +36,12 @@ void GxEPD2_213c::clearScreen(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(black_value);
     _writeData(black_value);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -61,12 +61,12 @@ void GxEPD2_213c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(black_value);
     _writeData(black_value);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -80,8 +80,10 @@ void GxEPD2_213c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16
 
 
 void GxEPD2_213c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_213c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -102,8 +104,8 @@ void GxEPD2_213c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (black)
       if (black)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -129,8 +131,8 @@ void GxEPD2_213c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (color)
       if (color)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -149,6 +151,7 @@ void GxEPD2_213c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
     }
     }
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_213c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_213c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -226,7 +229,7 @@ void GxEPD2_213c::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -234,7 +237,7 @@ void GxEPD2_213c::_PowerOn()
 void GxEPD2_213c::_PowerOff()
 void GxEPD2_213c::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
 }
 }
 
 
@@ -279,13 +282,13 @@ void GxEPD2_213c::_Init_Part()
 void GxEPD2_213c::_Update_Full()
 void GxEPD2_213c::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_213c::_Update_Part()
 void GxEPD2_213c::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd3c/GxEPD2_213c.h

@@ -24,6 +24,10 @@ class GxEPD2_213c : public GxEPD2_EPD
     static const bool hasColor = true;
     static const bool hasColor = true;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 40; // ms, e.g. 36991us
+    static const uint16_t power_off_time = 30; // ms, e.g. 20754us
+    static const uint16_t full_refresh_time = 15000; // ms, e.g. 14896608us
+    static const uint16_t partial_refresh_time = 15000; // ms, e.g. 14896608us
     // constructor
     // constructor
     GxEPD2_213c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_213c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 17 - 14
src/epd3c/GxEPD2_270c.cpp

@@ -34,12 +34,12 @@ void GxEPD2_270c::clearScreen(uint8_t black_value, uint8_t red_value)
 {
 {
   _Init_Part();
   _Init_Part();
   _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
   _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(~black_value);
     _writeData(~black_value);
   }
   }
   _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
   _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(~red_value);
     _writeData(~red_value);
   }
   }
@@ -55,12 +55,12 @@ void GxEPD2_270c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
 {
 {
   _Init_Part();
   _Init_Part();
   _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
   _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(~black_value);
     _writeData(~black_value);
   }
   }
   _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
   _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(~color_value);
     _writeData(~color_value);
   }
   }
@@ -73,8 +73,10 @@ void GxEPD2_270c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16
 
 
 void GxEPD2_270c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_270c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -93,8 +95,8 @@ void GxEPD2_270c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (black)
       if (black)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -120,8 +122,8 @@ void GxEPD2_270c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (color)
       if (color)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -139,6 +141,7 @@ void GxEPD2_270c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       _writeData(~data);
       _writeData(~data);
     }
     }
   }
   }
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_270c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_270c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -195,7 +198,7 @@ void GxEPD2_270c::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
   _writeData(w1 & 0xf8);
   _writeData(w1 & 0xf8);
   _writeData(h1 >> 8);
   _writeData(h1 >> 8);
   _writeData(h1 & 0xff);
   _writeData(h1 & 0xff);
-  _waitWhileBusy("refresh");
+  _waitWhileBusy("refresh", partial_refresh_time);
 }
 }
 
 
 void GxEPD2_270c::powerOff()
 void GxEPD2_270c::powerOff()
@@ -232,7 +235,7 @@ void GxEPD2_270c::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -240,7 +243,7 @@ void GxEPD2_270c::_PowerOn()
 void GxEPD2_270c::_PowerOff()
 void GxEPD2_270c::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
 }
 }
 
 
@@ -333,13 +336,13 @@ void GxEPD2_270c::_Init_Part()
 void GxEPD2_270c::_Update_Full()
 void GxEPD2_270c::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_270c::_Update_Part()
 void GxEPD2_270c::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd3c/GxEPD2_270c.h

@@ -24,6 +24,10 @@ class GxEPD2_270c : public GxEPD2_EPD
     static const bool hasColor = true;
     static const bool hasColor = true;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 20; // ms, e.g. 101361us
+    static const uint16_t power_off_time = 40; // ms, e.g. 29419us
+    static const uint16_t full_refresh_time = 16000; // ms, e.g. 15524093us
+    static const uint16_t partial_refresh_time = 16000; // ms, e.g. 15524093us
     // constructor
     // constructor
     GxEPD2_270c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_270c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 16 - 13
src/epd3c/GxEPD2_290c.cpp

@@ -36,12 +36,12 @@ void GxEPD2_290c::clearScreen(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(black_value);
     _writeData(black_value);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -61,12 +61,12 @@ void GxEPD2_290c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(black_value);
     _writeData(black_value);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -80,8 +80,10 @@ void GxEPD2_290c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16
 
 
 void GxEPD2_290c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_290c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -102,8 +104,8 @@ void GxEPD2_290c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (black)
       if (black)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -129,8 +131,8 @@ void GxEPD2_290c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (color)
       if (color)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -149,6 +151,7 @@ void GxEPD2_290c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
     }
     }
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_290c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_290c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -226,7 +229,7 @@ void GxEPD2_290c::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -234,7 +237,7 @@ void GxEPD2_290c::_PowerOn()
 void GxEPD2_290c::_PowerOff()
 void GxEPD2_290c::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
 }
 }
 
 
@@ -279,13 +282,13 @@ void GxEPD2_290c::_Init_Part()
 void GxEPD2_290c::_Update_Full()
 void GxEPD2_290c::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_290c::_Update_Part()
 void GxEPD2_290c::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd3c/GxEPD2_290c.h

@@ -24,6 +24,10 @@ class GxEPD2_290c : public GxEPD2_EPD
     static const bool hasColor = true;
     static const bool hasColor = true;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 40; // ms, e.g. 36557us
+    static const uint16_t power_off_time = 30; // ms, e.g. 20291us
+    static const uint16_t full_refresh_time = 15000; // ms, e.g. 14845408us
+    static const uint16_t partial_refresh_time = 15000; // ms, e.g. 14845408us
     // constructor
     // constructor
     GxEPD2_290c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_290c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 14 - 11
src/epd3c/GxEPD2_420c.cpp

@@ -36,12 +36,12 @@ void GxEPD2_420c::clearScreen(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(black_value);
     _writeData(black_value);
   }
   }
   _writeCommand(0x13);
   _writeCommand(0x13);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _writeData(color_value);
     _writeData(color_value);
   }
   }
@@ -80,8 +80,10 @@ void GxEPD2_420c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16
 
 
 void GxEPD2_420c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_420c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -102,8 +104,8 @@ void GxEPD2_420c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (black)
       if (black)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -129,8 +131,8 @@ void GxEPD2_420c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t data = 0xFF;
       uint8_t data = 0xFF;
       if (color)
       if (color)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -149,6 +151,7 @@ void GxEPD2_420c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
     }
     }
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_420c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_420c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
@@ -227,7 +230,7 @@ void GxEPD2_420c::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -235,7 +238,7 @@ void GxEPD2_420c::_PowerOn()
 void GxEPD2_420c::_PowerOff()
 void GxEPD2_420c::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
 }
 }
 
 
@@ -264,13 +267,13 @@ void GxEPD2_420c::_Init_Part()
 void GxEPD2_420c::_Update_Full()
 void GxEPD2_420c::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_420c::_Update_Part()
 void GxEPD2_420c::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 4 - 0
src/epd3c/GxEPD2_420c.h

@@ -24,6 +24,10 @@ class GxEPD2_420c : public GxEPD2_EPD
     static const bool hasColor = true;
     static const bool hasColor = true;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 40; // ms, e.g. 38006us
+    static const uint16_t power_off_time = 30; // ms, e.g. 20292us
+    static const uint16_t full_refresh_time = 16000; // ms, e.g. 15771891us
+    static const uint16_t partial_refresh_time = 16000; // ms, e.g. 15771891us
     // constructor
     // constructor
     GxEPD2_420c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_420c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)

+ 375 - 0
src/epd3c/GxEPD2_583c.cpp

@@ -0,0 +1,375 @@
+// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
+// Requires HW SPI and Adafruit_GFX. Caution: these e-papers require 3.3V supply AND data lines!
+//
+// based on Demo Example from Good Display: http://www.good-display.com/download_list/downloadcategoryid=34&isMode=false.html
+//
+// Author: Jean-Marc Zingg
+//
+// Version: see library.properties
+//
+// Library: https://github.com/ZinggJM/GxEPD2
+
+#include "GxEPD2_583c.h"
+#include "WaveTables.h"
+
+GxEPD2_583c::GxEPD2_583c(int8_t cs, int8_t dc, int8_t rst, int8_t busy) : GxEPD2_EPD(cs, dc, rst, busy, LOW, 40000000)
+{
+  _initial = true;
+  _power_is_on = false;
+}
+
+void GxEPD2_583c::init(uint32_t serial_diag_bitrate)
+{
+  GxEPD2_EPD::init(serial_diag_bitrate);
+  _initial = true;
+  _power_is_on = false;
+}
+
+void GxEPD2_583c::clearScreen(uint8_t value)
+{
+  if (value == 0xFF) value = 0x33; // white value for this controller
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _Update_Part();
+  _writeCommand(0x92); // partial out
+}
+
+void GxEPD2_583c::clearScreen(uint8_t black_value, uint8_t color_value)
+{
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  {
+    _send8pixel(~black_value, ~color_value);
+  }
+  _Update_Part();
+  _writeCommand(0x92); // partial out
+}
+
+void GxEPD2_583c::writeScreenBuffer(uint8_t value)
+{
+  if (value == 0xFF) value = 0x33; // white value for this controller
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _writeCommand(0x92); // partial out
+}
+
+void GxEPD2_583c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
+{
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  {
+    _send8pixel(~black_value, ~color_value);
+  }
+  _writeCommand(0x92); // partial out
+}
+
+void GxEPD2_583c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeImage(bitmap, NULL, x, y, w, h, invert, mirror_y, pgm);
+}
+
+void GxEPD2_583c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
+  x -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
+  int16_t x1 = x < 0 ? 0 : x; // limit
+  int16_t y1 = y < 0 ? 0 : y; // limit
+  int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+  int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+  int16_t dx = x1 - x;
+  int16_t dy = y1 - y;
+  w1 -= dx;
+  h1 -= dy;
+  if ((w1 <= 0) || (h1 <= 0)) return;
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(x1, y1, w1, h1);
+  _writeCommand(0x10);
+  for (int16_t i = 0; i < h1; i++)
+  {
+    for (int16_t j = 0; j < w1 / 8; j++)
+    {
+      uint8_t black_data = 0xFF;
+      uint8_t color_data = 0xFF;
+      if (black)
+      {
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
+        if (pgm)
+        {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+          black_data = pgm_read_byte(&black[idx]);
+#else
+          black_data = black[idx];
+#endif
+        }
+        else
+        {
+          black_data = black[idx];
+        }
+        if (invert) black_data = ~black_data;
+      }
+      if (color)
+      {
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
+        if (pgm)
+        {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+          color_data = pgm_read_byte(&color[idx]);
+#else
+          color_data = color[idx];
+#endif
+        }
+        else
+        {
+          color_data = color[idx];
+        }
+        if (invert) color_data = ~color_data;
+      }
+      _send8pixel(~black_data, ~color_data);
+    }
+#if defined(ESP8266)
+    yield();
+#endif
+  }
+  _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+}
+
+void GxEPD2_583c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  if (data1)
+  {
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+    int16_t wb = (w + 1) / 2; // width bytes, bitmaps are padded
+    x -= x % 2; // byte boundary
+    w = wb * 2; // byte boundary
+    int16_t x1 = x < 0 ? 0 : x; // limit
+    int16_t y1 = y < 0 ? 0 : y; // limit
+    int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+    int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+    int16_t dx = x1 - x;
+    int16_t dy = y1 - y;
+    w1 -= dx;
+    h1 -= dy;
+    if ((w1 <= 0) || (h1 <= 0)) return;
+    _Init_Part();
+    _writeCommand(0x91); // partial in
+    _setPartialRamArea(x1, y1, w1, h1);
+    _writeCommand(0x10);
+    for (int16_t i = 0; i < h1; i++)
+    {
+      for (int16_t j = 0; j < w1 / 2; j++)
+      {
+        uint8_t data;
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 2 + ((h - 1 - (i + dy))) * wb : j + dx / 2 + (i + dy) * wb;
+        if (pgm)
+        {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+          data = pgm_read_byte(&data1[idx]);
+#else
+          data = data1[idx];
+#endif
+        }
+        else
+        {
+          data = data1[idx];
+        }
+        if (invert) data = ~data;
+        _writeData(data);
+      }
+    }
+    _writeCommand(0x92); // partial out
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  }
+}
+
+void GxEPD2_583c::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
+  refresh(x, y, w, h);
+}
+
+void GxEPD2_583c::drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
+  refresh(x, y, w, h);
+}
+
+void GxEPD2_583c::drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
+{
+  writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
+  refresh(x, y, w, h);
+}
+
+void GxEPD2_583c::refresh(bool partial_update_mode)
+{
+  if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
+  else _Update_Full();
+}
+
+void GxEPD2_583c::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
+{
+  x -= x % 8; // byte boundary
+  w -= x % 8; // byte boundary
+  int16_t x1 = x < 0 ? 0 : x; // limit
+  int16_t y1 = y < 0 ? 0 : y; // limit
+  int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+  int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+  w1 -= x1 - x;
+  h1 -= y1 - y;
+  _Init_Part();
+  _setPartialRamArea(x1, y1, w1, h1);
+  _Update_Part();
+}
+
+void GxEPD2_583c::powerOff()
+{
+  _PowerOff();
+}
+
+void GxEPD2_583c::_send8pixel(uint8_t black_data, uint8_t color_data)
+{
+  for (uint8_t j = 0; j < 8; j++)
+  {
+    uint8_t t = 0x00; // black
+    if (black_data & 0x80); // keep black
+    else if (color_data & 0x80) t = 0x04; //color
+    else t = 0x03; // white
+    t <<= 4;
+    black_data <<= 1;
+    color_data <<= 1;
+    j++;
+    if (black_data & 0x80); // keep black
+    else if (color_data & 0x80) t |= 0x04; //color
+    else t |= 0x03; // white
+    black_data <<= 1;
+    color_data <<= 1;
+    _writeData(t);
+  }
+}
+
+void GxEPD2_583c::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
+{
+  uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
+  uint16_t ye = y + h - 1;
+  x &= 0xFFF8; // byte boundary
+  xe |= 0x0007; // byte boundary
+  _writeCommand(0x90); // partial window
+  _writeData(x / 256);
+  _writeData(x % 256);
+  _writeData(xe / 256);
+  _writeData(xe % 256);
+  _writeData(y / 256);
+  _writeData(y % 256);
+  _writeData(ye / 256);
+  _writeData(ye % 256);
+  //_writeData(0x01); // distortion on full right half
+  _writeData(0x00); // distortion on right half
+}
+
+void GxEPD2_583c::_PowerOn()
+{
+  if (!_power_is_on)
+  {
+    _writeCommand(0x04);
+    _waitWhileBusy("_PowerOn", power_on_time);
+  }
+  _power_is_on = true;
+}
+
+void GxEPD2_583c::_PowerOff()
+{
+  _writeCommand(0x02); // power off
+  _waitWhileBusy("_PowerOff", power_off_time);
+  _power_is_on = false;
+}
+
+void GxEPD2_583c::_InitDisplay()
+{
+  // reset required for wakeup
+  if (!_power_is_on && (_rst >= 0))
+  {
+    digitalWrite(_rst, 0);
+    delay(10);
+    digitalWrite(_rst, 1);
+    delay(10);
+  }
+  _writeCommand(0x01);
+  _writeData (0x37); //POWER SETTING
+  _writeData (0x00);
+  _writeCommand(0X00); //PANNEL SETTING
+  _writeData(0xCF);
+  _writeData(0x08);
+  _writeCommand(0x06); //boost
+  _writeData (0xc7);
+  _writeData (0xcc);
+  _writeData (0x28);
+  _writeCommand(0x30); //PLL setting
+  //_writeData (0x3a);   //PLL:    0-15��:0x3C, 15+:0x3A
+  _writeData (0x3c); //PLL:    0-15��:0x3C, 15+:0x3A
+  _writeCommand(0X41); //TEMPERATURE SETTING
+  _writeData(0x00);
+  _writeCommand(0X50); //VCOM AND DATA INTERVAL SETTING
+  _writeData(0x77);
+  _writeCommand(0X60); //TCON SETTING
+  _writeData(0x22);
+  _writeCommand(0x61); //600*448
+  _writeData (0x02);   //source 600
+  _writeData (0x58);
+  _writeData (0x01);   //gate 448
+  _writeData (0xc0);
+  _writeCommand(0X82); //VCOM VOLTAGE SETTING
+  _writeData(0x28);    //all temperature  range
+  _writeCommand(0xe5); //FLASH MODE
+  _writeData(0x03);
+}
+
+void GxEPD2_583c::_Init_Full()
+{
+  _InitDisplay();
+  _PowerOn();
+}
+
+void GxEPD2_583c::_Init_Part()
+{
+  _InitDisplay();
+  _PowerOn();
+}
+
+void GxEPD2_583c::_Update_Full()
+{
+  _writeCommand(0x12); //display refresh
+  _waitWhileBusy("_Update_Full", full_refresh_time);
+}
+
+void GxEPD2_583c::_Update_Part()
+{
+  _writeCommand(0x12); //display refresh
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
+}
+
+
+

+ 69 - 0
src/epd3c/GxEPD2_583c.h

@@ -0,0 +1,69 @@
+// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
+// Requires HW SPI and Adafruit_GFX. Caution: these e-papers require 3.3V supply AND data lines!
+//
+// based on Demo Example from Good Display: http://www.good-display.com/download_list/downloadcategoryid=34&isMode=false.html
+//
+// Author: Jean-Marc Zingg
+//
+// Version: see library.properties
+//
+// Library: https://github.com/ZinggJM/GxEPD2_32
+
+#ifndef _GxEPD2_583c_H_
+#define _GxEPD2_583c_H_
+
+#include "../GxEPD2_EPD.h"
+
+class GxEPD2_583c : public GxEPD2_EPD
+{
+  public:
+    // attributes
+    static const uint16_t WIDTH = 600;
+    static const uint16_t HEIGHT = 448;
+    static const GxEPD2::Panel panel = GxEPD2::GDEW0583Z21;
+    static const bool hasColor = true;
+    static const bool hasPartialUpdate = true;
+    static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 70; // ms, e.g. 59769us
+    static const uint16_t power_off_time = 50; // ms, e.g. 40024us
+    static const uint16_t full_refresh_time = 32000; // ms, e.g. 29165492us
+    static const uint16_t partial_refresh_time = 32000; // ms, e.g. 29165492us
+    // constructor
+    GxEPD2_583c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
+    // methods (virtual)
+    void init(uint32_t serial_diag_bitrate = 0); // = 0 : disabled
+    //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
+    void clearScreen(uint8_t value = 0x33); // init controller memory and screen (default white)
+    void clearScreen(uint8_t black_value, uint8_t red_value); // init controller memory and screen
+    void writeScreenBuffer(uint8_t value = 0x33); // init controller memory (default white)
+    void writeScreenBuffer(uint8_t black_value, uint8_t red_value); // init controller memory
+    // write to controller memory, without screen refresh; x and w should be multiple of 8
+    void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    void writeImage(const uint8_t* black, const uint8_t* red, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
+    void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // write to controller memory, with screen refresh; x and w should be multiple of 8
+    void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    void drawImage(const uint8_t* black, const uint8_t* red, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
+    void drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    void refresh(bool partial_update_mode = false); // screen refresh from controller memory to full screen
+    void refresh(int16_t x, int16_t y, int16_t w, int16_t h); // screen refresh from controller memory, partial screen
+    void powerOff();
+  private:
+    void _writeScreenBuffer(uint8_t value);
+    void _send8pixel(uint8_t black_data, uint8_t red_data);
+    void _setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
+    void _PowerOn();
+    void _PowerOff();
+    void _InitDisplay();
+    void _Init_Full();
+    void _Init_Part();
+    void _Update_Full();
+    void _Update_Part();
+  protected:
+    bool _initial, _power_is_on;
+};
+
+#endif
+

+ 80 - 17
src/epd3c/GxEPD2_750c.cpp

@@ -12,7 +12,7 @@
 #include "GxEPD2_750c.h"
 #include "GxEPD2_750c.h"
 #include "WaveTables.h"
 #include "WaveTables.h"
 
 
-GxEPD2_750c::GxEPD2_750c(int8_t cs, int8_t dc, int8_t rst, int8_t busy) : GxEPD2_EPD(cs, dc, rst, busy, LOW, 20000000)
+GxEPD2_750c::GxEPD2_750c(int8_t cs, int8_t dc, int8_t rst, int8_t busy) : GxEPD2_EPD(cs, dc, rst, busy, LOW, 40000000)
 {
 {
   _initial = true;
   _initial = true;
   _power_is_on = false;
   _power_is_on = false;
@@ -27,7 +27,17 @@ void GxEPD2_750c::init(uint32_t serial_diag_bitrate)
 
 
 void GxEPD2_750c::clearScreen(uint8_t value)
 void GxEPD2_750c::clearScreen(uint8_t value)
 {
 {
-  clearScreen(value, 0xFF);
+  if (value == 0xFF) value = 0x33; // white value for this controller
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _Update_Part();
+  _writeCommand(0x92); // partial out
 }
 }
 
 
 void GxEPD2_750c::clearScreen(uint8_t black_value, uint8_t color_value)
 void GxEPD2_750c::clearScreen(uint8_t black_value, uint8_t color_value)
@@ -36,7 +46,7 @@ void GxEPD2_750c::clearScreen(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _send8pixel(~black_value, ~color_value);
     _send8pixel(~black_value, ~color_value);
   }
   }
@@ -46,7 +56,16 @@ void GxEPD2_750c::clearScreen(uint8_t black_value, uint8_t color_value)
 
 
 void GxEPD2_750c::writeScreenBuffer(uint8_t value)
 void GxEPD2_750c::writeScreenBuffer(uint8_t value)
 {
 {
-  writeScreenBuffer(value, 0xFF);
+  if (value == 0xFF) value = 0x33; // white value for this controller
+  _Init_Part();
+  _writeCommand(0x91); // partial in
+  _setPartialRamArea(0, 0, WIDTH, HEIGHT);
+  _writeCommand(0x10);
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 2; i++)
+  {
+    _writeData(value);
+  }
+  _writeCommand(0x92); // partial out
 }
 }
 
 
 void GxEPD2_750c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
 void GxEPD2_750c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
@@ -55,7 +74,7 @@ void GxEPD2_750c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
   _writeCommand(0x91); // partial in
   _writeCommand(0x91); // partial in
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _setPartialRamArea(0, 0, WIDTH, HEIGHT);
   _writeCommand(0x10);
   _writeCommand(0x10);
-  for (int16_t i = 0; i < WIDTH * HEIGHT / 8; i++)
+  for (uint32_t i = 0; i < WIDTH * HEIGHT / 8; i++)
   {
   {
     _send8pixel(~black_value, ~color_value);
     _send8pixel(~black_value, ~color_value);
   }
   }
@@ -69,8 +88,10 @@ void GxEPD2_750c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16
 
 
 void GxEPD2_750c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_750c::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+  int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
   x -= x % 8; // byte boundary
   x -= x % 8; // byte boundary
-  w -= x % 8; // byte boundary
+  w = wb * 8; // byte boundary
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t x1 = x < 0 ? 0 : x; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t y1 = y < 0 ? 0 : y; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
   int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
@@ -92,8 +113,8 @@ void GxEPD2_750c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       uint8_t color_data = 0xFF;
       uint8_t color_data = 0xFF;
       if (black)
       if (black)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -110,8 +131,8 @@ void GxEPD2_750c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
       }
       }
       if (color)
       if (color)
       {
       {
-        // use w, h of bitmap for index!
-        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * (w / 8) : j + dx / 8 + (i + dy) * (w / 8);
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
         if (pgm)
         if (pgm)
         {
         {
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
 #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
@@ -130,13 +151,55 @@ void GxEPD2_750c::writeImage(const uint8_t* black, const uint8_t* color, int16_t
     }
     }
   }
   }
   _writeCommand(0x92); // partial out
   _writeCommand(0x92); // partial out
+  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
 }
 }
 
 
 void GxEPD2_750c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 void GxEPD2_750c::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
 {
 {
   if (data1)
   if (data1)
   {
   {
-    writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
+    int16_t wb = (w + 1) / 2; // width bytes, bitmaps are padded
+    x -= x % 2; // byte boundary
+    w = wb * 2; // byte boundary
+    int16_t x1 = x < 0 ? 0 : x; // limit
+    int16_t y1 = y < 0 ? 0 : y; // limit
+    int16_t w1 = x + w < WIDTH ? w : WIDTH - x; // limit
+    int16_t h1 = y + h < HEIGHT ? h : HEIGHT - y; // limit
+    int16_t dx = x1 - x;
+    int16_t dy = y1 - y;
+    w1 -= dx;
+    h1 -= dy;
+    if ((w1 <= 0) || (h1 <= 0)) return;
+    _Init_Part();
+    _writeCommand(0x91); // partial in
+    _setPartialRamArea(x1, y1, w1, h1);
+    _writeCommand(0x10);
+    for (int16_t i = 0; i < h1; i++)
+    {
+      for (int16_t j = 0; j < w1 / 2; j++)
+      {
+        uint8_t data;
+        // use wb, h of bitmap for index!
+        int16_t idx = mirror_y ? j + dx / 2 + ((h - 1 - (i + dy))) * wb : j + dx / 2 + (i + dy) * wb;
+        if (pgm)
+        {
+#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
+          data = pgm_read_byte(&data1[idx]);
+#else
+          data = data1[idx];
+#endif
+        }
+        else
+        {
+          data = data1[idx];
+        }
+        if (invert) data = ~data;
+        _writeData(data);
+      }
+    }
+    _writeCommand(0x92); // partial out
+    delay(1); // yield() to avoid WDT on ESP8266 and ESP32
   }
   }
 }
 }
 
 
@@ -229,7 +292,7 @@ void GxEPD2_750c::_PowerOn()
   if (!_power_is_on)
   if (!_power_is_on)
   {
   {
     _writeCommand(0x04);
     _writeCommand(0x04);
-    _waitWhileBusy("_PowerOn");
+    _waitWhileBusy("_PowerOn", power_on_time);
   }
   }
   _power_is_on = true;
   _power_is_on = true;
 }
 }
@@ -237,7 +300,7 @@ void GxEPD2_750c::_PowerOn()
 void GxEPD2_750c::_PowerOff()
 void GxEPD2_750c::_PowerOff()
 {
 {
   _writeCommand(0x02); // power off
   _writeCommand(0x02); // power off
-  _waitWhileBusy("_PowerOff");
+  _waitWhileBusy("_PowerOff", power_off_time);
   _power_is_on = false;
   _power_is_on = false;
 }
 }
 
 
@@ -261,8 +324,8 @@ void GxEPD2_750c::_InitDisplay()
   _writeCommand(0x01);
   _writeCommand(0x01);
   _writeData (0x37);       //POWER SETTING
   _writeData (0x37);       //POWER SETTING
   _writeData (0x00);
   _writeData (0x00);
-  _writeCommand(0x04);     //POWER ON
-  _waitWhileBusy("POWER");
+  //_writeCommand(0x04);     //POWER ON
+  //_waitWhileBusy("PowerOn", power_on_time);
   _writeCommand(0X00);     //PANNEL SETTING
   _writeCommand(0X00);     //PANNEL SETTING
   _writeData(0xCF);
   _writeData(0xCF);
   _writeData(0x08);
   _writeData(0x08);
@@ -304,13 +367,13 @@ void GxEPD2_750c::_Init_Part()
 void GxEPD2_750c::_Update_Full()
 void GxEPD2_750c::_Update_Full()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Full");
+  _waitWhileBusy("_Update_Full", full_refresh_time);
 }
 }
 
 
 void GxEPD2_750c::_Update_Part()
 void GxEPD2_750c::_Update_Part()
 {
 {
   _writeCommand(0x12); //display refresh
   _writeCommand(0x12); //display refresh
-  _waitWhileBusy("_Update_Part");
+  _waitWhileBusy("_Update_Part", partial_refresh_time);
 }
 }
 
 
 
 

+ 6 - 2
src/epd3c/GxEPD2_750c.h

@@ -24,14 +24,18 @@ class GxEPD2_750c : public GxEPD2_EPD
     static const bool hasColor = true;
     static const bool hasColor = true;
     static const bool hasPartialUpdate = true;
     static const bool hasPartialUpdate = true;
     static const bool hasFastPartialUpdate = false;
     static const bool hasFastPartialUpdate = false;
+    static const uint16_t power_on_time = 50; // ms, e.g. 36540us
+    static const uint16_t power_off_time = 50; // ms, e.g. 40579us
+    static const uint16_t full_refresh_time = 32000; // ms, e.g. 31094507us
+    static const uint16_t partial_refresh_time = 32000; // ms, e.g. 31094507us
     // constructor
     // constructor
     GxEPD2_750c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     GxEPD2_750c(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
     // methods (virtual)
     void init(uint32_t serial_diag_bitrate = 0); // = 0 : disabled
     void init(uint32_t serial_diag_bitrate = 0); // = 0 : disabled
     //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
     //  Support for Bitmaps (Sprites) to Controller Buffer and to Screen
-    void clearScreen(uint8_t value = 0xFF); // init controller memory and screen (default white)
+    void clearScreen(uint8_t value = 0x33); // init controller memory and screen (default white)
     void clearScreen(uint8_t black_value, uint8_t red_value); // init controller memory and screen
     void clearScreen(uint8_t black_value, uint8_t red_value); // init controller memory and screen
-    void writeScreenBuffer(uint8_t value = 0xFF); // init controller memory (default white)
+    void writeScreenBuffer(uint8_t value = 0x33); // init controller memory (default white)
     void writeScreenBuffer(uint8_t black_value, uint8_t red_value); // init controller memory
     void writeScreenBuffer(uint8_t black_value, uint8_t red_value); // init controller memory
     // write to controller memory, without screen refresh; x and w should be multiple of 8
     // write to controller memory, without screen refresh; x and w should be multiple of 8
     void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
     void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);