E-paper and deep sleep

Tags: ESP32 EPD e-paper

You want to perform a partial update of a Waveshare 4.2 BW e-paper display (may works for other displays) after a deep sleep / power off of your MCU (and your e-paper display) ? It seems that GxEPD does not can do it, but its possible and easy to do.

The Waveshare 4.2 uses the IL0398 IC. It has two buffers: one for the old bitmap, and one for the new one. The IC0398 uses the difference between the two bitmap to perform the partial ("fast") refresh (~1 second). But with a power off of the module (if you want to "sleep" and save your battery), the chip forget of course the old bitmap.

But you can easily modify the library to send the old image, with the 0x10 command, instead of 0x13 command for the "new" bitmap. For example, with GxEPD add the following function:

void GxEPD2_420::writeImage(const uint8_t bitmap[], const uint8_t bitmap2[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
{
  printf("writeImage(%d,%d,%d,%d,%d,%d,%d)\n",x,y,w,h,invert,mirror_y,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 < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(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); //OLD, or black
  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;
      _writeData(data);
    }
  }
  _writeCommand(0x13); //NEW or RED
  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(&bitmap2[idx]);
#else
        data = bitmap2[idx];
#endif
      }
      else
      {
        data = bitmap2[idx];
      }
      if (invert) data = ~data;
      _writeData(data);
    }
  }
  _writeCommand(0x92); // partial out
  delay(1); // yield() to avoid WDT on ESP8266 and ESP32
}

And you can use with:

display.writeNative(img,img2,0,0,400,300,false,false,true);
display.refresh(1);

where img is the old image, and img2 is the new image.

Note: The white/black/red 4.2 display use the same IC, but one buffer is for the black and one for the red. No partial refresh is possible, and this explains why the refresh for a BWR display is always a "full" (and slow) refresh (~4 seconds).