Go Down

Topic: Waveshare e-paper displays with SPI (Read 195088 times) previous topic - next topic

AurelSam

Hi @ZinggJM,

I tried to modify the library file as you told me :

Code: [Select]
void GxEPD2_AVR_BW::setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
  _pw_x = gx_uint16_min(x, width());
  _pw_y = gx_uint16_min(y, height());
  _pw_w = gx_uint16_min(w, width() - _pw_x);
  _pw_h = gx_uint16_min(h, height() - _pw_y);
  _rotate(_pw_x, _pw_y, _pw_w, _pw_h);
  _using_partial_mode = true;
}


But nothing changed and I've got the same issue (with a character on the right of the screen).

So I made a simplified version of my project to show you what is happening. In this projet, the function "dispModif()" seems to be the problem.



Thanks for your help.
Regards

ZinggJM

@AurelSam, Hi Aurel,

There are some missing fillScreen(GxEPD_WHITE) in GxEPD2_AVR. In GxEPD2 this is done.

You can avoid this issue, if you add display.fillScreen(GxEPD_WHITE); in your dispModif() as first statement in the do..while loop, like you did in dispTemp(String val, int x, int y).
The loop is executed twice for every page needed, to set both controller buffers equal (for differential refresh); this controller switches buffers after refresh, that's why OK / MENU over OK blink.

If you would use a black background, you would need display.fillScreen(GxEPD_BLACK) in any case.

Jean-Marc
No personal message please; any question may be useful for other users. Use code tags for code. Make links clickable with URL tags. Provide links to the product in question.

AurelSam

@ZinggJM

Hi,

I tried to add a display.fillScreen(GxEPD_WHITE) like you told me and it works and everything is okay now.


Thanks for your help.

Goodbye, Aurelien

Manu84

@ZinggJM,

I just read your last post about lut table from Ben and potential problem with these table 'cause they d'ont produce a symmetric field.
At this step even if I start to read the controller datasheet and view Ben's video, the how is working is not clear in my head. I must take more time to well understand it.

Well for now, I have another problem. I use your gxEPD2 but I am not able to get a correct partial update after deep sleeping my esp8266... I just have a full refresh with the display.init.

I look to the init with arguments wich normaly not get a full refresh after return from deep sleep.
I am not able to get it working.

So do you have a little example of code to use to get a working partial update after return from deep sleep instead of a full refresh ?

Regards

Manu84

ok, I answer to myself, hope it could help community:

so for partial update after deep sleep, you must use init function with alternate parameter and distinguished if its is a cold start or a return from deep sleep. if it is a cold start use true for initial parameter. otherwise use false.
That's all !

akouz

#1445
Jul 22, 2019, 03:01 am Last Edit: Jul 22, 2019, 04:06 am by akouz
Potential issue with the original wavetable from Ben Krasnow.

I took a quick look at the original wavetable from Ben Krasnow.
It looks like this wavetable doesn't produce pure differential update signals, but also "sustain" signals of equal length (it activates signals for bb (black to black) and for ww (white to white)).
As these signals are active on every refresh, even if there is no change, this produces a dc field which may affect polarization and even damage to the fluid. I saw a post saying it can even cause crystallization.

My modified wavetable also has a (separate) sustain phase, much shorter, which still seems worse than the actual demo version with the current panels. This version is no longer used by default.

I will check with the original demo version, and with alterations of the phase length.
But for e-papers of this size or bigger a more complex waveform may be needed for fast partial update.

Jean-Marc

I feel comforted to notice that the demo version also has a "set floating" phase.
This makes sure that the driving signals become inactive at the end of the refresh phase, even if no power off command is sent to the controller.
I am not sure about this point, how the controller really behaves, and it may be hard to find out.

Hi Jean-Marc,

Could you please clarify content of LUTs? I use GDEW027W3, control chip IL91874. There are 5 partial update LUTs in your GxEPD2_270.cpp library, one table for VCOM, and four for transitions WW, WB, BW, BB.

VCOM table is as follows:
Code: [Select]

const uint8_t GxEPD2_270::lut_20_vcomDC_partial[] PROGMEM =
{
  0x00, 0x00,
  0x00, 0x19, 0x01, 0x00, 0x00, 0x01,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

According to IL91874 datasheet:
- XON=0x00 - "No all gate on", whatever it means
- ST_CHV=0x00 - "No VCOM High voltage"
- First state defined (eg the first 6-byte row):
-- 0x00 - level selection (VCM_DC) for all 4 frames defined in next 4 bytes
-- 0x19 - frame number 25 defined
-- 0x01 - frame number 1 defined
-- 0x00 - frame number 0 defined
-- 0x00 - frame number 0 defined
-- 0x01 - repeat number (eg execute it once)

Does it mean that at frame numbers 0, 1, 25 it will be (VCM_DC) on VCOM pin? As far as I know, (VCM_DC) is a small dc offset specified by command "R82H (VDCS): Vcom_DC Setting register" to compensate not equal positive and negative voltages. Thus, it can be assumed as 0.

WW and BB tables essentially are the same as VCOM table. Apparently there is no active driving voltage for white/white and black/black pixels. Thus, there is no dc fields you mentioned. Nevertheless, I noticed that white background becames whiter during partial update. I guess some induced voltages are discharged by those LUTs, is it feasible?

BW table is as follows:
Code: [Select]

const uint8_t GxEPD2_270::lut_22_bw_partial[] PROGMEM =
{
  0x80, 0x19, 0x01, 0x00, 0x00, 0x01,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


- Similarly to VCOM table, only first state defined (eg first 6-byte row):
-- 0x80 - level  (VSL-VCM_DC) for first frame number,  levels (VCM_DC) for 3 other frame numbers
-- 0x19 - frame number 25 defined
-- 0x01 - frame number 1 defined
-- 0x00 - frame number 0 defined
-- 0x00 - frame number 0 defined
-- 0x01 - repeat number (eg execute once)

Apparently it sets negative voltage  (VSL-VCM_DC) during 25-th frame, and (VCM_DC) offset during frames 0 and 1.

Each row specified only 4 frames out of 256 possible and defines voltages during those frames. What is the voltage during other 252 frames? Is it floating?

I guess you set 0 (eg VCM_DC on both electrodes) during frames 0,1 to set EPD matrix into kind of initial state, then you wait 24 frames, then drive EPD during 25-th frame, then leave it floating. Is it a correct description?

Regards
 Alex

ZinggJM

#1446
Jul 22, 2019, 07:02 am Last Edit: Jul 22, 2019, 07:25 am by ZinggJM
@akouz,

The wavetables for GDEW027W3 are taken from the demo code from Good Display, no changes.
The only e-paper I used a modified wavetable for is the GDEW042T2, because originally there was no wavetable available from Good Display.
For some of the newer e-paper displays I slightly changed the phase length for partial update.

I don't know how VCOM handling works. The other LUTs determine the driving voltage and sign applied.

Quote
-- 0x19 - frame number 25 defined
No, 0x19 is the phase length of the first phase.

Quote
I guess you set 0 (eg VCM_DC on both electrodes) during frames 0,1 to set EPD matrix into kind of initial state, then you wait 24 frames, then drive EPD during 25-th frame, then leave it floating. Is it a correct description?
No, this is completely wrong. Only phases with phase length >0 are executed.

Note that several controllers have the same LUT format, but other ones have different ones.
No personal message please; any question may be useful for other users. Use code tags for code. Make links clickable with URL tags. Provide links to the product in question.

akouz

No, 0x19 is the phase length of the first phase.

No, this is completely wrong. Only phases with phase length >0 are executed.
@ZinggJM,

Thank you for clarification. It is a bit of surprise because IL91874 datasheet rev 0.3 on page 36 describes it as follows:

Code: [Select]

1st Parameter | 1st  Level selection [1:0] | 2nd Level selection [1:0] | 3rd  Level selection [1:0] | 4th level selection[1:0]
2nd Parameter | 1st  Frame number [7:0]
3rd Parameter | 2nd Frame number [7:0]
4th Parameter | 3rd Frame number[7:0]
5th Parameter | 4th Frame number[7:0]
6th Parameter | Repeat numbers[7:0]


Code: [Select]

Frame number [7:0]  | 00000000 : 0 frame
                    | 00000001 : 1 frame
                    | …
                    | 11111110 : 254 frame
                    | 11111111 : 255 frame


Maybe they mean "number of frames" while writing "frame number".

ZinggJM

#1448
Jul 23, 2019, 07:59 am Last Edit: Jul 23, 2019, 10:00 am by ZinggJM
I think with "Frame number" they mean "number for the frame" and with "number" they mean "number of clock cycles": :)

In IL0373.pdf they name it "NUMBER OF FRAMES", which is not clearer either.

We need to apply some common sense when reading "Chinese" specifications.
No personal message please; any question may be useful for other users. Use code tags for code. Make links clickable with URL tags. Provide links to the product in question.

51M0N95

Hi there!

is there a way to identify different EPDs via micro controller?

For example I want to know if a 1.54 inch [1] or a 7.5 inch EPD [2] is attached and therefore do a different EPD handling...



[1] https://www.waveshare.com/wiki/1.54inch_e-Paper_Module_(B)
[2] https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT_(B)


Greetings! :)

ZinggJM

@51M0N95,

The "Hi there!" most likely doesn't know. The "Who knows" might know something about this.

Most of these e-paper controllers have some read capability through SDIO SPI on the DIN pin.
But most don't have an ID like TFT controllers.
For details you would need to look at the controller specifications.
You can find the controller specs on the Good Display web site.

The standard Arduino SPIClass doesn't support SDIO SPI, only standard SPI with separate MISO and MOSI.

I have not yet tried to read from these controllers.

Please make web-links clickable by using the "Insert a link" command symbol.
No personal message please; any question may be useful for other users. Use code tags for code. Make links clickable with URL tags. Provide links to the product in question.

do5rsw

Is it possible that the current GitHub Version is at least partially broken?

I just encountered this:

Setup: Arduino IDE 1.8.9 on Linux 64Bit
Latest GitHub Clone from ZinggJM's Repo
Arduino Mega 2650/ADK
2.9" Waveshare Epaper Display

wired like suggested in the comments in the GxEPD2_Example Sketch
imported the Libs from Example dir and src dir.

Upon compiling I have no Error reports about missing Headerfiles or missing things at all but I get a Linker failure when avr-gcc tries to link GxEPD2_Example together that reports various undefined references to functions that are decleared in GxEPD2_290.h :

Quote
/home/binarykitchen/Downloads/arduino-1.8.9/hardware/tools/avr/bin/avr-gcc -w -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections,--relax -mmcu=atmega2560 -o /tmp/arduino_build_237695/GxEPD2_Example.ino.elf /tmp/arduino_build_237695/sketch/GxEPD2_Example.ino.cpp.o /tmp/arduino_build_237695/libraries/src/GxEPD2_EPD.cpp.o /tmp/arduino_build_237695/libraries/Adafruit-GFX-Library-master/Adafruit_GFX.cpp.o /tmp/arduino_build_237695/libraries/Adafruit-GFX-Library-master/Adafruit_SPITFT.cpp.o /tmp/arduino_build_237695/libraries/Adafruit-GFX-Library-master/glcdfont.c.o /tmp/arduino_build_237695/libraries/SPI/SPI.cpp.o /tmp/arduino_build_237695/core/core.a -L/tmp/arduino_build_237695 -lm
/tmp/ccD0vV5l.ltrans1.ltrans.o: In function `main':
<artificial>:(.text.startup+0xa0e): undefined reference to `GxEPD2_290::powerOff()'
<artificial>:(.text.startup+0xb18): undefined reference to `GxEPD2_290::hibernate()'
<artificial>:(.text.startup+0xd1c): undefined reference to `GxEPD2_290::hibernate()'
/tmp/ccD0vV5l.ltrans2.ltrans.o: In function `GxEPD2_BW<GxEPD2_290, 296u>::nextPage() [clone .constprop.45]':
<artificial>:(.text+0x1ec): undefined reference to `GxEPD2_290::writeImage(unsigned char const*, int, int, int, int, bool, bool, bool)'
<artificial>:(.text+0x214): undefined reference to `GxEPD2_290::refresh(int, int, int, int)'
<artificial>:(.text+0x268): undefined reference to `GxEPD2_290::writeImage(unsigned char const*, int, int, int, int, bool, bool, bool)'
<artificial>:(.text+0x272): undefined reference to `GxEPD2_290::refresh(bool)'
<artificial>:(.text+0x360): undefined reference to `GxEPD2_290::writeImage(unsigned char const*, int, int, int, int, bool, bool, bool)'
<artificial>:(.text+0x3be): undefined reference to `GxEPD2_290::refresh(int, int, int, int)'
<artificial>:(.text+0x40a): undefined reference to `GxEPD2_290::writeImage(unsigned char const*, int, int, int, int, bool, bool, bool)'
<artificial>:(.text+0x462): undefined reference to `GxEPD2_290::refresh(bool)'
<artificial>:(.text+0x47e): undefined reference to `GxEPD2_290::powerOff()'
/tmp/ccD0vV5l.ltrans2.ltrans.o: In function `global constructors keyed to 65535_0_GxEPD2_Example.ino.cpp.o.3025':
<artificial>:(.text.startup+0x7a): undefined reference to `GxEPD2_290::GxEPD2_290(signed char, signed char, signed char, signed char)'
<artificial>:(.text.startup+0x136): undefined reference to `vtable for GxEPD2_290'
<artificial>:(.text.startup+0x138): undefined reference to `vtable for GxEPD2_290'
collect2: error: ld returned 1 exit status
Bibliothek src im Ordner: /home/binarykitchen/Arduino/libraries/src (legacy) wird verwendet
Bibliothek Adafruit-GFX-Library-master in Version 1.5.6 im Ordner: /home/binarykitchen/Arduino/libraries/Adafruit-GFX-Library-master  wird verwendet
Bibliothek SPI in Version 1.0 im Ordner: /home/binarykitchen/Downloads/arduino-1.8.9/hardware/arduino/avr/libraries/SPI  wird verwendet
exit status 1
Fehler beim Kompilieren für das Board Arduino Mega ADK.
However when I (additionally) istalled GxEPD and GxEPD2 from Library Manager those were gone and all worked fine.
Since the were no missing (header) files I suppose something is broken in the current GitHub version.

Cheers
Sebastian

ZinggJM

#1452
Jul 27, 2019, 07:38 am Last Edit: Jul 27, 2019, 01:42 pm by ZinggJM Reason: explanation added
@do5rsw,

Quote
Is it possible that the current GitHub Version is at least partially broken?
I don't think so.

I just updated all libraries on my 2nd notebook with Library Manager, and GxEPD2_Example compiles fine.

Quote
Latest GitHub Clone from ZinggJM's Repo
You would need to be more specific, what you did and how you installed the libraries.

Quote
Bibliothek src im Ordner: /home/binarykitchen/Arduino/libraries/src (legacy) wird verwendet
This looks suspicious. The library should be GxEPD2, not src!
Looks like you copied the src directory tree instead of the whole library!
Bad habit "learned" from the Waveshare "library"?


Note that source files are automatically included in the build process from subdirectories of src, if src is a first level subdirectory of the main directory of the library.

GxEPD2 and GxEPD can be installed or updated with the Library Manager. This method should be used.

I once had problems with update after I had replaced some files in the installed library. The Library Manager got confused with directories used for the library. In this case you need to delete the library, and do a fresh install using the Library Manager.

Installing a library using ZIP download and installing ZIP file using Library Manager also works; this is what I do on my development notebook to check a version before tagging it on GitHub to make it available for Library manager.

You should avoid to copy files from a GitHub clone to an installed library, unless you know what you do.
No personal message please; any question may be useful for other users. Use code tags for code. Make links clickable with URL tags. Provide links to the product in question.

anselmpaul

#1453
Jul 27, 2019, 02:39 pm Last Edit: Jul 27, 2019, 06:05 pm by anselmpaul
This might be a bit offtopic for this thread, but since I'm working with an e-paper display and ZinggJM wonderful library, I'm going to post here. Essentially I'm having troubles with data types in C. I'm just very bad at C.

1.) I receive an image as byte array via websockets.
2.) The payload of the websocket is JSON encoded.
3.) I use ArduinoJSON to parse the payload.
4.) I then hand over the image / byte array to the drawBitmap function of GxEPD2.

Somewhere along this way either the ESP8266 crashes, or there is just random noise displayed. The image data itself should be fine. If I take the byte array and save it in PROGMEM like here, I can load it just fine. I just don't know how to handle the incoming data (String to char array, char array to uint8_t, pointers, I just get lost...)

Here's my code:
Code: [Select]
#include <Arduino.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

#include <ArduinoJson.h>

#include <WebSocketsClient.h>
#include <SocketIOclient.h>

#include <Hash.h>

#include <GxEPD2_BW.h>
#include "test.h"


ESP8266WiFiMulti WiFiMulti;
SocketIOclient socketIO;
GxEPD2_BW<GxEPD2_213_flex, GxEPD2_213_flex::HEIGHT> display(GxEPD2_213_flex(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16)); // GDEW0213I5F

void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
    switch(type) {
        case sIOtype_DISCONNECT:
            Serial.printf("[IOc] Disconnected!\n");
            break;
        case sIOtype_CONNECT: {
            DynamicJsonDocument doc(1024);
            JsonArray array = doc.to<JsonArray>();
            array.add("identify");
            array.add("eink");
            String output;
            serializeJson(doc, output);
            socketIO.sendEVENT(output);

            // Print JSON for debugging
            Serial.println(output);
            Serial.printf("[IOc] Connected to url: %s\n", payload);
        }
            break;
        case sIOtype_EVENT:
        {
            Serial.printf("[IOc] got event: %s", payload);
            const size_t capacity = JSON_ARRAY_SIZE(2) + 15320;
            DynamicJsonDocument doc(capacity);
            DeserializationError error = deserializeJson(doc, payload, length);
            if(error) {
                Serial.printf("deserializeJson() failed: ");
                Serial.println(error.c_str());
                return;
            }



            String eventName = doc[0];
            Serial.printf("[IOc] event name: %s\n", eventName.c_str());
            String image = doc[1];
            //uint8_t test[] = doc[1];
            //Serial.println(image);
            //unsigned char* test[3000] = {doc[1]};

            if (eventName == "full") {
                fullRefresh(image);
            } else if (eventName == "part") {
                partialRefresh(image);
            } else if (eventName == "area") {
                //areaRefresh(image);
                // TODO and coords + size
            }
        }
            break;
        case sIOtype_ACK:
            Serial.printf("[IOc] get ack: %u\n", length);
            hexdump(payload, length);
            break;
        case sIOtype_ERROR:
            Serial.printf("[IOc] get error: %u\n", length);
            hexdump(payload, length);
            break;
        case sIOtype_BINARY_EVENT:
            Serial.printf("[IOc] get binary: %u\n", length);
            hexdump(payload, length);
            break;
        case sIOtype_BINARY_ACK:
            Serial.printf("[IOc] get binary ack: %u\n", length);
            hexdump(payload, length);
            break;
    }
}

void setup() {
    Serial.begin(115200);
    //Serial.setDebugOutput(true);

    WiFi.mode(WIFI_STA);

    WiFiMulti.addAP("**", "*");
    WiFiMulti.addAP("**", "*");

    while (WiFiMulti.run() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    String ip = WiFi.localIP().toString();
    Serial.printf("[SETUP] WiFi Connected %s\n", ip.c_str());

    // server address, port and URL
    //socketIO.begin("141.76.67.231", 3000);
    socketIO.begin("192.168.178.75", 3000);
    display.init(115200);

    // event handler
    socketIO.onEvent(socketIOEvent);
}

void loop() {
    socketIO.loop();
}

void fullRefresh(String image) {
    display.setFullWindow();
    Serial.println(image);
    uint8_t buffer[13779];
    image.getBytes(buffer, 13779);   
    Serial.println(image.length());
    display.firstPage();
    do
    {
        Serial.println('#');
        display.fillScreen(GxEPD_WHITE);
        display.drawBitmap(0, 0, buffer, display.epd2.WIDTH, display.epd2.HEIGHT, GxEPD_BLACK);   
    }   
    while (display.nextPage());
    Serial.println('.');
}

void partialRefresh(String image) {
    display.setPartialWindow(0, 0, display.width(), display.height());
    display.firstPage();
    do {
        Serial.println();

        display.fillScreen(GxEPD_WHITE);


        char buffer[3000];
        image.toCharArray(buffer, 3000);   
        //const unsigned char test1[] = {0XFF, ...,0XFF};
        //const unsigned char test2[] = {0XFF, ..., 0XFF};
        display.drawBitmap(0, 0, (uint8_t*) buffer, display.epd2.WIDTH, display.epd2.HEIGHT, GxEPD_BLACK);   
    }
    while (display.nextPage());
    delay(1000);
   
    //uint8_t test = image.c_str();
    //char buffer[2756];
    //const char *buffer = image.c_str();
    //image.toCharArray(buffer, 2756);
    //image.toCharArray(buffer, 2756);
}

void areaRefresh(char image) {

}

ZinggJM

@anselmpaul,

Quote
If I take the byte array and save it in PROGMEM
I am still not sure if you receive the image data in binary format or ASCII encoded.
The C source would be in ASCII. You would need to convert ASCII to binary for drawBitmap.

Unfortunately I have no experience with JSON.
No personal message please; any question may be useful for other users. Use code tags for code. Make links clickable with URL tags. Provide links to the product in question.

Go Up