Waveshare e-paper displays with SPI

Support for downloading bitmaps through Wifi to Spiffs or to display is now available in GxEPD2_32 examples for ESP8266, as well as displaying bitmaps from SD or Spiffs.

Jean-Marc

Hello, I am having some issues with my display.

I bought a 4.2-inch waveshare e-paper display off of Amazon, which has been working for me until I tried soldering the display to my ESP8266. I have checked the connections, but it just doesn't update when I call an update function. Below I have some of my code and pictures of the connections to the display.

#include <ArduinoJson.h>

#include <ESP8266WiFi.h>
#include <DebugMacros.h>
#include <HTTPSRedirect.h>

#include <GxEPD.h>
#include <GxFont_GFX.h>

#include <GxIO/GxIO_SPI/GxIO_SPI.cpp>
#include <GxIO/GxIO.cpp>

#include <GxGDEW042T2/GxGDEW042T2.cpp>      // 4.2" b/w

// Fonts

#include <Fonts/FreeSansBoldOblique12pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>

int sleepTime = 10;

bool reboot = false;

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

const char* host = "script.google.com";
String url = "";
String GScriptId = "****";

GxIO_Class io(SPI, SS, 0, 2);
GxEPD_Class display(io);

HTTPSRedirect* client = nullptr;

void setup() {
  
  Serial.begin(115200);

  while (!Serial) continue;
  
  display.init(115200);
  
  display.setTextColor(GxEPD_BLACK);
  display.fillScreen(GxEPD_WHITE);
  display.setCursor(0, 0);
  display.setRotation(3);
  
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address  ");
  Serial.println(WiFi.localIP());

  refresh();

  Serial.println("sleeping for: ");
  Serial.print(sleepTime * 10e6);

  ESP.deepSleep(sleepTime * 10e6);
}

void refresh() {
  
  url = "/macros/s/" + GScriptId + "/exec";

  client = new HTTPSRedirect();
  
  client->setPrintResponseBody(true);

  Serial.print("Connecting to ");
  Serial.println(host);

  bool flag = false;
  
  for (int i=0; i<5; i++){
    int retval = client->connect(host, 443);
    if (retval == 1) {
       flag = true;
       break;
    }
    else
      Serial.println("Connection failed. Retrying...");
  }

  if (!flag){
    Serial.print("Could not connect to server: ");
    Serial.println(host);
    Serial.println("Exiting...");
    return;
  }
  
  client->GET(url, host);

  DynamicJsonBuffer jsonBuffer(4096);

  JsonObject& root = jsonBuffer.parseObject(client->getResponseBody());

  if (!root.success()) {
    Serial.println("Parsing Failed!");
  } else {
  
    String title[root["events"].size()];
    String verbStart[root["events"].size()];
    String verbEnd[root["events"].size()];

    
    long startTime[root["events"].size()];
    long endTime[root["events"].size()];

    int hrs[root["times"].size()];
    
    String hrsTimes[root["times"].size()];
    
    uint32_t t = root["time"];
  
    String verboseTime = root["verbTime"];
  
    for (int i = 0; i < root["events"].size(); i++) {
      startTime[i] = root["events"][i]["start"];
      endTime[i] = root["events"][i]["end"];
      
      verbStart[i] = root["events"][i]["verbStart"].asString();
      verbEnd[i] = root["events"][i]["verbEnd"].asString();
      title[i] = root["events"][i]["title"].asString();
    }

    for (int i = 0; i < root["times"].size(); i++) {
      hrs[i] = root["times"][i];
      hrsTimes[i] = root["verbTimes"][i].asString();
    }
  
    display.setTextColor(GxEPD_BLACK);
    display.fillScreen(GxEPD_WHITE);

    drawDither(0, 0, display.width(), display.height());
    
    for (int i = 0; i < root["events"].size(); i++) {

      display.fillRoundRect(1, round(startTime[i] / 36) + 1, display.width() - 50, round((endTime[i] - startTime[i]) / 36) + 2, 10, GxEPD_WHITE);
      display.drawRoundRect(1, round(startTime[i] / 36) + 1, display.width() - 50, round((endTime[i] - startTime[i]) / 36) + 2, 10, GxEPD_BLACK);
      if (round(endTime[i] / 36) > 46) {
        if (round(startTime[i] / 36) + 22 > 0) {
          display.setCursor(5, round(startTime[i] / 36) + 22);
        } else {
          display.setCursor(5, 22);
        }
        display.setTextColor(GxEPD_BLACK);
        display.setFont(&FreeSansBoldOblique12pt7b);
        display.print(title[i]);
        if (round(startTime[i] / 36) + 46 > 46) {
          display.setCursor(5, round(startTime[i] / 36) + 46);
        } else {
          display.setCursor(5, 46);
        }
        display.setFont(&FreeMonoBold12pt7b);
        display.print(verbStart[i]);
        display.print("-");
        display.print(verbEnd[i]);
      }
    }
    
    for (int i = 0; i < root["times"].size(); i++) {
      display.fillRect(254, hrs[i] - 1, 47, 3, GxEPD_WHITE);
      display.drawFastHLine(255, hrs[i], 45, GxEPD_BLACK);
      
      if (hrsTimes[i].length() == 1) {
        display.fillRect(270, hrs[i] + 5, 15, 20, GxEPD_WHITE);
        display.drawRect(270, hrs[i] + 5, 15, 20, GxEPD_BLACK);
        display.setCursor(270, hrs[i] + 22);
        display.print(hrsTimes[i]);
      } else {
        display.fillRect(270, hrs[i] + 5, 24, 20, GxEPD_WHITE);
        display.drawRect(270, hrs[i] + 5, 24, 20, GxEPD_BLACK);
        display.setCursor(270, hrs[i] + 22);
        display.print(hrsTimes[i]);
      }
    }
    
    display.update();
    display.powerDown();
  }
  
  delete client;
  client = nullptr;
}

void drawDither(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
  for (int x1 = x; x1 < x + w; x1 += 2) {
    for (int y1 = y; y1 < y + h; y1 += 2) {
      display.drawPixel(x1, y1, GxEPD_BLACK);
    }
  }
}

void loop() {}

@epicface2304

I bought a 4.2-inch waveshare e-paper display off of Amazon, which has been working for me until I tried soldering the display to my ESP8266.

What has been working? What is the difference? Do you use the same wiring? Did the GxEPD_SPI_TestExample work before? Does it still work?

You could post diagnostic output of GxEPD_SPI_TestExample.

And fix your picture links.

I just realized my mistake.

It appears I have fried it... I by mistake soldered the 3.3v of the display to the 5v of the ESP8266 board and now the display is not working. I've ordered a new display, which has been working well for me. I have verified that my code is working well with the new display.

Sorry for wasting your time... :cry:

Just in case...

Here is the diagnostic information of the GxEPD_SPI_TestExample:

⸮
setup done
Power On : 9
drawBitmap : 1
drawBitmap : 1
Power Off : 2
Power On : 9
drawBitmap : 1
Power Off : 2
Power On : 10
update : 2
Power Off : 2
Power On : 6
drawCornerTest : 2
Power Off : 1
Power On : 6
update : 1
Power Off : 2
Power On : 5
update : 1
Power Off : 2
Power On : 5
update : 1
Power Off : 1
Power On : 5
update : 2
Power Off : 2
Power On : 5
update : 2
Power Off : 2
Power On : 2
update : 1
Power Off : 2
Power On : 5
drawBitmap : 5
Power Off : 1
Power On : 5
drawBitmap : 6
Power Off : 2
Power On : 9
update : 2
Power Off : 2
Power On : 2
drawCornerTest : 2
Power Off : 2
Power On : 5
update : 1
Power Off : 2
Power On : 6
update : 1
Power Off : 2
Power On : 5
update : 2
Power Off : 2

Hello everyone,

I bought a Lolin Nodemcu V3 with a Waveshare 4.2 (black & white) e-paper screen.

I uploaded a test sketch with arduino ide and Lolin is working fine (thanks to the serial print ! :wink: )
Now, I try to have my screen updated with strings, but I can't.

Can anyone give me the good wiring between Lolin Nodemcu and Waveshare ? (I've seen multiple possibilities in this thread...) With a ino sketch example (just one line written on screen) would be really great !

Thanks a lot for help !

Hello everyone,

To be more precise on my last message.
This is my configuration :

  • GxEPD-master library-properties : 2.3.14
  • arduino IDE : 1.8.5
  • 4.2inch (Black and White) Waveshare e-Paper Module
  • Lolin NodeMcu V3

My wiring pinout :
Busy -> D2
RST -> D4
DC -> D3
CS -> D8
CLK -> D5
DIN -> D7
GND -> G
VCC -> 3V

Code used (from GxFont_GFX_Example.ino) :

// include library, include base class, make path known
#include <GxEPD.h>
#include <GxGDEW042T2/GxGDEW042T2.cpp>      // 4.2" b/w

#if !defined(_GxFont_GFX_TFT_eSPI_H_)
// FreeFonts from Adafruit_GFX
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
#endif
#if defined(_ADAFRUIT_TF_GFX_H_)
#include <Fonts/Open_Sans_Bold_12pt.h>
#endif


#include <GxIO/GxIO_SPI/GxIO_SPI.cpp>
#include <GxIO/GxIO.cpp>

// generic/common.h
//static const uint8_t SS    = 15; // D8
//static const uint8_t MOSI  = 13; // D7
//static const uint8_t MISO  = 12; // D6
//static const uint8_t SCK   = 14; // D5
GxIO_Class io(SPI, /*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2); // arbitrary selection of D3(=0), D4(=2), selected for default of GxEPD_Class
GxEPD_Class display(io /*RST=D4*/ /*BUSY=D2*/); // default selection of D4(=2), D2(=4)

void setup()
{
  Serial.begin(9600);
  Serial.println();
  Serial.println("setup");

  display.init(9600); // enable diagnostic output on Serial

  Serial.println("setup done");
}

void loop()
{
#if !defined(_ADAFRUIT_TF_GFX_H_) && !defined(_GxFont_GFX_TFT_eSPI_H_) && !defined(U8g2_for_Adafruit_GFX_h)
  showFont("FreeMonoBold12pt7b", &FreeMonoBold12pt7b);
#endif
#if defined(U8g2_for_Adafruit_GFX_h)
  showFont("u8g2_font_helvR14_tf", u8g2_font_helvR14_tf); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
  delay(2000);
  showFont("u8g2_font_profont22_mr", u8g2_font_profont22_mr); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
  delay(2000);
#endif
#if defined(_GxFont_GFX_TFT_eSPI_H_)
  showFreeFont("FreeMonoBold12pt7b", &FreeMonoBold12pt7b);
  delay(2000);
  //showFreeFont("NULL", NULL);
  showTextFont("Font 1", 1, 1);
  delay(2000);
  showTextFont("Font 1", 1, 2);
  delay(2000);
  showTextFont("Font 1", 1, 3);
  delay(2000);
  showTextFont("Font 2", 2, 1);
  delay(2000);
  showTextFont("Font 2", 2, 2);
  delay(2000);
  showTextFont("Font 2", 2, 3);
  delay(2000);
  showTextFont("Font 4", 4, 1);
  delay(2000);
  showTextFont("Font 4", 4, 2);
  delay(2000);
  showTextFont("Font 4", 4, 3);
#endif
#if defined(_ADAFRUIT_TF_GFX_H_)
  showFont("Open_Sans_Bold_12pt", OPENSANSBOLD_12);
#endif
  delay(10000);
}

void showFont(const char name[], const GFXfont* f)
{
  display.fillScreen(GxEPD_WHITE);
  display.setTextColor(GxEPD_BLACK);
  display.setFont(f);
  display.setCursor(0, 0);
  display.println();
  display.println(name);
  display.println(" !\"#$%&'()*+,-./");
  display.println("0123456789:;<=>?");
  display.println("@ABCDEFGHIJKLMNO");
  display.println("PQRSTUVWXYZ[\\]^_");
#if defined(HAS_RED_COLOR)
  display.setTextColor(GxEPD_RED);
#endif
  display.println("`abcdefghijklmno");
  display.println("pqrstuvwxyz{|}~ ");
  display.update();
  delay(5000);
}

#if defined(U8g2_for_Adafruit_GFX_h)
void showFont(const char name[], const uint8_t *f)
{
  display.setRotation(0);
  display.fillScreen(GxEPD_WHITE);
  display.setFontMode(1);                   // use u8g2 transparent mode (this is default)
  display.setFontDirection(0);              // left to right (this is default)
  display.setForegroundColor(GxEPD_BLACK);  // apply Adafruit GFX color
  display.setBackgroundColor(GxEPD_WHITE);  // apply Adafruit GFX color
  display.setFont(f); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
  display.setCursor(0, 0);
  display.println();
  display.println(name);
  display.println(" !\"#$%&'()*+,-./");
  display.println("0123456789:;<=>?");
  display.println("@ABCDEFGHIJKLMNO");
  display.println("PQRSTUVWXYZ[\\]^_");
  display.println("`abcdefghijklmno");
  display.println("pqrstuvwxyz{|}~ ");
  display.println("Umlaut ÄÖÜäéöü");
  display.update();
}
#endif

#if defined(_ADAFRUIT_TF_GFX_H_)
void showFont(const char name[], uint8_t f)
{
  display.setRotation(0);
  display.fillScreen(GxEPD_WHITE);
  display.setTextColor(GxEPD_BLACK);
  display.setFont(f);
  display.setCursor(0, 0);
  display.println();
  display.println(name);
  display.println(" !\"#$%&'()*+,-./");
  display.println("0123456789:;<=>?");
  display.println("@ABCDEFGHIJKLMNO");
  display.println("PQRSTUVWXYZ[\\]^_");
  display.println("`abcdefghijklmno");
  display.println("pqrstuvwxyz{|}~ ");
  display.update();
}
#endif

#if defined(_GxFont_GFX_TFT_eSPI_H_)
void showFreeFont(const char name[], const GFXfont* f)
{
  display.setRotation(0);
  display.fillScreen(GxEPD_WHITE);
  display.setTextColor(GxEPD_BLACK);
  display.setFreeFont(f);
  display.setCursor(0, 0);
  display.println();
  display.println(name);
  display.println(" !\"#$%&'()*+,-./");
  display.println("0123456789:;<=>?");
  display.println("@ABCDEFGHIJKLMNO");
  display.println("PQRSTUVWXYZ[\\]^_");
  display.println("`abcdefghijklmno");
  display.println("pqrstuvwxyz{|}~ ");
  display.update();
}
void showTextFont(const char name[], uint8_t f, uint8_t size)
{
  display.setRotation(0);
  display.fillScreen(GxEPD_WHITE);
  display.setTextColor(GxEPD_BLACK, GxEPD_WHITE);
  display.setTextFont(f);
  display.setTextSize(size);
  display.setCursor(0, 0);
  display.println();
  display.println(name);
  display.println(" !\"#$%&'()*+,-./");
  display.println("0123456789:;<=>?");
  display.println("@ABCDEFGHIJKLMNO");
  display.println("PQRSTUVWXYZ[\\]^_");
  display.println("`abcdefghijklmno");
  display.println("pqrstuvwxyz{|}~ ");
  display.update();
}
#endif

No move on e-paper. No display on serial output : just boot's junk characters and then it hangs.

I tested the Lolin Nodemcu with another project code, it runs well, so it is'nt guilty.
I made a try with a Wemos D1, I've got exactly the same result...

I don't know what I'm doing wrong...

@johweb,

Your wiring looks correct for use with GxEPD on Lolin NodeMcu. I assume V3 uses same pin configuration as V1.

There are also different versions of Wemos D1, but it uses the same pin names Dn as Wemos D1 mini and Lolin NodeMcu 1.0.

You should at least see "setup" in Serial Monitor. Do you see it with display disconnected?

Some ESP8266 pins determine startup mode; I avoided using these as inputs.

You can set bitrate at 74880 baud to see the "boot's junk characters".

You could test with unmodified GxEPD_SPI_TestExample.

Thanks for your answer !

I made another test : I plugged the module on a Raspberry GPIO and I tried the waveshare demo code. It's working fine, so my e-paper module is OK.

I tested original GxEPD_SPI_TestExample (with just 4.2B&W line uncommented). It remains the same : I don't even see junk boot characters.
If I remove the wires between screen and Lolin, serial print give me these messages :
Power On : 1
drawBitmap : 2
Power Off : 1
Power On : 2
update : 1
Power Off : 2
Power On : 2
drawCornerTest : 2
Power Off : 2
Power On : 1
update : 2
Power Off : 2

@johweb,

I did a quick search for "Lolin NodeMcu V3" in the Arduino Forum. I didn't find an obvious reason for your issue - there are many results - but you can do this yourself.

I tried to give you a hint how to find out what might be wrong. But you need to do some testing and thinking yourself, as I have no Lolin NodeMcu V3.

The obvious next step would be to find out which line/pin causes the boot failure, so disconnect the wires one by one. BUSY is the only input to the processor, so this would be the first candidate.

ESP8266 has two different SPI connections; if you use the wrong one, flash communication can get disturbed.

Of course you get the short BUSY times of your diagnostic output with display disconnected (or long timeouts if the processor sees the opposite value on the pin used for BUSY).

Thanks for the hints.
I'll tell you if I success... :wink:

One important step for me...
Setting serial to 74880 give me translation of junk characters at boot. With e-paper module connected on spi, Nodemcu hangs saying "Bootmode (7,7) Waiting for host" :o

My screen is working !!! :slight_smile:
What I found on others forums :

Ok, after debugging for a while: the problem is with the CSN pin on GPIO pin 15. Pin 15 is also used in the ESP8266 to define boot from SD card.

This user had the same problem : bootmode 7 7 with a spi sensor connected. So I did the same as him : I moved the CS wire from GPIO 15 to GPIO 5 (D1) and changed this line in the demo code :

//GxIO_Class io(SPI, /*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2); // arbitrary selection of D3(=0), D4(=2), selected for default of GxEPD_Class
GxIO_Class io(SPI, /*CS=D1*/ 5, /*DC=D3*/ 0, /*RST=D4*/ 2); // arbitrary selection of D3(=0), D4(=2), selected for default of GxEPD_Class

And now, everything is working fine ! I'm so happy !!
Thanks for your hints, have a nice day.

Hi ZinggJM,

I want to use a Waveshare 2.9 inch B&W EPD with a MEGA 2560 R3.
The display works with a arduino nano pretty well, but the small memory is the problem, so I want to port this to an mega R3.

A few month ago (Waveshare e-paper displays with SPI - Displays - Arduino Forum) the was already a thread concerning this, but he never posted a whole code example how he got the display working with the mega.

First Im not clear about the pin allocation. Then, a small code expamle would be great.

Hope you can help me with this,

Khay.

Hi Khay,

I hope you can be helped with a quick answer; come back if you need more detailed help.

You can use the suggested pin mapping for AVR Arduino (SCK, MOSI, SS, …), just note that the SPI pins are at the ISCP header (6 pin block), not at pin 13, 11, 10.

And although you have slightly more RAM, you still need paged drawing for buffered drawing.

Start with example PagedDisplayExampleSmallRam.ino, if you use GxEPD.

GxEPD2_AVR has a "smaller" example GxEPD2_AVR_Example.ino

Don't forget the series resistors on the data lines.

Jean-Marc

Hello Jean-Marc,

thank you for the quick reply.

Now my Nano is connected like this...

GxEPD2_AVR_BW display(GxEPD2::GDEH029A1, /CS=/ SS, /DC=/ 9, /RST=/ 8, /BUSY=/ 7);
D13=CLK
D11=DIN
D10=CS
D09=DC
D08=RST
D07=BUSY
and of course 3V3,GND (RST and DC are swapped, I started with the waveshare example)

...without any serial resistors (?) I didn't get what value to put in here. Seems to work directely with the nano.

Now, how do I need to connect the MEGA in detail?
SS=Digital 53
MOSI=Digital 51
MISO=Digital 52
SCLK=Digital 53
...
(also connected to the ISP header, as you mentioned), see my attachnemt of the MEGA-Pins

what about busy, rst, DC?
Dont have any clue, what has to be connected how to the MEGA
Then, how do I need to change then display(GxEPD2::....) parameters then?

Sorry for those "basic" questions". I already read the complete thread without getting out this info clearly.

Ok, I know that it will be still a paged drawing. Maybe the page size can be increased somehow?

Thanks for your help in advance,
Khay.

I am short of time today, sorry.

connect MOSI to DIN, use e.g. 4.7k,
SCLK to CLK
SS to CS
eigher from the pins you mentioned, or from the ISCP header.

The other pins just need to correspond to the values in the constructor.

Hi Jean-Marc,

after tinkering around, I've found out a setup which works for the MEGA 2560 with the 2.9inch waveshare.

Here's my pin-allocation.

D52=CLK/SCLK
D51=DIN/MOSI
D53=CS/SS
D44=DC
D45=RST
D43=BUSY

and 3v3,GND.

The display-init should look like this:
GxEPD2_AVR_BW display(GxEPD2::GDEH029A1,53, 44, 45, 43);

Probably it's possible to connect them to any other free digital pin.
All data lines have also a 3k9 resistor in series to the Mega-digital pin.

Didn't realize, that the display init-function handles also pin-numbers like '45'.
Where in the lib are the pins assigned? How does the lib know, that there is a MEGA-CPU and not a AT328?

Khay.

Hi Khay,

I prefer to not give lessons on C++ programming here, I am not a teacher and not good at teaching or explaining.

The display-init should look like this:
GxEPD2_AVR_BW display(GxEPD2::GDEH029A1,53, 44, 45, 43);

This is not a display-init; it is the construction of an object instance named "display" of type (aka class) named "GxEPD2_AVR_BW".

The constructor of the class GxEPD2_AVR_BW can be found in the corresponding header file GxEPD2_AVR_BW.h : GxEPD2_AVR_BW.h

as you can see it uses no default parameter values, so you have to specify them, e.g. as you did.

    GxEPD2_AVR_BW(GxEPD2::Panel panel, int8_t cs, int8_t dc, int8_t rst, int8_t busy);

The code of the GxEPD2_AVR_BW class does not distinguish between MEGA and 328.
You could make the buffer size dependent on the processor, but this will not make a noticeable performance difference. The value chosen is a "good compromise" for AVR processors.

  private:

    static const uint16_t buffer_size = 800;

Jean-Marc

Hi everybody :slight_smile:

Just playing around with the GxEPD2_32_SD_BitmapExample and it is working great :slight_smile:
Thanks Jean-Marc for your work.

Does anybody has a tip on how I can create a 3 color bitmap for the 3 color dispays?
I tryed a few programms but could not find any programm that can do it :slight_smile:

Cheers,
Christian

Hi Christian,

I am still working on this topic, on enhancements and bugfixes, and on problems with WiFi download of big bitmaps on ESP8266.

You can create a normal bitmap with 24bit per pixel containing e.g. black, white and red (or yellow) parts. Support for bitmaps with 2bpp, 4bpp, 8bpp with palette will follow.

Jean-Marc