WeAct 2.9" epaper module working

Not officially supported but it works:
Code:

spi:
  
  mosi_pin: GPIO15  # aka SDA
  clk_pin: GPIO02  #aka SCL

display:
  - platform: waveshare_epaper
    model: 2.90inV2
    cs_pin: GPIO16
    dc_pin: GPIO17
    reset_pin: GPIO05
    busy_pin: GPIO18
    rotation: 270
    full_update_every: 60
    update_interval: 2s
    lambda: |-    
      it.print(0, 0, id(font1), "Hello World!");    
      it.strftime(0, 64, id(font1), "%H:%M:%S", id(esptime).now());

4 Likes

Which board does it work with?
In my case this configuration does not work with en ESP-C3 board and Esphome.

The same screen work fine with the same ESP-C3 board running Arduino code.

I was using a NodeMCU ESP32 V1.1

Code:

esp32:
  board: esp32dev
  framework:
    type: arduino

Did you use the same i/o pins? On your board, you may need to use other i/o pins.

I managed to get it to work after several tries with ESPHome config and Arduino demo file :

**ESP32-C3 pinout:

  • DC=1,
  • RST=2,
  • BUSY=3,
  • SCL(SCK)=4,
  • SDA(MOSI)=6,
  • SS=7

*ESPHome test

# Setup SPI

spi:
  clk_pin: 4
  mosi_pin: 6

# Configuration for the display
display:
  - platform: waveshare_epaper
    model: 2.90inV2
    cs_pin: 7
    dc_pin: 1
    busy_pin: 3
    reset_pin: 2
    rotation: 270°
    id: my_display
    lambda: |-
      it.printf(125, 6, id(font1), TextAlign::TOP_CENTER, "%s", "Hello World!");
      it.strftime(125, 116, id(font1), TextAlign::BOTTOM_CENTER, "%Y-%m-%d %H:%M", id(time_homeassistant).now());

Arduino demo

// base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter, uses ~1.2k more code

// enable or disable GxEPD2_GFX base class

#define ENABLE_GxEPD2_GFX 0

#include <GxEPD2_BW.h>

#include <GxEPD2_3C.h>

#include <Fonts/FreeMonoBold9pt7b.h>

// ESP32-C3 SS=7,SCL(SCK)=4,SDA(MOSI)=6,BUSY=3,RST=2,DC=1

// 2.13'' EPD Module

GxEPD2_BW<GxEPD2_213_BN, GxEPD2_213_BN::HEIGHT> display(GxEPD2_213_BN(/*CS=5*/ SS, /*DC=*/ 1, /*RST=*/ 2, /*BUSY=*/ 3)); // DEPG0213BN 122x250, SSD1680

//GxEPD2_3C<GxEPD2_213_Z98c, GxEPD2_213_Z98c::HEIGHT> display(GxEPD2_213_Z98c(/*CS=5*/ SS, /*DC=*/ 1, /*RST=*/ 2, /*BUSY=*/ 3)); // GDEY0213Z98 122x250, SSD1680

// 2.9'' EPD Module

//GxEPD2_BW<GxEPD2_290_BS, GxEPD2_290_BS::HEIGHT> display(GxEPD2_290_BS(/*CS=5*/ SS, /*DC=*/ 1, /*RST=*/ 2, /*BUSY=*/ 3)); // DEPG0290BS 128x296, SSD1680

//GxEPD2_3C<GxEPD2_290_C90c, GxEPD2_290_C90c::HEIGHT> display(GxEPD2_290_C90c(/*CS=5*/ SS, /*DC=*/ 1, /*RST=*/ 2, /*BUSY=*/ 3)); // GDEM029C90 128x296, SSD1680

void setup()

{

  pinMode(8, OUTPUT);

  digitalWrite(8, HIGH);

   

  display.init(115200,true,50,false);

  helloWorld();

  helloFullScreenPartialMode();

  delay(1000);

  if (display.epd2.hasFastPartialUpdate)

  {

    showPartialUpdate();

    delay(1000);

  }

  display.hibernate();

}

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

const char HelloWeACtStudio[] = "WeAct Studio";

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-tbh);

    display.print(HelloWorld);

    display.setTextColor(display.epd2.hasColor ? GxEPD_RED : GxEPD_BLACK);

    display.getTextBounds(HelloWeACtStudio, 0, 0, &tbx, &tby, &tbw, &tbh);

    x = ((display.width() - tbw) / 2) - tbx;

    display.setCursor(x, y+tbh);

    display.print(HelloWeACtStudio);

  }

  while (display.nextPage());

}

void helloFullScreenPartialMode()

{

  //Serial.println("helloFullScreenPartialMode");

  const char fullscreen[] = "full screen update";

  const char fpm[] = "fast partial mode";

  const char spm[] = "slow partial mode";

  const char npm[] = "no partial mode";

  display.setPartialWindow(0, 0, display.width(), display.height());

  display.setRotation(1);

  display.setFont(&FreeMonoBold9pt7b);

  if (display.epd2.WIDTH < 104) display.setFont(0);

  display.setTextColor(GxEPD_BLACK);

  const char* updatemode;

  if (display.epd2.hasFastPartialUpdate)

  {

    updatemode = fpm;

  }

  else if (display.epd2.hasPartialUpdate)

  {

    updatemode = spm;

  }

  else

  {

    updatemode = npm;

  }

  // do this outside of the loop

  int16_t tbx, tby; uint16_t tbw, tbh;

  // center update text

  display.getTextBounds(fullscreen, 0, 0, &tbx, &tby, &tbw, &tbh);

  uint16_t utx = ((display.width() - tbw) / 2) - tbx;

  uint16_t uty = ((display.height() / 4) - tbh / 2) - tby;

  // center update mode

  display.getTextBounds(updatemode, 0, 0, &tbx, &tby, &tbw, &tbh);

  uint16_t umx = ((display.width() - tbw) / 2) - tbx;

  uint16_t umy = ((display.height() * 3 / 4) - tbh / 2) - tby;

  // center HelloWorld

  display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);

  uint16_t hwx = ((display.width() - tbw) / 2) - tbx;

  uint16_t hwy = ((display.height() - tbh) / 2) - tby;

  display.firstPage();

  do

  {

    display.fillScreen(GxEPD_WHITE);

    display.setCursor(hwx, hwy);

    display.print(HelloWorld);

    display.setCursor(utx, uty);

    display.print(fullscreen);

    display.setCursor(umx, umy);

    display.print(updatemode);

  }

  while (display.nextPage());

  //Serial.println("helloFullScreenPartialMode done");

}

void showPartialUpdate()

{

  // some useful background

  helloWorld();

  // 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;

  if (display.epd2.WIDTH < 104) cursor_y = box_y + 6;

  float value = 13.95;

  uint16_t incr = display.epd2.hasFastPartialUpdate ? 1 : 3;

  display.setFont(&FreeMonoBold9pt7b);

  if (display.epd2.WIDTH < 104) display.setFont(0);

  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);

  }

}

void loop() {

  // put your main code here, to run repeatedly:

  digitalWrite(8, HIGH);

  delay(1000);

  digitalWrite(8, LOW);

  delay(1000);  

}
1 Like

Hello,
I’m trying to get a GDEY0213Z98 (2.13" b/w/r) work with esphome (not supported yet)
I was able to make it work with GxEPD2 library and arduino ide.

Did you find a way to mix both ? arduino and esphome ?

How are the ESPhome config and the Arduino demo supposed to be joined together?

Hello,
Thank you for your config file for esphome, but, when I put it in my config, I have an error. Do you have add another line to work well ? (fonts and spi)

You must have a font entry:

font:

  • file:
    type: gfonts
    family: Ubuntu Mono
    weight: bold
    id: font1
    size: 32

hmm can’t get this working with my WeAct 2.9" red/black/white ePaper with ESP32-C3 supermini from AliExpress. It’s just making the display flicker black then white, then static/snow/dithering. I can sometimes see the requested text “Hello Adam2” amongst the constant flickering. The logs complain:

[W][component:237]: Component display took a long time for an operation (230 ms).

I’m pretty sure my soldering is OK and the pins are connected as they should be between ESP32 and epaper display (triple checked) and the Home Assistant YAML config matches the pins. Powered from USB-C.

I tried different “model” but “model: 2.90inV2” does seem to be best.

Here’s my config if anyone can spot what’s wrong?


esphome:
  name: adam1
  friendly_name: adam1

esp32:
  board: esp32-c3-devkitm-1  # Use the specified board
  framework:
    type: arduino

logger:
  level: DEBUG  # Set logging level (DEBUG for verbose output)
  baud_rate: 115200  # Set the baud rate for logging output

# Enable Home Assistant API
api:
  encryption:
    key: "1234"

ota:
  - platform: esphome
    password: "1234"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Adam1 Fallback Hotspot"
    password: "1234"

captive_portal:

font:
  - file:
      type: gfonts
      family: Roboto
      weight: 900
    id: roboto_16
    size: 16

spi:
  clk_pin: 4       # SCK (SPI Clock) / SCL on the board - Green
  mosi_pin: 6      # MOSI (SPI Data Input) / SDA on the board - Yellow

display:
  - platform: waveshare_epaper
    cs_pin: 7        # CS (Chip Select) - Blue
    dc_pin: 3        # D/C (Data/Command) - White
    reset_pin: 8     # RES (Reset) - Orange
    busy_pin: 5      # BUSY (Busy signal) - Purple
    model: 2.90inv2
    rotation: 270
    full_update_every: 60
    update_interval: 60s  # Set this to 10s or longer for testing
    id: adam_epaper1
    lambda: |-
      ESP_LOGD("display", "Filling display with white...");
      it.fill(Color::WHITE);  // Clear the display
      delay(200); // Small delay

      ESP_LOGD("display", "Printing: Hello Adam...");
      it.printf(125, 6, id(roboto_16), TextAlign::TOP_CENTER, "%s", "Hello Adam2!");