Waveshare e-paper displays with SPI

@ds-1991,

I am too lazy to go searching which library G6EJD uses, although I think it is GxEPD, to answer your question.

Consider providing all information needed.

I would propose to use the library GxEPD2, to cope easily with not enough RAM.

You could also create your display instance in heap space. Try this:

GxGDEW075T7* pDisplay = new GxGDEW075T7(...);

GxGDEW075T7& display = *pDisplay;

not verified.

Should be GxGDEW075Z08 for your e-paper display. See also in GxGDEW075Z08.h:

// divisor for AVR or limited RAM, should be factor of GxGDEW075Z08_HEIGHT
// add conditional case if the compiler complains for your target

Could add divisor 2 for ESP32. This would need paged drawing.

Jean-Marc

Hey, im using GDEW075T7 panel from Good display.

I want to clearize some things:

to send an image data we use that code:

   EPD_SendCommand(0x13);

    for (unsigned short i = 0; i < 48000; i++){
        EPD_SendData(image[i]);
    }

but what the code below does?:

   EPD_SendCommand(0x10);

    for (unsigned short i = 0; i < 48000; i++){
        EPD_SendData(0xff);
    }

why we need write other chip memory?

Other question is how to power up display.
What difference between power off and deep sleep?
When we need go to deep sleep or power off?
And how working deep sleep and power off?

is that right func of deep sleep?:

void EPD_Sleep()
{
    EPD_SendCommand(0X02);  	//power off
    EPD_7in5_V2_Readbusy();
    EPD_SendCommand(0X07);  	//deep sleep
    EPD_SendData(0xA5);
}

and to woke up we use this, right?:

void power_on()
{
    EPD_SendCommand(0x04); //POWER ON
    EPD_7in5_V2_Readbusy();
}

but what will cause this func?:

void power_off()
{
    EPD_SendCommand(0X02);  	//power off
    EPD_7in5_V2_Readbusy();
}

Thanks for any help :0)

@astr,

but what the code below does?:
why we need write other chip memory?

The GDEW075T7 can do differential refresh. 0x10 is to write to the "previous buffer".

Even if you use only full refresh, the difference between the "current" and "previous" buffers is used in some phases of the waveform applied to do the refresh. Therefore the result is better if the previous buffer is initialized to white, and you don't see random pixels during refresh.

power_on() activates the voltage generation for the panel driving voltages.
power_off() stops the voltage generation.

EDP_Sleep() sends the controller to deep sleep (minimum current use). Only buffer content is retained.
Deep sleep needs the reset line pulled low to wake the controller. It doesn't listen to any command during deep sleep.

and to woke up we use this, right?:

No.

Jean-Marc

Hello. Need advice.
I am using GxEPD2_3C display. Full refresh takes 27 seconds. Is it normal? i am printing 5 digit number only (big enough to fit screen).
Is it possible to make it faster? i tried partial update but got exactly same results.

void refresh()
{
 display.fillScreen(GxEPD_WHITE);
 display.setFullWindow();
 display.setRotation(3);
 
 display.setFont(&Arimo_Regular_100);

 display.setTextColor(GxEPD_BLACK);
 display.setCursor(5, 95);
 display.print(rtcMem.current);   // That is 5 digit value, like 94884
 while (display.nextPage());
 delay(100);
 display.powerOff();
 delay(100);
 Serial.print("UPDATE TIME: ");
 Serial.println(millis() / 1000);    // and here i got 27 second... 
}

void partialrefresh()
{
 char text[5];
 sprintf(text,"%ul",rtcMem.current);
 Serial.println("PartialRefresh");
 display.setFont(&Arimo_Regular_100);
 display.setRotation(3);
 display.setTextColor(GxEPD_BLACK);
 int16_t tbx, tby; uint16_t tbw, tbh;
 display.getTextBounds(text, 0, 0, &tbx, &tby, &tbw, &tbh);
 // center bounding box by transposition of origin:
 uint16_t x = ((display.width() - tbw) / 2) - tbx;
 uint16_t y = ((display.height() - tbh) / 2) - tby;

 uint16_t wh = Arimo_Regular_100.yAdvance;
 uint16_t wy = (display.height() * 3 / 4) - wh / 2;
 display.setPartialWindow(0, wy, display.width(), wh);
 display.firstPage();
 do
 {
   display.fillScreen(GxEPD_WHITE);
   display.setCursor(x, y);
   display.print(rtcMem.current);
 }
 while (display.nextPage());
 Serial.print("UPDATE TIME: ");
 Serial.println(millis() / 1000);
}

@KennyUA,

you are too lazy to tell what e-paper panel you use?

I am too lazy to explain, or to fix your code. Hint: search for "red particles".

You seem to have a 7.5" or 5.83" 3-color e-paper; see MyEPDs_UpdateInfos.pdf

Sorry, i thought i copied full init line from code, but no...
Its 2.9" 3 color display. And according to your information with update time is more or less normal operation, thanks.

Jean-Marc, thank you for your quick response.

G6EJD uses GxEPD2. You are right with the panel, it's a GDEW075Z08.

Is the divisor also available for GxEPD2? Or is, as I understood from the library description, paged drawing done automatically in this library?

ds-1991:
Jean-Marc, thank you for your quick response.

G6EJD uses GxEPD2. You are right with the panel, it's a GDEW075Z08.

Is the divisor also available for GxEPD2? Or is, as I understood from the library description, paged drawing done automatically in this library?

Yes, paged drawing is done automatically in GxEPD2.

This used to compile:

GxEPD2_3C<GxEPD2_750c_Z08, GxEPD2_750c_Z08::HEIGHT> display(GxEPD2_750c_Z08(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // GDEW075Z08 800x480

Maybe now you need to change to:

GxEPD2_3C<GxEPD2_750c_Z08, GxEPD2_750c_Z08::HEIGHT / 2> display(GxEPD2_750c_Z08(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // GDEW075Z08 800x480

But this is strange, the example GxEPD2_Example.ino still uses only 34% with full buffer:

Sketch uses 596253 bytes (45%) of program storage space. Maximum is 1310720 bytes.
Global variables use 111780 bytes (34%) of dynamic memory, leaving 215900 bytes for local variables. Maximum is 327680 bytes.

But this is misleading, I think only 96K is available for dram0_0_seg. This is a known ESP32 package issue.

Jean-Marc

I've change that, but then only half of the screen gets drawn.

I played around with this number and figured out, that I can go up to 13/16. Any number bigger than that won't compile. This uses 120352 bytes (36%) of dynamic memory. I also read about a limitation of dram size on ESP32 which is 160KB.

I guess I need to find another way to show the data on the screen. Maybe I'm going to create the image on a server and load it from there. Hopefully this will work.

Thank you for your help

@ds-1991,

did you understand how paged display works with GxEPD2?

the firstPage(); do{…} while (nextPage()); loop?

or are you using GxEPD instead?

I thought paged drawing is done somehow automatically in the background, but now I looked back onto the example and figured out how it works. Everything works now as expected and I can go on as planned with the project.

Thanks a lot.

Jean-Marc,
Thank you for your work making the waveshare e-papers more accessible with your library and spending so much time on here answering questions.

I'm using a Waveshare 2.9 B/W/Red display on a custom ESP32-based PCB. The display works perfectly but I am having a problem where after initializing the display, digitalWrite on pin 19 no longer works. I have a function that enables a voltage divider, takes a voltage reading, and outputs it to the screen.

I was trying to figure out why the getVbat function worked perfect in a standalone project but not at all in my epaper project so I inserted a bunch of labeled test points to determine when it stopped working. Here is the condensed applicable sections of code (link to full code at the bottom):

#include <Adafruit_LIS3DH.h>
#include <SPI.h>
//#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <EEPROM.h>
#include <Wire.h>  
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
#include <Preferences.h>
#include <Fonts/Roboto8.h> //Roboto_Regular8pt7b
#include <Fonts/SpecialElite10.h> //SpecialElite_Regular10pt7b
Preferences preferences;

//Initialize display:
//GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));

#define VOLTS 34
#define VBAT_EN 19
#define V_CAL_A 0.003578
#define V_CAL_B 0//0.3827

void getVbat(){
  int V1raw;
  digitalWrite(VBAT_EN,HIGH);
  V1raw=analogRead(VOLTS);
  Vbat=((float)V1raw)*V_CAL_A+V_CAL_B;
  Serial.print(VBAT_EN);Serial.print("=");
  Serial.print(digitalRead(VBAT_EN));Serial.print(" ");
  Serial.print(" Vraw=");Serial.print(V1raw);
  Serial.print(" Vbat=");Serial.print(Vbat);
  digitalWrite(VBAT_EN,LOW);
  Serial.print(" ");Serial.print(VBAT_EN);Serial.print("=");
  Serial.println(digitalRead(VBAT_EN));
}


void setup() {
  analogReadResolution(11);
  analogSetPinAttenuation(VOLTS, ADC_11db); //6db range 0-2.2v
  pinMode(VOLTS, INPUT);
  pinMode(VBAT_EN, OUTPUT);
  Serial.begin(115200);
  Serial.print("A ");  getVbat();
    // Initialize SPIFFS
  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  Serial.print("B ");  getVbat();
  btStop();
  WiFi.mode(WIFI_OFF);
  //SERVER SETUP
  Serial.print("C ");  getVbat();
    // Route for root / web page
  server.on("/wifi.html", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("/wifi.html");

//... LOTS OF WEB SERVER CODE OMITTED

  Serial.print("K "); getVbat();    
  display.init();  //Initialize e-paper
  Serial.print("L "); getVbat();
 //Setup LIS3DH Accelerometer:
  lis.begin(0x19);
  lis.setRange(LIS3DH_RANGE_2_G);   // 2, 4, 8 or 16 G!
  lis.setDataRate(LIS3DH_DATARATE_100_HZ); //1,10,25,50,100,200,400,LIS3DH_DATARATE_POWERDOWN 
  lis.setClick(2, CLICKTHRESHHOLD, TIMELIMIT, TIMELATENCY, TIMEWINDOW);
  Serial.print("M "); getVbat();
  loadData(); //Load data from SPIFFS
  Serial.print("N "); getVbat();
  refreshDisplay();
  WakeTime=millis();
}

The console output looks like this:

ets Jun  8 2016 00:22:57

rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1100
load:0x40078000,len:10088
load:0x40080400,len:6380
entry 0x400806a4
A 19=1  Vraw=967 Vbat=3.46 19=0
B 19=1  Vraw=967 Vbat=3.46 19=0
C 19=1  Vraw=965 Vbat=3.45 19=0
D 19=1  Vraw=967 Vbat=3.46 19=0
E 19=1  Vraw=966 Vbat=3.46 19=0
F 19=1  Vraw=967 Vbat=3.46 19=0
G 19=1  Vraw=966 Vbat=3.46 19=0
H 19=1  Vraw=967 Vbat=3.46 19=0
I 19=1  Vraw=967 Vbat=3.46 19=0
J 19=1  Vraw=967 Vbat=3.46 19=0
K 19=1  Vraw=967 Vbat=3.46 19=0
L 19=0  Vraw=0 Vbat=0.00 19=0
M 19=0  Vraw=0 Vbat=0.00 19=0
Reading data from EEPROM...
Read cycle time: [E][Preferences.cpp:354] getUInt(): nvs_get_u32 fail: cycleTime NOT_FOUND
N 19=0  Vraw=0 Vbat=0.00 19=0
Starting to refresh display.

Loading image '/logo1.bmp'
File size: 19062
Image Offset: 118
Header size: 40
Bit Depth: 4
Image size: 128x296
loaded in 68 ms
Done refreshing display.

If you notice, between battery reads "K" and "L" the VOLTS_EN pin (19) is no longer HIGH. The only thing that happens in the code between K and L is initializing the display:

Serial.print("K "); getVbat();    
  display.init();  //Initialize e-paper
  Serial.print("L "); getVbat();

Link to full code: here

Am I doing something wrong?

@mattard,

to answer your question with certainty, I would need to know which board you select to compile for.

But most boards use the standard hardware SPI pins for the SPIClass.

See e.g. in C:\Users\xxx\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\variants\d32\d32_core.h

static const uint8_t SS    = 5;
static const uint8_t MOSI  = 23;
static const uint8_t MISO  = 19;
static const uint8_t SCK   = 18;

The SPIClass initializes pin 19 for use as MISO pin. After that it can't be used for anything else, until SPI.end() is called.

You can remap HW SPI pins on ESP32, see example GxEPD2_WS_ESP32_Driver.ino

 // *** special handling for Waveshare ESP32 Driver board *** //
  // ********************************************************* //
  SPI.end(); // release standard SPI pins, e.g. SCK(18), MISO(19), MOSI(23), SS(5)
  //SPI: void begin(int8_t sck=-1, int8_t miso=-1, int8_t mosi=-1, int8_t ss=-1);
  SPI.begin(13, 12, 14, 15); // map and init SPI pins SCK(13), MISO(12), MOSI(14), SS(15)
  // *** end of special handling for Waveshare ESP32 Driver board *** //
  // **************************************************************** //

I think this should also work if you only change the pin for MISO; use any free pin for this.

You could also just edit d32_core.h; this should also work. But you would need to remember/redo after any platform update.

Jean-Marc

Version 1.2.6 of Library GxEPD2 is available through Library Manager.

  • slightly improved differential refresh for GDEW1248T3
  • minor fixes

Jean-Marc

ZinggJM:
@mattard,

to answer your question with certainty, I would need to know which board you select to compile for.

But most boards use the standard hardware SPI pins for the SPIClass.

See e.g. in C:\Users\xxx\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\variants\d32\d32_core.h

static const uint8_t SS    = 5;

static const uint8_t MOSI  = 23;
static const uint8_t MISO  = 19;
static const uint8_t SCK   = 18;




The SPIClass initializes pin 19 for use as MISO pin. After that it can't be used for anything else, until SPI.end() is called.

You can remap HW SPI pins on ESP32, see example GxEPD2_WS_ESP32_Driver.ino



// *** special handling for Waveshare ESP32 Driver board *** //
 // ********************************************************* //
 SPI.end(); // release standard SPI pins, e.g. SCK(18), MISO(19), MOSI(23), SS(5)
 //SPI: void begin(int8_t sck=-1, int8_t miso=-1, int8_t mosi=-1, int8_t ss=-1);
 SPI.begin(13, 12, 14, 15); // map and init SPI pins SCK(13), MISO(12), MOSI(14), SS(15)
 // *** end of special handling for Waveshare ESP32 Driver board *** //
 // **************************************************************** //




I think this should also work if you only change the pin for MISO; use any free pin for this.

You could also just edit d32_core.h; this should also work. But you would need to remember/redo after any platform update.

Jean-Marc

Thanks for the reply! That certainly must be it, I have compiled for "ESP32 Dev Module". I am using the standard SPI pins except I had repurposed the MISO pin. I have tried remapping MISO to 27 or 35 and adding a short delay but it still doesn't seem to be releasing pin 19:

  display.init();  //Initialize e-paper
  SPI.end();// release standard SPI pins, e.g. SCK(18), MISO(19), MOSI(23), SS(5)
  SPI.begin(18,27,23,5); // restart SPI on pins, SCK(18), MISO(27), MOSI(23), SS(5)
  delay(10);

EDIT:
I found by moving the custom SPI.begin statement to the top of the setup function that the SPI was initialized correctly. I'm not sure what the difference is--in a test sketch, I initiated SPI on default pins, ended it, and then started again on custom pins and it worked fine.

Hi,
I'm successfully using SPI library with GDEW029T5 display and I'm actually able to create nice gui and render pictures.
The only little issue is that my board's display [ GitHub - sqfmi/badgy: Home of Badgy - IoT Badge ver 2c] supports 4 color (grayscale) so I'm curious if support for 4 color will be implemented.
At the moment I've combined the grayscale example available on the badgy repo with SPI library and I need to switch between them when needed.

Thanks for any reply

@dadokkio,

you forgot to mention which library you use! What do you mean by SPI library?

You could take a look at library GxEPD2_4G

Note that this library is experimental.

You would need to download it as ZIP-file, and install it with the Add ZIP-file method of Library Manager.

Jean-Marc

Sorry.. the import, based on the main badgy example, at the moment are:

#include <SPI.h>
#include <GxEPD.h>
#include <GxGDEW029T5/GxGDEW029T5.h>
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>

Thanks for the quick reply, I'm going to test that now.

@dadokkio,

I'm successfully using SPI library with GDEW029T5 display

Thanks, now I know what you mean :slight_smile:

GxEPD is the e-paper display library you use; GxEPD uses the SPI library for the connection through SPI.

I need to switch between them when needed.

I still don't know what you mean by this.

Now I know. I need to teach each Newbie to provide clickable links to things they mention!

This is a standalone example not using an e-paper library.

Please also read General Guidance and How to use the Forum

For a first post I've made some mistakes ::slight_smile: .. sorry again.
In any case, you are correct. When I need to plot a 4 color bitmap I had to to reinitialize the display and use all the boilerplate code.

Now it's way better :smiley: :smiley: I just need to change the logic I used to update the display but everything is working.

Thanks again! I'll provide feedback soon.