Waveshare e-paper displays with SPI

@robgreate, Hi Rob,

The new Arduino Nano RP2040 is supported by the Arduino IDE as a normal target processor, afaik. But I don't know it yet, and don't have it yet. So it is not (yet) supported by GxEPD2.
The examples of GxEPD2 have constructors or #defines to select the e-paper and the constructor parameters. The constructor(s) are in conditional compile sections. These conditional compile sections exist only for the processors I have and chose to test with.

For any new processor you either need to add a conditional compile section, or add a constructor outside a conditional compile section. Otherwise "display" is not defined.
Thus, Without any chances in the example code, you have a chance, but you need a change!

Please note that this topic is intended for use of e-paper displays with the Arduino IDE and mainly for use with my libraries GxEPD2 and GxEPD.

For any use of other programming environments, e.g. RasPi, please start a new topic. And maybe in a different forum.

Jean-Marc

I wonder why SPI interface is not initialized in void setup.

It is, by the call to init().

It would also be very kind if you update the libary for mBed Core (maybe already in progress!?).

Not planned, I have no time for this.

1 Like

@frankvnz, Hi Frank,

I congratulate you for having got GxEPD2 to work on RasPi. Nice initial work.

But I think you have a way to go to make this usable for general use.
I don't like the inclusion of sources from a separate library of mine, but of course you are free to do this, as long as you include the copyright and license information.

A first look at your sources confirms my impression that these libraries (my GxEPD2 and your GxEPD2-RPi) should be kept separate.

You are welcome to add references to your topic on your progress.
But keep in mind that this (my) topic is dedicated to the use of e-papers with the Arduino IDE and my libraries.

Jean-Marc

Ok I'll try to get it running and will let you know about success.
Thanks for helping me up.
:wave:

Hi Rob,

there are coincidences. I try to take conclusions. And order hardware.

Yesterday, when I started another instance of the Arduino IDE, I was prompted for another library update. I updated TFT_eSPI, and noticed it supports now Arduino Nano RP2040.

I did a quick search on AliExpress for RP2040 and found a lot of suppliers for a RP2040 RasPi.
Then I looked on arduino.cc and found this The Arduino Nano RP2040 Connect is here, which is quite different.

The prices are reasonable, so I think I will order both.

At my age, it is always a challenge to learn new things. But its exactly what I am up to, and it is my most important medicine,

Jean-Marc

I've made some progress with my code, and for the most part the 2.66" display is working well. However I'm running into an issue where any of the example code I have that has partial updates, the initial screen is written and that's it. The code continues to loop in the background as my serial print statements keep getting output, but no updates are made to the display. I'm assuming this is some issue with the copied over code from the 2.9" display we discussed in #2615 / #2616. If you have any other pointers they would be appreciated. Here is my current code.

#define ENABLE_GxEPD2_GFX 0
#include <GxEPD2_BW.h>
#include <U8g2_for_Adafruit_GFX.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

#define MAX_DISPLAY_BUFFER_SIZE 800 
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
GxEPD2_BW<GxEPD2_266_T90, GxEPD2_266_T90::HEIGHT> display(GxEPD2_266_T90(/*CS=D8*/ 15, /*DC=D3*/ 4, /*RST=D4*/ 2, /*BUSY=D2*/ 5)); // GDEM0266T90

U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;

const char* ssid     = "...";
const char* password = "...";

void setup()
{
  Serial.begin(115200);
  if (!WiFi.getAutoConnect() || ( WiFi.getMode() != WIFI_STA) || ((WiFi.SSID() != ssid) && String(ssid) != "........"))
  {
    WiFi.mode(WIFI_STA); // switch off AP
    WiFi.begin(ssid, password);
    delay(1000);
  }

  display.init();
  u8g2Fonts.begin(display); // connect u8g2 procedures to Adafruit GFX
}

void loop()
{
  displayValues();
  delay(300);  
}


void displayValues()
{
  int depth;
  int speed;
  
  if (WiFi.status() == WL_CONNECTED)
  {
    HTTPClient http;
    http.begin("http://192.168.0.127:1880/data/boat.json");
    int httpCode = http.GET();

    if (httpCode > 0)
    {
      const size_t bufferSize = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(8) + 370;
      DynamicJsonDocument doc(bufferSize);
      deserializeJson(doc, http.getString());
 
      depth = atoi(doc["depth"]); 
      speed = atoi(doc["speed"]); 
 
      Serial.print("Depth:");
      Serial.println(depth);
      Serial.print("Speed:");
      Serial.println(speed);
    }
    delay(300);
  }
  display.setRotation(1); // 0--> No rotation ,  1--> rotate 90 deg
  uint16_t bg = GxEPD_WHITE;
  uint16_t fg = GxEPD_BLACK;
  u8g2Fonts.setFontMode(1);                 // use u8g2 transparent mode (this is default)
  u8g2Fonts.setFontDirection(0);            // left to right (this is default)
  u8g2Fonts.setForegroundColor(fg);         // apply Adafruit GFX color
  u8g2Fonts.setBackgroundColor(bg);         // apply Adafruit GFX color
  u8g2Fonts.setFont(u8g2_font_logisoso92_tn); //u8g2_font_logisoso32_tn--->numbers only to save memory ; u8g2_font_logisoso32_tr , u8g2_font_logisoso32_tf -->numbers&letters
  //Serial.println(display.hasFastPartialUpdate());
  //int16_t tbx, tby; uint16_t tbw, tbh;
  uint16_t x = 50;  // ((display.width() - tbw) / 2) - tbx;
  uint16_t y = 122; // ((display.height() - tbh) / 2) - tby;
  
  // Display A0 value
  //#define width u8g2Fonts.getUTF8Width("20")
  //#define display_width  (180 - width)
  display.setPartialWindow(0, 0, 296, 152); //this sets a window for the partial update, so the values can update without refreshing the entire screen.
  display.firstPage();
  do
  {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(x,y);
    u8g2Fonts.print(depth);
    u8g2Fonts.setFont(u8g2_font_logisoso20_tf);
    u8g2Fonts.setCursor(200,90);
    u8g2Fonts.println("depth");
    u8g2Fonts.setCursor(200,y);
    u8g2Fonts.println("feet");
  }
  while (display.nextPage());
}

@jerdavismn,

I can't help you, as I don't have this display.

The class GxEPD2_290_T94 uses the OTP waveform tables in the SSD1680 controller for both full refresh and partial refresh. These OTP waveforms are programmed by the manufacturer for the specific panel used. You can take a look at the Good Display demo for the GDEM0266T90, to see if they also use OTP for differential refresh, or program the waveform table to registers. Or if they don't use differential refresh at all.

I don't want to spend time on a display I don't have.

Jean-Marc

Added: you could enable diagnostic output of the library.

I was able to get diagnostic mode on. I have not had much luck deciphering differences between the sample code from Good Displays and what's built in.

Pulling from another example you helped with I tried adding a command that did a full write before the one that did partial updates. Here's the updated sketch.

#define ENABLE_GxEPD2_GFX 0
#include <GxEPD2_BW.h>
#include <U8g2_for_Adafruit_GFX.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

#define MAX_DISPLAY_BUFFER_SIZE 800 
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
GxEPD2_BW<GxEPD2_266_T90, GxEPD2_266_T90::HEIGHT> display(GxEPD2_266_T90(/*CS=D8*/ 15, /*DC=D3*/ 4, /*RST=D4*/ 2, /*BUSY=D2*/ 5)); // GDEM0266T90

U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;

const char* ssid     = "...";
const char* password = "...";

void setup()
{
  Serial.begin(115200);
  if (!WiFi.getAutoConnect() || ( WiFi.getMode() != WIFI_STA) || ((WiFi.SSID() != ssid) && String(ssid) != "........"))
  {
    WiFi.mode(WIFI_STA); // switch off AP
    WiFi.begin(ssid, password);
    delay(1000);
  }

  display.init(115200);
  u8g2Fonts.begin(display); // connect u8g2 procedures to Adafruit GFX
}

void loop()
{
  displayBoot();
  delay(2000);
  displayValues();
  delay(300);  
}

void displayBoot()
{
  display.setFullWindow();
  display.setRotation(1); // 0--> No rotation ,  1--> rotate 90 deg
  uint16_t bg = GxEPD_WHITE;
  uint16_t fg = GxEPD_BLACK;
  u8g2Fonts.setFontMode(1);                 // use u8g2 transparent mode (this is default)
  u8g2Fonts.setFontDirection(0);            // left to right (this is default)
  u8g2Fonts.setForegroundColor(fg);         // apply Adafruit GFX color
  u8g2Fonts.setBackgroundColor(bg);         // apply Adafruit GFX color
  u8g2Fonts.setFont(u8g2_font_logisoso20_tf); //u8g2_font_logisoso32_tn--->numbers only to save memory ; u8g2_font_logisoso32_tr , u8g2_font_logisoso32_tf -->numbers&letters
  uint16_t x = 50;  // ((display.width() - tbw) / 2) - tbx;
  uint16_t y = 122; // ((display.height() - tbh) / 2) - tby;
  

  display.firstPage();
  do
  {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(10,70);
    u8g2Fonts.print("Depth display starting...");
  }
  while (display.nextPage());
}

void displayValues()
{
  int depth;
  int speed;
  
  if (WiFi.status() == WL_CONNECTED)
  {
    HTTPClient http;
    http.begin("http://192.168.0.127:1880/data/boat.json");
    int httpCode = http.GET();

    if (httpCode > 0)
    {
      const size_t bufferSize = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(8) + 370;
      DynamicJsonDocument doc(bufferSize);
      deserializeJson(doc, http.getString());
 
      depth = atoi(doc["depth"]); 
      speed = atoi(doc["speed"]); 
 
      Serial.print("Depth:");
      Serial.println(depth);
      Serial.print("Speed:");
      Serial.println(speed);
    }
    delay(300);
  }
  
  display.setRotation(1); // 0--> No rotation ,  1--> rotate 90 deg
  uint16_t bg = GxEPD_WHITE;
  uint16_t fg = GxEPD_BLACK;
  u8g2Fonts.setFontMode(1);                 // use u8g2 transparent mode (this is default)
  u8g2Fonts.setFontDirection(0);            // left to right (this is default)
  u8g2Fonts.setForegroundColor(fg);         // apply Adafruit GFX color
  u8g2Fonts.setBackgroundColor(bg);         // apply Adafruit GFX color
  u8g2Fonts.setFont(u8g2_font_logisoso92_tn); //u8g2_font_logisoso32_tn--->numbers only to save memory ; u8g2_font_logisoso32_tr , u8g2_font_logisoso32_tf -->numbers&letters
  //Serial.println(display.hasFastPartialUpdate());
  int16_t tbx, tby; uint16_t tbw, tbh;
  uint16_t x = ((display.width() - tbw) / 2) - tbx;
  uint16_t y = ((display.height() - tbh) / 2) - tby;
  
  // Display A0 value
  //#define width u8g2Fonts.getUTF8Width("20")
  //#define display_width  (180 - width)
  display.setPartialWindow(10, 10, 296, 152); //this sets a window for the partial update, so the values can update without refreshing the entire screen.
  display.firstPage();
  do
  {
    display.fillScreen(GxEPD_WHITE);
    u8g2Fonts.setCursor(x,y);
    u8g2Fonts.print(depth);
    u8g2Fonts.setFont(u8g2_font_logisoso20_tf);
    u8g2Fonts.setCursor(200,90);
    u8g2Fonts.println("depth");
    u8g2Fonts.setCursor(200,y);
    u8g2Fonts.println("feet");
  }
  while (display.nextPage());
}

Here is the diagnostic output.

connected with magrathea, channel 8
dhcp client start...
_PowerOn : 94564
ip:192.168.0.42,mask:255.255.255.0,gw:192.168.0.1
_Update_Full : 3288232
_PowerOff : 140808
:ref 1
:wr 183 0
:wrc 183 183 0
:ack 183
:rn 293
:c0 1, 293
Depth:20
Speed:7
:close
:ur 1
:dsrcv 0
:del
_PowerOn : 95101
_Update_Part : 94555
pm open,type:2 0
_Update_Full : 3288312
_PowerOff : 139983
:ref 1
:wr 183 0
:wrc 183 183 0
:ack 183
:rn 293
:c0 1, 293
Depth:12
Speed:7
:close
:ur 1
:dsrcv 0
:del
_PowerOn : 94736
_Update_Part : 94351
_Update_Full : 3287927
_PowerOff : 140724
:ref 1
:wr 183 0
:wrc 183 183 0
:ack 183
:rn 292
:c0 1, 292
Depth:5
Speed:8
:close
:ur 1
:dsrcv 0
:del
_PowerOn : 94843
_Update_Part : 94566
_Update_Full : 3287834
_PowerOff : 140112

Is there a process to get you a display if that would aid in having this supported? Is that an option?

Yes, you could tell me in a personal message your intention to donate that display, then I will answer with my address.

Thank you for the information. Your partial update times are way too short, e.g. _Update_Part : 94555
It should be about or higher than the one for GDEM029T94, see in GxEPD2_290_T94.h:

    static const uint16_t full_refresh_time = 3200; // ms, e.g. 3154996us
    static const uint16_t partial_refresh_time = 500; // ms, e.g. 458231us

Either the controller in your panel doesn't have waveform tables for differential refresh, or it detected no difference. It may have a second set of waveform tables for something else, e.g. grey levels.

Explanation: the controller SSD1680 has one bit to select between two refresh modes, selecting one of two wavetable sets, afaik.

I assume you checked your output works with setFullWindow().

Please also check with the example GxEPD2_Example.ino. Does partial update work with this?

Jean-Marc

Thanks for the hint. Let me share my experience with this issue:

I was struggling with the 7.5inch display and waveshare e-paper hat for days! I was totally mental from this as the hat worked fine with 2.9inch displays but refused to work with the 7.5inch one. So my wiring was fine but it didn't work. On the other hand the 7.5inch display worked fine with the waveshare example library so it was really confusing.

Changing the reset time duration didn't help but as I was messing around with the PCB I tried to bypass the 3.3V regulator on the HAT (short pin 1 and 5 on the 5pin little sucker...). And it worked! I feel stupid for not discovering this sooner but I didn't suspect this as the 2.9inch display worked fine....

It also started working in ESPhome integration for Home assistant

Hi,

I am trying to use 1.54 Inch epaper display with SPI connection.

I have a couple of questions.
Is the reset and _Busy pins mandatory to get to use the display?

I have the port of SPI with Vcc GND MOSI MISO CS CLK.
Is this sufficient ?

I understand that RST and BUSY needs to be connected as GPIOs. But is it possible to use the display without these pins?

I am using a Kinetis K10 controller and established the connectiion using SPI.
Regardless of the Command or the data that I send, I am getting only noise dots on the screen.

The SPI transfer is sucessfully completed for every command and data but I am not able to clear the screen at all.

@viveknaththulasi, Hi,

welcome to the forum! Please read How to get the best out of this forum, if you haven't done yet.

I am used to get riddles as questions by first time posters. Sometimes I can guess, but I can't guess all.

From your picture I know what e-paper you use, but it would help all readers if you post a link.

You tell that you send commands and data to the display, but you don't tell what you send.
Do you use a library, and if yes, which library? Else it comes as no surprise you only get garbage.

Did your display refresh, e.g. flicker and update the noise display?

Jean-Marc

You can do without RST (pull-up high), as long as you don't use deep sleep of the display.
You can do without BUSY, if you wait long enough for commands such as refresh or power-on to terminate.

Hi @ZinggJM ,

Sorry for giving abstract infomation at first.

1.54inch e-Paper V2 version : https://www.waveshare.com/wiki/1.54inch_e-Paper_Module

I modified this library for my needs : e-Paper/EPD_1in54_V2.c at master · waveshare/e-Paper · GitHub

I am trying to initialize and clear the screen for displaying further useful information.
Attached the steps

Steps;

	_txBuffer[0] = 0x12;//SWRESET
	stat = EpdSend(_txBuffer,0,1);

	_txBuffer[0] = 0x01;//Driver output control
	stat = EpdSend(_txBuffer,0,1);

	_txBuffer[0] = 0xc7;
	_txBuffer[1] = 0x00;
	_txBuffer[2] = 0x00;
	stat = EpdSend(_txBuffer,1,3);

	_txBuffer[0] = 0x11;//Data entry sequence setting
	stat = EpdSend(_txBuffer,0,1);

	_txBuffer[0] = 0x00;
	_txBuffer[1] = 0x01;
	stat = EpdSend(_txBuffer,0,2);

	_txBuffer[0] = 0x01;
	_txBuffer[1] = 0x00;
	_txBuffer[2] = 0x00;
	stat = EpdSend(_txBuffer,1,3);

//
	_txBuffer[0] = 0x44;//set RAM-X address start/end Position
	stat = EpdSend(_txBuffer,0,1);

	_txBuffer[0] = 0x00;
	_txBuffer[1] = 0x18;
	stat = EpdSend(_txBuffer,1,2);

	_txBuffer[0] = 0x45;//set RAM-Y address start/end Position
	stat = EpdSend(_txBuffer,0,1);

	_txBuffer[0] = 0xc7;
	_txBuffer[1] = 0x00;
	_txBuffer[2] = 0x00;
	_txBuffer[3] = 0x00;
	stat = EpdSend(_txBuffer,1,4);

	_txBuffer[0] = 0x3C;//Detect temperature
	stat = EpdSend(_txBuffer,0,1);


	_txBuffer[0] = 0x01;
	stat = EpdSend(_txBuffer,1,1);

	_txBuffer[0] = 0x18;//Border Waveform
	stat = EpdSend(_txBuffer,0,1);


	_txBuffer[0] = 0x80;
	stat = EpdSend(_txBuffer,1,1);

EpdSend function	
status_t EPaperDisplay::EpdSend(uint8_t *bytes, uint8_t dataOrCmd,uint8_t datalength) {
	status_t stat;

	GPIO_ClearPinsOutput(BOARD_INITPINS_DSPI0_CS_GPIO, BOARD_INITPINS_DSPI0_CS_GPIO_PIN_MASK);
	if(dataOrCmd == 0)//cmd
		GPIO_ClearPinsOutput(BOARD_INITPINS_DSPI0_DC_GPIO, BOARD_INITPINS_DSPI0_DC_GPIO_PIN_MASK);
	else//data
		GPIO_SetPinsOutput(BOARD_INITPINS_DSPI0_DC_GPIO, BOARD_INITPINS_DSPI0_DC_GPIO_PIN_MASK);

	stat = _spi0_instance.TransferData(bytes, datalength);

	GPIO_SetPinsOutput(BOARD_INITPINS_DSPI0_CS_GPIO, BOARD_INITPINS_DSPI0_CS_GPIO_PIN_MASK);
vTaskDelay(pdMS_TO_TICKS(2));

	return stat;
}

What would be the Ideal time to wait between each command?

I am getting different noise each time I send the signal.
Also I get a blink of black border on the Screen on every execution.
After all the steps mentioned in the list, I clear and display the screen.
Baudrate of SPI : 600K

You can find some measured BUSY times here: GxEPD2_154_D67.h.

10ms after SW reset, according to specs.

You didn't answer about refresh, and I don't see any refresh command in your code snippet.
I don't intend to analyze your code, sorry.

I am getting different noise each time I send the signal.
Also I get a blink of black border on the Screen on every execution.
After all the steps mentioned in the list, I clear and display the screen.
Baudrate of SPI : 600K

Aha, didn't notice this addition.

Version 1.3.4 of library GxEPD2 is available, install or update with Library Manager.

  • added support for GDEH116T91 960x640 b/w e-paper panel
  • GDEH116T91 has only full screen refresh, no wavetable for differential refresh yet
  • added support for processor Arduino Nano RP2040 Connect to the examples
  • added general fast b/w refresh for capable 3-color displays GDEW0213Z19, GDEW029Z13
  • added example GxEPD2x_FastBlackWhiteOnColor.ino for GDEW0213Z19, GDEW029Z13
  • evaluation of other fast b/w capable 3-color panels may follow

Jean-Marc

Added: support for GDEH116T91 is thanks to the panel donated by @frightanic.

Hi @ZinggJM
Thank you for your library and effortless support. I bought a 7.5 inch B/W HD ePaper from Waveshare, which is not supported at this moment. Do you think there is any workaround how to use this display with current GxEPD2 library or I have to wait until will be fully supported?

@panEarth, Hi, welcome back!

You need to find out which controller your panel uses, e.g. on the Good Display website.
Then look if there is a b/w panel supported with the same controller (there is one!).
There is a good chance you just need to change the WIDTH and HEIGHT in the header of the driver class for this panel.

Fortunately I added controller names recently to this file: GxEPD2/GxEPD2_display_selection_new_style.h at master · ZinggJM/GxEPD2 · GitHub.

BTW: hint: b/w and b/w/r panels of same dimension quite often use the same controller.

Good Luck!

Jean-Marc

Hi Jean-Marc, thanks for your work on the library! I've been working on a 7.5" v2 board (750_T7). It works fine on a Raspberry Pi, so I know the wiring is ok and the controller functional.

[edit] Also got the good-display example code working now on a 'Arduino 33 Sense' board:

#include"Ap_29demo.h"
//IO settings
int BUSY_Pin = 8; 
int RES_Pin = 9; 
int DC_Pin = 10; 
int CS_Pin = 7; 
int SCK_Pin = 13; 
int SDI_Pin = 11; 

#define EPD_W21_MOSI_0  digitalWrite(SDI_Pin,LOW)
#define EPD_W21_MOSI_1  digitalWrite(SDI_Pin,HIGH) 

#define EPD_W21_CLK_0 digitalWrite(SCK_Pin,LOW) 
#define EPD_W21_CLK_1 digitalWrite(SCK_Pin,HIGH)

#define EPD_W21_CS_0 digitalWrite(CS_Pin,LOW)
#define EPD_W21_CS_1 digitalWrite(CS_Pin,HIGH)

#define EPD_W21_DC_0  digitalWrite(DC_Pin,LOW)
#define EPD_W21_DC_1  digitalWrite(DC_Pin,HIGH)
#define EPD_W21_RST_0 digitalWrite(RES_Pin,LOW)
#define EPD_W21_RST_1 digitalWrite(RES_Pin,HIGH)
#define isEPD_W21_BUSY digitalRead(BUSY_Pin)
////////FUNCTION//////
void driver_delay_us(unsigned int xus);
void driver_delay_xms(unsigned long xms);
void DELAY_S(unsigned int delaytime);     
void SPI_Delay(unsigned char xrate);
void SPI_Write(unsigned char value);
void EPD_W21_WriteDATA(unsigned char command);
void EPD_W21_WriteCMD(unsigned char command);
//EPD
void EPD_W21_Init(void);
void EPD_init(void);
void PIC_display1(void);
void EPD_sleep(void);
void EPD_refresh(void);
void lcd_chkstatus(void);
void PIC_display_Clean(void);
unsigned char HRES,VRES_byte1,VRES_byte2;

void setup() {
   pinMode(BUSY_Pin, INPUT); 
   pinMode(RES_Pin, OUTPUT);  
   pinMode(DC_Pin, OUTPUT);    
   pinMode(CS_Pin, OUTPUT);    
   pinMode(SCK_Pin, OUTPUT);    
   pinMode(SDI_Pin, OUTPUT);    
}


//Tips//
/*When the electronic paper is refreshed in full screen, the picture flicker is a normal phenomenon, and the main function is to clear the display afterimage in the previous picture.
  When the local refresh is performed, the screen does not flash.*/
/*When you need to transplant the driver, you only need to change the corresponding IO. The BUSY pin is the input mode and the others are the output mode. */


void loop() {
  while(1)
  {
    //PICTURE1
    EPD_init(); //EPD init
    PIC_display1();
    EPD_refresh();//EPD_refresh   
    EPD_sleep();//EPD_sleep,Sleep instruction is necessary, please do not delete!!!
    delay(5000);
    
    //PICTURE Clean
     EPD_init(); //EPD init
    PIC_display_Clean();
    EPD_refresh();//EPD_refresh   
    EPD_sleep();//EPD_sleep,Sleep instruction is necessary, please do not delete!!!
    while(1);

  }
}




///////////////////EXTERNAL FUNCTION////////////////////////////////////////////////////////////////////////
/////////////////////delay//////////////////////////////////////
void driver_delay_us(unsigned int xus)  //1us
{
  for(;xus>1;xus--);
}
void driver_delay_xms(unsigned long xms) //1ms
{  
    unsigned long i = 0 , j=0;

    for(j=0;j<xms;j++)
  {
        for(i=0; i<256; i++);
    }
}
void DELAY_S(unsigned int delaytime)     
{
  int i,j,k;
  for(i=0;i<delaytime;i++)
  {
    for(j=0;j<4000;j++)           
    {
      for(k=0;k<222;k++);
                
    }
  }
}
//////////////////////SPI///////////////////////////////////
void SPI_Delay(unsigned char xrate)
{
  unsigned char i;
  while(xrate)
  {
    for(i=0;i<2;i++);
    xrate--;
  }
}


void SPI_Write(unsigned char value)                                    
{                                                           
    unsigned char i;  
   SPI_Delay(1);
    for(i=0; i<8; i++)   
    {
        EPD_W21_CLK_0;
       SPI_Delay(1);
       if(value & 0x80)
          EPD_W21_MOSI_1;
        else
          EPD_W21_MOSI_0;   
        value = (value << 1); 
       SPI_Delay(1);
       driver_delay_us(1);
        EPD_W21_CLK_1; 
        SPI_Delay(1);
    }
}

void EPD_W21_WriteCMD(unsigned char command)
{
  SPI_Delay(1);
  EPD_W21_CS_0;                   
  EPD_W21_DC_0;   // command write
  SPI_Write(command);
  EPD_W21_CS_1;
}
void EPD_W21_WriteDATA(unsigned char command)
{
  SPI_Delay(1);
  EPD_W21_CS_0;                   
  EPD_W21_DC_1;   // command write
  SPI_Write(command);
  EPD_W21_CS_1;
}



/////////////////EPD settings Functions/////////////////////
void EPD_W21_Init(void)
{
  EPD_W21_RST_0;    // Module reset
  driver_delay_xms(1000);//At least 10ms delay 
  EPD_W21_RST_1;
  driver_delay_xms(1000);//At least 10ms delay 
  
}
void EPD_init(void)
{
    unsigned char HRES_byte1=0x03;      //800
    unsigned char HRES_byte2=0x20;
    unsigned char VRES_byte1=0x01;      //480
    unsigned char  VRES_byte2=0xE0;
  
    EPD_W21_Init(); //Electronic paper IC reset

    EPD_W21_WriteCMD(0x01);     //POWER SETTING
    EPD_W21_WriteDATA (0x07);
    EPD_W21_WriteDATA (0x07);    //VGH=20V,VGL=-20V
    EPD_W21_WriteDATA (0x3f);   //VDH=15V
    EPD_W21_WriteDATA (0x3f);   //VDL=-15V

    EPD_W21_WriteCMD(0x04);  //Power on
    lcd_chkstatus();        //waiting for the electronic paper IC to release the idle signal
  
    EPD_W21_WriteCMD(0X00);     //PANNEL SETTING
    EPD_W21_WriteDATA(0x1F);   //KW-3f   KWR-2F BWROTP 0f BWOTP 1f

    EPD_W21_WriteCMD(0x61);         //tres      
    EPD_W21_WriteDATA (HRES_byte1);   //source 800
    EPD_W21_WriteDATA (HRES_byte2);
    EPD_W21_WriteDATA (VRES_byte1);   //gate 480
    EPD_W21_WriteDATA (VRES_byte2);
  
    EPD_W21_WriteCMD(0X15);   
    EPD_W21_WriteDATA(0x00);    

    EPD_W21_WriteCMD(0X50);     //VCOM AND DATA INTERVAL SETTING
    EPD_W21_WriteDATA(0x10);
    EPD_W21_WriteDATA(0x07);

    EPD_W21_WriteCMD(0X60);     //TCON SETTING
    EPD_W21_WriteDATA(0x22);
}
void EPD_refresh(void)
{
    EPD_W21_WriteCMD(0x12);     //DISPLAY REFRESH   
    driver_delay_xms(100);          //!!!The delay here is necessary, 200uS at least!!!     
    lcd_chkstatus();
} 
void EPD_sleep(void)
{ 
    EPD_W21_WriteCMD(0X02);   //power off
    lcd_chkstatus();
  
}


void PIC_display1(void)
{
    unsigned int i;
    EPD_W21_WriteCMD(0x10);        //Transfer old data
    for(i=0;i<48000;i++)      
    EPD_W21_WriteDATA(0xff); 
  
    EPD_W21_WriteCMD(0x13);        //Transfer new data
    for(i=0;i<20000;i++)      
     EPD_W21_WriteDATA(pgm_read_byte(&gImage_1[i]));
    for(i=0;i<48000-20000;i++)      
     EPD_W21_WriteDATA(0x00); 
     

}

void PIC_display_Clean(void)
{
    unsigned int i;
    EPD_W21_WriteCMD(0x10);        //Transfer old data
    for(i=0;i<48000;i++)       
  {
    EPD_W21_WriteDATA(0xff);
  }
  
    EPD_W21_WriteCMD(0x13);        //Transfer new data    
    for(i=0;i<48000;i++)       
  {
    EPD_W21_WriteDATA(0x00);
  }
}
void lcd_chkstatus(void)
{
  unsigned char busy;
  do
  {
    EPD_W21_WriteCMD(0x71);
    busy = isEPD_W21_BUSY;
    busy =!(busy & 0x01);        
  }
  while(busy);   
  driver_delay_xms(200);                       
}

[edit2]

Nevermind - took longer than I'll admit to, but this finally worked. Might actually have to go back to the ESP32 Huzzah and try that one again too.

// GxEPD2_HelloWorld.ino by Jean-Marc Zingg

// see GxEPD2_wiring_examples.h for wiring suggestions and examples
// if you use a different wiring, you need to adapt the constructor parameters!

// uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX
//#include <GFX.h>

#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>

// select the display class and display driver class in the following file (new style):
// #include "GxEPD2_display_selection_new_style.h"

// or select the display constructor line in one of the following files (old style):
//#include "GxEPD2_display_selection.h"
//#include "GxEPD2_display_selection_added.h"

// alternately you can copy the constructor from GxEPD2_display_selection.h or GxEPD2_display_selection_added.h to here
// e.g. for Wemos D1 mini:
GxEPD2_BW<GxEPD2_750_T7, GxEPD2_750_T7::HEIGHT> display(GxEPD2_750_T7(/*CS=D10*/ 7, /*DC=D10*/ 10, /*RST=D9*/ 9, /*BUSY=D7*/ 8)); // GDEH0154D67

void setup()
{
  display.init();
  helloWorld();
  display.hibernate();
}

const char HelloWorld[] = "Hello World!";

void helloWorld()
{
  display.setRotation(1);
  display.setFont(&FreeMonoBold9pt7b);
  display.setTextColor(GxEPD_BLACK);
  int16_t tbx, tby; uint16_t tbw, tbh;
  display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  // center the bounding box by transposition of the origin:
  uint16_t x = ((display.width() - tbw) / 2) - tbx;
  uint16_t y = ((display.height() - tbh) / 2) - tby;
  display.setFullWindow();
  display.firstPage();
  do
  {
    display.fillScreen(GxEPD_WHITE);
    display.setCursor(x, y);
    display.print(HelloWorld);
  }
  while (display.nextPage());
}

void loop() {};

Hi,
I have a Waveshare E-Paper 2.7" B&W with Waveshare Esp32 board.
In my code used (GxEPD2) i have a initialise e-paper display function but its looks that if a fill all screen with white the screen won't clean from old text.
@ZinggJM any idea why ?

void initDisplay() {
  display.init(115200);
  SPI.end();
  SPI.begin(13, 12, 14, 15);
  display.setFullWindow();
  display.firstPage();
  display.fillScreen(GxEPD_WHITE);
  display.fillScreen(GxEPD_WHITE);
  display.setPartialWindow(0, 0, display.width(), display.height());
  display.fillScreen(GxEPD_WHITE);
}

@rt400,

Why don't you check with one of the examples, and then compare with your code?
Hint: missing e.g. do {...} while(display.nextPage());

this is from the example in github but i change lines to make the screen clean before display the data. in other func i have do {...} while(display.nextPage())...
also this initDisplay() run on setup() . all i want that on device boot he clean all the screen from old text.