My e-Paper journey using GxEDP2 library

I will continue to post to this thread with the sage of my EDP and GxEDP2 journey.

I and grateful to Jean-Marc Zingg for his amazing work on the GxEDP2 ePaper Library.

I purchased a Good Display 2.7" BW display for my initial exposure to ePaper.

Good Display PN GDEW027W3 + the DESPI-C02 Adapter Board.

I do have a project in mind however at the moment I'm simply trying to understand how to use the library and get some text on the screen. I started with the GxEPD example "GxEPD2_HelloWorld.ino" and was able to get the display up and running with a Pro Mini.

Now I've moved to a ESP8266 to run the "GxEPD2_NotPagedExample.ino" again with almost no problems. However I'm only a fair(or worse) C coder and a only have a hint of C++ under my belt. I'm basically a visual / graphical guy so I don't pick up somethings as quickly as others do.
So I took the "GxEPD2_NotPagedExample.ino" and pared it down to just printing text and hibernating. I've added comments for the methods used however I'm sure there are some errors. I'm posting my code (actually @ZinggJM's code) in case others.

My next goals is how to understand how the screen data goes from the µP memory to the display IC (with two buffers) to the screen. I expect this has something to do with the writeImageAgain method.

/* Based on "GxEPD2_NotPagedExample.ino" a display Library example for SPI e-paper panels from Dalian Good Display
   and boards from Waveshare.
   Requires HW SPI and Adafruit_GFX. Caution: the e-paper panels require 3.3V supply AND data lines!
     Author: Jean-Marc Zingg
______________________________________________________________________________________________________

 This file is a pared down version of "GxEPD2_NotPagedExample.ino".  All code except that needed
 to display some text in the middle of the display has been removed.  My goal is to simplify
 the code allowing a better understanding of the functions needed to display text.

 This file is for use with an esp32 or esp8266.  I've only tested it on an esp8266 (NodeMCU)
 All the compiler "#if defined" statements have been removed for clarity (mine) with the decisions for
 the esp8266 and GDEW027W3 display.

 CAUTION:  my line by line comments probably have some errors.

 EPD = electronic paper display aka e-Paper

 Program function:  Display "text_01" in the center of the screen then hibernate.

todo:  These are just programming, little to do with how the display works.
 1) Modify the "displayText()" procedure to accept text pointer.
 2) Modify the "displayText()" procedure to accept coordinates and / or descriptors.  i.e. center, center left, top left, bottom left etc
 3) Modify the "displayText()" procedure to accept different fonts.
*/

// base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter, uses ~1.2k more code
// enable or disable GxEPD2_GFX base class
#define ENABLE_GxEPD2_GFX 0

// uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX
//#include <GFX.h>
// Note: if you use this with ENABLE_GxEPD2_GFX 1:
//       uncomment it in GxEPD2_GFX.h too, or add #include <GFX.h> before any #include <GxEPD2_GFX.h>

#include <GxEPD2_BW.h>            // in this file you must find the #include for your display and uncomment it.
//#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <StreamString.h>         // Apparently needed to "Print" to the screen.  Seems to be special for the ESP8266
                                  //     or at least different
#define PrintString StreamString  // guess this is only a name change (for clarity?)

// Constructor:  creates an EPD object named "display"
// select one and adapt to your mapping, can use full buffer size (full HEIGHT)
GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> display(GxEPD2_270(/*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));


void setup()
{
  // for output to IDE Serial monitor
  Serial.begin(115200);
  Serial.println();
  Serial.println("setup");
  delay(100);
  
  display.init(115200);       // the init fcn simply sends a low pulse to display board rst line.
                              // This is the only way to wake the display IC from hibernation.

  // first update should be full refresh
  displayText();
  delay(1000);

  // partial refresh mode can be used to full screen,
  // effective if display panel hasFastPartialUpdate
//  helloFullScreenPartialMode();

  delay(10000);

  display.powerOff();
  //deepSleepTest();
  Serial.println("setup done");
}

void loop() { }

// note for partial update window and setPartialWindow() method:
// partial update window size and position is on byte boundary in physical x direction
// the size is increased in setPartialWindow() if x or w are not multiple of 8 for even rotation, y or h for odd rotation
// see also comment in GxEPD2_BW.h, GxEPD2_3C.h or GxEPD2_GFX.h for method setPartialWindow()

const char text_01[] = "Good Display 2.7\"";
const char hibernating[] = "hibernating ...";
const char again[] = "again";

void displayText()
{
  display.setRotation(1);
  display.setFont(&FreeMonoBold9pt7b);
  display.setTextColor(GxEPD_BLACK);

  int16_t tbx, tby; uint16_t tbw, tbh;
  display.getTextBounds(text_01, 0, 0, &tbx, &tby, &tbw, &tbh);  // send a textarray, originx, originy returns the center point of the text and
                                                                 // the height & width at the previously defined font... I think?

  // center bounding box by transposition of origin:
  uint16_t x = ((display.width() - tbw) / 2) - tbx;
  uint16_t y = ((display.height() - tbh) / 2) - tby;
  
  // full window mode is the initial mode, set it anyway
  display.setFullWindow();            // setspartial_mode = false; sets the screen x,y = 0 and height & width to display max
  
  display.fillScreen(GxEPD_WHITE);
  display.setCursor(x, y);
  display.print(text_01);           // writes text_01 in the controllers memory.
  display.display(false);           // screen will not change until this command is made.
                                    //  with partial update = false.  display buffer content to screen, useful for full screen buffer
                                    //  not sure which buffer is in play here.  µP or the display IC buffer(s)
  delay(5000);

  display.getTextBounds(hibernating, 0, 0, &tbx, &tby, &tbw, &tbh);
  uint16_t hx = (display.width() - tbw) / 2;
  uint16_t hy = (display.height() / 3) + tbh / 2; // y is base line!
  display.getTextBounds(again, 0, 0, &tbx, &tby, &tbw, &tbh);
  uint16_t ax = (display.width() - tbw) / 2;
  uint16_t ay = (display.height() * 2 / 3) + tbh / 2; // y is base line!
  display.fillScreen(GxEPD_WHITE);
  display.setCursor(hx, hy);
  display.print(hibernating);
  display.setCursor(ax, ay);
  display.print(again);
  display.display(false);
  display.hibernate();          //turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
                                //  here powerOff() = turns off generation of panel driving voltages, avoids screen fading over time
}

I try to be patient with your posts. But I am not a teacher and I will not correct whatever wrong you write. I may just stop responding to your posts.

From GxEPD2_EPD.h, the driver class interface:

    // 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;
    // for differential update: set current and previous buffers equal (for fast partial update to work correctly)
    virtual void writeScreenBufferAgain(uint8_t value = 0xFF) // init controller memory (default white)
    {
      // most controllers with differential update do switch buffers on refresh, can use:
      writeScreenBuffer(value);
    }
    virtual void writeImageAgain(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)
    {
      // most controllers with differential update do switch buffers on refresh, can use:
      writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
    }

See also this post: GxEPD2 - Partial update with deep sleep

I'm sorry if it seemed like I was asking for your help. You've done quite a lot already.

This post was to hopefully help others who are in my situation, or convince those considering using ePaper for some project. I've made a number of similar posts, mostly in the Hubitat forum but some here as well. If I struggle with some device (usually the case) I try to summarize my findings to help those behind.
My Caution that there may be some mistakes was just that. It was not a cry for someone to correct those mistakes.

I will admit I was kinda asking for help with the different buffers, however I don't expect you to spend time teaching me. I will, once I get them figured out create a similar post on what I find hoping to pass on the knowledge I've found.

So I'm sorry if I made it feel like I was badgering you for information :slight_smile: I only included your forum name because I was uncomfortable posting this code without folks understanding there was absolutely no unique code from me and all the posted code was written by you.

Regards

John