(I know e-ink fatigue isn’t mechanically the same as phosphor burn-in, but it’s a useful term for the effect.)
I have an Inky Frame 7.3 that’s been running as an image frame with overlaid date and approximate time (and some sensor graphs, actually a whole thin-client setup), refreshing every ten minutes, for a few months. So that should be a few tens of thousands of refreshes.
This is a particularly drastic photo where it cleared to plain red with some white text because it was showing an error screen while the server it fetches images from was down:
It’s not normally that obvious, and it shakes out a bit if you do lots of manual clear-to-colors, but it’s definitely becoming subtly noticable. I’ve not had much luck finding actual hard specification data on these panels, but I was expecting more like millions of refrshes lifespan from what I’d gleaned—i.e. I wouldn’t be seeing this problem for years.
What I have noticed is the way the Inky refreshes the panel is not how I thought e-ink panel refreshes were supposed to work. To pinch a graphic from a completely unrelated bug report elsewhere:
The idea as I understand it that e-ink “afterburn” artefacts are a result of the ink in the cells not being pulled back to rest position and residual charges needing to be cancelled out, so first you need to “undo” the first image back to a blank slate. But what the Inky seems to do is draw the inverse of the new framebuffer contents, then flash its way inverting fullscreens to taupe, then draw the new image.
I’m saying “the Inky” instead of PicoGraphics, because it seems the refresh dance is really a single SPIO command once the graphics buffer is filled: pimoroni-pico/drivers/inky73/inky73.cpp at main · pimoroni/pimoroni-pico · GitHub (I assume the python version wraps the C++)
command(DRF, {0}); // start display refresh
I went digging for what might be the right panel to try to find an answer, and if I got it right that it’s https://www.good-display.com/product/442.html , looking at their ESP32 sample code, they define this fuction (slightly reformatted for brevity):
void PIC_display(const unsigned char* picData) {
unsigned int i,j,k;
unsigned char temp1,temp2;
unsigned char data_H,data_L,data;
//Acep_color(White); //Each refresh must be cleaned first
EPD_W21_WriteCMD(0x10);
for(i=0;i<480;i++) {
k=0;
for(j=0;j<800/2;j++) {
/* snip converting pixels into data nibbles for forum post brevity */
EPD_W21_WriteDATA(data);
}
}
//Refresh
EPD_W21_WriteCMD(0x12); //DISPLAY REFRESH
EPD_W21_WriteDATA(0x00);
delay(1); //!!!The delay here is necessary, 200uS at least!!!
lcd_chkstatus(); //waiting for the electronic paper IC to release the idle signal
}
So it’s doing the same thing of sending the new image data before the refresh command…but what I don’t is why //Acep_color(White); //Each refresh must be cleaned first
is commented out. If it wasn’t, that function would do pretty much the same as this one, but where data
is always plain white, before moving on to then sending the actual image data and doing a second refresh. (Reformatted again slightly:)
void Acep_color(unsigned char color) {
unsigned int i,j;
EPD_W21_WriteCMD(0x10);
for(i=0;i<480;i++) {
for(j=0;j<800/2;j++) {
EPD_W21_WriteDATA(color);
}
}
//Refresh
EPD_W21_WriteCMD(0x12); //DISPLAY REFRESH
EPD_W21_WriteDATA(0x00);
delay(1); //!!!The delay here is necessary, 200uS at least!!!
lcd_chkstatus(); //waiting for the electronic paper IC to release the idle signal
}
(PicoGraphics also doesn’t do the delay marked with !!!exclamation marks!!!
before busy-waiting as in this sample code, but I assume all that would cause is an early-return while the panel was still working and the risk of sending it more commands while busy—which doesn’t seem to be happening.)
So in summary:
- What is the expected refresh-cycle lifespan of the e-ink panel? Is repainting every 10 minutes simply too aggressive?
- Is the PicoGraphics library refreshing the panel properly? Should it be clearing to blank, or with a negative of the existing image, first?
- If not, would it help to work around this for now by doing a two-stage manual clear, first to a negative image (or plain taupe?),
display.update()
, then to the target image,display.update()
? At the cost of twice the refresh duration. (Inverting the current PG framebuffer contents in Python may be tricky…)
- If not, would it help to work around this for now by doing a two-stage manual clear, first to a negative image (or plain taupe?),
Thanks!