// 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! // // Display Library based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html // // Author: Jean-Marc Zingg // // Version: see library.properties // // Library: https://github.com/ZinggJM/GxEPD2 // // Purpose: show uses of GxEPD2_GFX base class for references to a display instance // Supporting Arduino Forum Topics: // Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0 // Good Display ePaper for Arduino: https://forum.arduino.cc/index.php?topic=436411.0 // see GxEPD2_wiring_examples.h for wiring suggestions and examples // base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter, uses ~1.2k more code // enable GxEPD2_GFX base class #define ENABLE_GxEPD2_GFX 1 // uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX //#include // Note: if you use this with ENABLE_GxEPD2_GFX 1: // uncomment it in GxEPD2_GFX.h too, or add #include before any #include // !!!! ============================================================================================ !!!! #include #include #include #include #include "BitmapDisplay.h" #include "TextDisplay.h" // select the display constructor line in one of the following files (old style): #include "GxEPD2_display_selection.h" #include "GxEPD2_display_selection_added.h" //#include "GxEPD2_display_selection_more.h" // private // or select the display class and display driver class in the following file (new style): #include "GxEPD2_display_selection_new_style.h" static const uint8_t EPD_BUSY = 25; static const uint8_t EPD_CS = 15; static const uint8_t EPD_RST = 26; static const uint8_t EPD_DC = 27; static const uint8_t EPD_SCK = 13; static const uint8_t EPD_MISO = 12; // Master-In Slave-Out not used, as no data from display static const uint8_t EPD_MOSI = 14; GxEPD2_3C display(GxEPD2_750c_Z08(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)); // GDEW075Z08 800x480, GD7965 BitmapDisplay bitmaps(display); void setup() { Serial.begin(115200); Serial.println(); Serial.println("setup"); delay(100); //display.init(115200); // default 10ms reset pulse, e.g. for bare panels with DESPI-C02 display.init(115200, true, 2, false); // USE THIS for Waveshare boards with "clever" reset circuit, 2ms reset pulse SPI.end(); SPI.begin(EPD_SCK, EPD_MISO, EPD_MOSI, EPD_CS); // first update should be full refresh helloWorld(display); delay(1000); // partial refresh mode can be used to full screen, // effective if display panel hasFastPartialUpdate helloFullScreenPartialMode(display); delay(1000); helloArduino(display); delay(1000); helloEpaper(display); delay(1000); //helloValue(display, 123.9, 1); //delay(1000); showFont(display, "FreeMonoBold9pt7b", &FreeMonoBold9pt7b); delay(1000); //BitmapDisplay(display).drawBitmaps(); bitmaps.drawBitmaps(); //return; if (display.epd2.hasPartialUpdate) { showPartialUpdate(); delay(1000); } // else // on GDEW0154Z04 only full update available, doesn't look nice //drawCornerTest(); //showBox(16, 16, 48, 32, false); //showBox(16, 56, 48, 32, true); display.powerOff(); deepSleepTest(); Serial.println("setup done"); } void loop() { } void deepSleepTest() { //Serial.println("deepSleepTest"); const char hibernating[] = "hibernating ..."; const char wokeup[] = "woke up"; const char from[] = "from deep sleep"; const char again[] = "again"; display.setRotation(1); display.setFont(&FreeMonoBold9pt7b); display.setTextColor(GxEPD_BLACK); int16_t tbx, tby; uint16_t tbw, tbh; // center text display.getTextBounds(hibernating, 0, 0, &tbx, &tby, &tbw, &tbh); 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(hibernating); } while (display.nextPage()); display.hibernate(); delay(5000); display.getTextBounds(wokeup, 0, 0, &tbx, &tby, &tbw, &tbh); uint16_t wx = (display.width() - tbw) / 2; uint16_t wy = (display.height() / 3) + tbh / 2; // y is base line! display.getTextBounds(from, 0, 0, &tbx, &tby, &tbw, &tbh); uint16_t fx = (display.width() - tbw) / 2; uint16_t fy = (display.height() * 2 / 3) + tbh / 2; // y is base line! display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.setCursor(wx, wy); display.print(wokeup); display.setCursor(fx, fy); display.print(from); } while (display.nextPage()); 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.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.setCursor(hx, hy); display.print(hibernating); display.setCursor(ax, ay); display.print(again); } while (display.nextPage()); display.hibernate(); //Serial.println("deepSleepTest done"); } void showBox(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool partial) { //Serial.println("showBox"); display.setRotation(1); if (partial) { display.setPartialWindow(x, y, w, h); } else { display.setFullWindow(); } display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.fillRect(x, y, w, h, GxEPD_BLACK); } while (display.nextPage()); //Serial.println("showBox done"); } void drawCornerTest() { display.setFullWindow(); display.setFont(&FreeMonoBold9pt7b); display.setTextColor(GxEPD_BLACK); for (uint16_t r = 0; r <= 4; r++) { display.setRotation(r); display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.fillRect(0, 0, 8, 8, GxEPD_BLACK); display.fillRect(display.width() - 18, 0, 16, 16, GxEPD_BLACK); display.fillRect(display.width() - 25, display.height() - 25, 24, 24, GxEPD_BLACK); display.fillRect(0, display.height() - 33, 32, 32, GxEPD_BLACK); display.setCursor(display.width() / 2, display.height() / 2); display.print(display.getRotation()); } while (display.nextPage()); delay(2000); } } // 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() // showPartialUpdate() purposely uses values that are not multiples of 8 to test this void showPartialUpdate() { // some useful background helloWorld(display); // use asymmetric values for test uint16_t box_x = 10; uint16_t box_y = 15; uint16_t box_w = 70; uint16_t box_h = 20; uint16_t cursor_y = box_y + box_h - 6; float value = 13.95; uint16_t incr = display.epd2.hasFastPartialUpdate ? 1 : 3; display.setFont(&FreeMonoBold9pt7b); display.setTextColor(GxEPD_BLACK); // show where the update box is for (uint16_t r = 0; r < 4; r++) { display.setRotation(r); display.setPartialWindow(box_x, box_y, box_w, box_h); display.firstPage(); do { display.fillRect(box_x, box_y, box_w, box_h, GxEPD_BLACK); //display.fillScreen(GxEPD_BLACK); } while (display.nextPage()); delay(2000); display.firstPage(); do { display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE); } while (display.nextPage()); delay(1000); } //return; // show updates in the update box for (uint16_t r = 0; r < 4; r++) { display.setRotation(r); display.setPartialWindow(box_x, box_y, box_w, box_h); for (uint16_t i = 1; i <= 10; i += incr) { display.firstPage(); do { display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE); display.setCursor(box_x, cursor_y); display.print(value * i, 2); } while (display.nextPage()); delay(500); } delay(1000); display.firstPage(); do { display.fillRect(box_x, box_y, box_w, box_h, GxEPD_WHITE); } while (display.nextPage()); delay(1000); } }