E-ink screen and sd card, photoframe

Hello everybody,

I'm starting on my first e-ink/arduino project with an (micro) sd card.
I already dit my research on, spi, lowpower, sd, en the e-ink module.
This being the inspiration for my project: e-ink photoframe-project
These are the modules I will be using:

My goal is to create a photoframe that with an e-ink screen. The device needs to be very power efficiënt, so I want to use a deepsleep kind off mode in the arduino. Also the device will get it's .bmp files (foto's) from the micro sd card and show a different foto every 24 hours.

This is the code I will use for showing an image on the e-ink screen:

#include"Ap_29demo.h"
//IO settings
int BUSY_Pin = 8; 
int RES_Pin = 9; 
int DC_Pin = 10; 
int CS_Pin = 11; 
int SCK_Pin = 12;  
int SDI_Pin = 13;

#define EPD_W21_MOSI_0  digitalWrite(SDI_Pin,LOW)
#define EPD_W21_MOSI_1  digitalWrite(SDI_Pin,HIGH) 

#define EPD_W21_CLK_0 digitalWrite(SCK_Pin,LOW)
#define EPD_W21_CLK_1 digitalWrite(SCK_Pin,HIGH)

#define EPD_W21_CS_0 digitalWrite(CS_Pin,LOW)
#define EPD_W21_CS_1 digitalWrite(CS_Pin,HIGH)

#define EPD_W21_DC_0  digitalWrite(DC_Pin,LOW)
#define EPD_W21_DC_1  digitalWrite(DC_Pin,HIGH)
#define EPD_W21_RST_0 digitalWrite(RES_Pin,LOW)
#define EPD_W21_RST_1 digitalWrite(RES_Pin,HIGH)
#define isEPD_W21_BUSY digitalRead(BUSY_Pin)

////////FUNCTION//////
void driver_delay_us(unsigned int xus);
void driver_delay_xms(unsigned long xms);
void DELAY_S(unsigned int delaytime);     
void SPI_Delay(unsigned char xrate);
void SPI_Write(unsigned char value);
void EPD_W21_WriteDATA(unsigned char command);
void EPD_W21_WriteCMD(unsigned char command);
//EPD
void EPD_W21_Init(void);
void EPD_init(void);
void PIC_display(const unsigned char* picData_old,const unsigned char* picData_new);
void EPD_sleep(void);
void EPD_refresh(void);
void lcd_chkstatus(void);
void PIC_display_Clean(void);
unsigned char HRES,VRES_byte1,VRES_byte2;

void setup() {
   pinMode(BUSY_Pin, INPUT); 
   pinMode(RES_Pin, OUTPUT);  
   pinMode(DC_Pin, OUTPUT);    
   pinMode(CS_Pin, OUTPUT);    
   pinMode(SCK_Pin, OUTPUT);    
   pinMode(SDI_Pin, OUTPUT);    
}
//Tips//
/*When the electronic paper is refreshed in full screen, the picture flicker is a normal phenomenon, and the main function is to clear the display afterimage in the previous picture.
  When the local refresh is performed, the screen does not flash.*/
/*When you need to transplant the driver, you only need to change the corresponding IO. The BUSY pin is the input mode and the others are the output mode. */

void loop() {
  while(1)
  {
    //PICTURE1
    EPD_init(); //EPD init
    PIC_display(gImage_black1,gImage_red1);//EPD_picture1 // refers to the image in the .h file provided by GooDisplay.
    EPD_refresh();//EPD_refresh   
    EPD_sleep();//EPD_sleep,Sleep instruction is necessary, please do not delete!!!
    delay(6000);
    
    
    //PICTURE Clean
     EPD_init(); //EPD init
    PIC_display_Clean();
    EPD_refresh();//EPD_refresh   
    EPD_sleep();//EPD_sleep,Sleep instruction is necessary, please do not delete!!!
    while(1);
  }


}
/////////////////////delay//////////////////////////////////////
void driver_delay_us(unsigned int xus)  //1us
{
  for(;xus>1;xus--);
}
void driver_delay_xms(unsigned long xms) //1ms
{  
    unsigned long i = 0 , j=0;

    for(j=0;j<xms;j++)
  {
        for(i=0; i<256; i++);
    }
}
void DELAY_S(unsigned int delaytime)     
{
  int i,j,k;
  for(i=0;i<delaytime;i++)
  {
    for(j=0;j<4000;j++)           
    {
      for(k=0;k<222;k++);
                
    }
  }
}
//////////////////////SPI///////////////////////////////////
void SPI_Delay(unsigned char xrate)
{
  unsigned char i;
  while(xrate)
  {
    for(i=0;i<2;i++);
    xrate--;
  }
}


void SPI_Write(unsigned char value)                                    
{                                                           
    unsigned char i;  
   SPI_Delay(1);
    for(i=0; i<8; i++)   
    {
        EPD_W21_CLK_0;
       SPI_Delay(1);
       if(value & 0x80)
          EPD_W21_MOSI_1;
        else
          EPD_W21_MOSI_0;   
        value = (value << 1); 
       SPI_Delay(1);
       driver_delay_us(1);
        EPD_W21_CLK_1; 
        SPI_Delay(1);
    }
}

void EPD_W21_WriteCMD(unsigned char command)
{
  SPI_Delay(1);
  EPD_W21_CS_0;                   
  EPD_W21_DC_0;   // command write
  SPI_Write(command);
  EPD_W21_CS_1;
}
void EPD_W21_WriteDATA(unsigned char command)
{
  SPI_Delay(1);
  EPD_W21_CS_0;                   
  EPD_W21_DC_1;   // command write
  SPI_Write(command);
  EPD_W21_CS_1;
}



/////////////////EPD settings Functions/////////////////////
void EPD_W21_Init(void)
{
  EPD_W21_RST_0;    // Module reset
  delay(100); //At least 10ms
  EPD_W21_RST_1;
  delay(100);  
}
void EPD_init(void)
{
    EPD_W21_Init(); //Electronic paper IC reset
  
    EPD_W21_WriteCMD(0x06);         //boost soft start
    EPD_W21_WriteDATA (0x17);   //A
    EPD_W21_WriteDATA (0x17);   //B
    EPD_W21_WriteDATA (0x17);   //C       


    EPD_W21_WriteCMD(0x04);  //Power on
    lcd_chkstatus();        //waiting for the electronic paper IC to release the idle signal

    EPD_W21_WriteCMD(0x00);     //panel setting
    EPD_W21_WriteDATA(0x0f);    //LUT from OTP£¬400x300
    EPD_W21_WriteDATA(0x0d);     //VCOM to 0V fast
  }
void EPD_refresh(void)
{
    EPD_W21_WriteCMD(0x12);     //DISPLAY REFRESH   
    driver_delay_xms(100);          //!!!The delay here is necessary, 200uS at least!!!     
    lcd_chkstatus();
} 
void EPD_sleep(void)
{
    EPD_W21_WriteCMD(0X50);  //VCOM AND DATA INTERVAL SETTING  
    EPD_W21_WriteDATA(0xf7);
       
    EPD_W21_WriteCMD(0X02);   //power off
    lcd_chkstatus();
    EPD_W21_WriteCMD(0X07);   //deep sleep
    EPD_W21_WriteDATA(0xA5);
}


void PIC_display(const unsigned char* picData_old,const unsigned char* picData_new)
{
    unsigned int i;
    EPD_W21_WriteCMD(0x10);        //Transfer old data
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(pgm_read_byte(&picData_old[i]));
  }
  
    EPD_W21_WriteCMD(0x13);        //Transfer new data
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(pgm_read_byte(&picData_new[i]));
  }
}
void PIC_display_Clean(void)
{
    unsigned int i;
    EPD_W21_WriteCMD(0x10);        //Transfer old data
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(0xff);
  }
  
    EPD_W21_WriteCMD(0x13);        //Transfer new data
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(0xff);
  }
}
void lcd_chkstatus(void)
{
  unsigned char busy;
  do
  {
    EPD_W21_WriteCMD(0x71);
    busy = isEPD_W21_BUSY;
    busy =!(busy & 0x01);        
  }
  while(busy);   
  driver_delay_xms(200);                       
}
//////////////////////////////////END/////////////////////////////////////////

The code is provided by GooDisplay.

Now the problem: the "gImage_black1,gImage_red1" is being red from a .h file, but I don't know how I can get a image to be read from a sd card instead.

Can someone tell me how I could replace the Image_black and Image_red with a reference to a .bmp file on the micro sd card? That would be an amazing start.

Thanks a lot in advance.

-Casper

Looks like each image is an array of 15000 bytes in PROGMEM. You can replace the PROGMEM array with a file:

    File imageFile = SD.open("filepath", FILE_READ);
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(imageFile.read());
  }

I just made up that syntax. Look at the SD library examples for how to open and read a file.

1 Like

Hello,

The difficult part is to convert from bmp format to whatever format required by the display, but it shouldn't be too hard. Or, convert with the software they provide, and store as binary file on the SD

Well it actually can read bmp. I'm gonna use a C file though.

Well it has been a while!
I have been working hard on the project and I definitely had a lot of succes!
this is what changed:

  • I changed the microcontroller, now using a seed XIAO microcontroller (SAMD21), which has a lot more sram (from 2k to 32k) which is great for the 4.2 inch e-ink display
  • I have been able to display the standard code on the E-ink display.
  • I have been able to read the files of an sd card while the screen is also connected to the microcontroller, so everything is hooked up and I don't think the layout has to be changed.

The problem: I still don't seem to be able to read/load the sd card data into the SRAM and display the data on the E-ink display. I have changed the code with the help of @johnwasser. The data is being read from the sd card, but the E-ink doens't do anything with it it seems.

I made a txt file with all the data from the #include"Ap_29demo.h" file. a file that is split up into 2 parts: a black part and a red part (since the screen has these 2 colours available). I expected that the program would just grab it from there but of course the compiler can now no longer find the red and black it was searching for. Maybe I should change some things in the Ap_29demo.h file?

Any ideas?

My new code:

#include"Ap_29demo.h"
#include <SPI.h>
#include <SD.h>

File myFile;

//IO settings
int BUSY_Pin = 5;
int RES_Pin = 4;
int DC_Pin = 3;
int CS_Pin = 2;
int SCK_Pin = 1;
int SDI_Pin = 0;

#define EPD_W21_MOSI_0  digitalWrite(SDI_Pin,LOW)
#define EPD_W21_MOSI_1  digitalWrite(SDI_Pin,HIGH)

#define EPD_W21_CLK_0 digitalWrite(SCK_Pin,LOW)
#define EPD_W21_CLK_1 digitalWrite(SCK_Pin,HIGH)

#define EPD_W21_CS_0 digitalWrite(CS_Pin,LOW)
#define EPD_W21_CS_1 digitalWrite(CS_Pin,HIGH)

#define EPD_W21_DC_0  digitalWrite(DC_Pin,LOW)
#define EPD_W21_DC_1  digitalWrite(DC_Pin,HIGH)
#define EPD_W21_RST_0 digitalWrite(RES_Pin,LOW)
#define EPD_W21_RST_1 digitalWrite(RES_Pin,HIGH)
#define isEPD_W21_BUSY digitalRead(BUSY_Pin)

////////FUNCTION//////
void driver_delay_us(unsigned int xus);
void driver_delay_xms(unsigned long xms);
void DELAY_S(unsigned int delaytime);
void SPI_Delay(unsigned char xrate);
void SPI_Write(unsigned char value);
void EPD_W21_WriteDATA(unsigned char command);
void EPD_W21_WriteCMD(unsigned char command);
//EPD
void EPD_W21_Init(void);
void EPD_init(void);
void PIC_display(const unsigned char* picData_old, const unsigned char* picData_new);
void EPD_sleep(void);
void EPD_refresh(void);
void lcd_chkstatus(void);
void PIC_display_Clean(void);
unsigned char HRES, VRES_byte1, VRES_byte2;

void setup() {

  Serial.begin(9600);
  while (!Serial)
  {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print("Initializing SD card...");

  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  pinMode(BUSY_Pin, INPUT);
  pinMode(RES_Pin, OUTPUT);
  pinMode(DC_Pin, OUTPUT);
  pinMode(CS_Pin, OUTPUT);
  pinMode(SCK_Pin, OUTPUT);
  pinMode(SDI_Pin, OUTPUT);
}
//Tips//
/*When the electronic paper is refreshed in full screen, the picture flicker is a normal phenomenon, and the main function is to clear the display afterimage in the previous picture.
  When the local refresh is performed, the screen does not flash.*/
/*When you need to transplant the driver, you only need to change the corresponding IO. The BUSY pin is the input mode and the others are the output mode. */

void loop() {
  while (1)
  {


    //PICTURE1
    EPD_init(); //EPD init
    PIC_display(gImage_black1, gImage_red1); //EPD_picture1
    EPD_refresh();//EPD_refresh
    EPD_sleep();//EPD_sleep,Sleep instruction is necessary, please do not delete!!!
    delay(6000);


    //PICTURE Clean
    EPD_init(); //EPD init
    PIC_display_Clean();
    EPD_refresh();//EPD_refresh
    EPD_sleep();//EPD_sleep,Sleep instruction is necessary, please do not delete!!!
    while (1);

  }


}
/////////////////////delay//////////////////////////////////////
void driver_delay_us(unsigned int xus)  //1us
{
  for (; xus > 1; xus--);
}
void driver_delay_xms(unsigned long xms) //1ms
{
  unsigned long i = 0 , j = 0;

  for (j = 0; j < xms; j++)
  {
    for (i = 0; i < 256; i++);
  }
}
void DELAY_S(unsigned int delaytime)
{
  int i, j, k;
  for (i = 0; i < delaytime; i++)
  {
    for (j = 0; j < 4000; j++)
    {
      for (k = 0; k < 222; k++);

    }
  }
}
//////////////////////SPI///////////////////////////////////
void SPI_Delay(unsigned char xrate)
{
  unsigned char i;
  while (xrate)
  {
    for (i = 0; i < 2; i++);
    xrate--;
  }
}


void SPI_Write(unsigned char value)
{
  unsigned char i;
  SPI_Delay(1);
  for (i = 0; i < 8; i++)
  {
    EPD_W21_CLK_0;
    SPI_Delay(1);
    if (value & 0x80)
      EPD_W21_MOSI_1;
    else
      EPD_W21_MOSI_0;
    value = (value << 1);
    SPI_Delay(1);
    driver_delay_us(1);
    EPD_W21_CLK_1;
    SPI_Delay(1);
  }
}

void EPD_W21_WriteCMD(unsigned char command)
{
  SPI_Delay(1);
  EPD_W21_CS_0;
  EPD_W21_DC_0;   // command write
  SPI_Write(command);
  EPD_W21_CS_1;
}
void EPD_W21_WriteDATA(unsigned char command)
{
  SPI_Delay(1);
  EPD_W21_CS_0;
  EPD_W21_DC_1;   // command write
  SPI_Write(command);
  EPD_W21_CS_1;
}



/////////////////EPD settings Functions/////////////////////
void EPD_W21_Init(void)
{
  EPD_W21_RST_0;    // Module reset
  delay(100); //At least 10ms
  EPD_W21_RST_1;
  delay(100);
}
void EPD_init(void)
{
  EPD_W21_Init(); //Electronic paper IC reset

  EPD_W21_WriteCMD(0x06);         //boost soft start
  EPD_W21_WriteDATA (0x17);   //A
  EPD_W21_WriteDATA (0x17);   //B
  EPD_W21_WriteDATA (0x17);   //C


  EPD_W21_WriteCMD(0x04);  //Power on
  lcd_chkstatus();        //waiting for the electronic paper IC to release the idle signal

  EPD_W21_WriteCMD(0x00);     //panel setting
  EPD_W21_WriteDATA(0x0f);    //LUT from OTP£¬400x300
  EPD_W21_WriteDATA(0x0d);     //VCOM to 0V fast
}
void EPD_refresh(void)
{
  EPD_W21_WriteCMD(0x12);     //DISPLAY REFRESH
  driver_delay_xms(100);          //!!!The delay here is necessary, 200uS at least!!!
  lcd_chkstatus();
}
void EPD_sleep(void)
{
  EPD_W21_WriteCMD(0X50);  //VCOM AND DATA INTERVAL SETTING
  EPD_W21_WriteDATA(0xf7);

  EPD_W21_WriteCMD(0X02);   //power off
  lcd_chkstatus();
  EPD_W21_WriteCMD(0X07);   //deep sleep
  EPD_W21_WriteDATA(0xA5);
}


void PIC_display(const unsigned char* picData_old, const unsigned char* picData_new) // this function should now be using the SD card memory
{
  unsigned int i;
  myFile = SD.open("logo.txt", FILE_READ);
  if (myFile)
  {
    Serial.println("logo.txt:");
    Serial.write(myFile.read());
  }
  else
  {
    Serial.println("error opening file"); // if the file didn't open, print an error:
  }
  EPD_W21_WriteCMD(0x10);        //Transfer old data
  for (i = 0; i < 15000; i++)
  {
    EPD_W21_WriteDATA(myFile.read(&picData_old[i]));
  }
  myFile = SD.open("logo.txt", FILE_READ);
  if (myFile)
  {
    Serial.println("logo.txt:");
    Serial.write(myFile.read());
  }
  else
  {
    Serial.println("error opening file"); // if the file didn't open, print an error:
  }
  EPD_W21_WriteCMD(0x13);        //Transfer new data
  for (i = 0; i < 15000; i++)
  {
    Serial.write("drawing picture");
    EPD_W21_WriteDATA(myFile.read(&picData_new[i]));
  }
  Serial.write("picture drawn");
  delay(5000);
}
void PIC_display_Clean(void)
{
  unsigned int i;
  EPD_W21_WriteCMD(0x10);        //Transfer old data
  for (i = 0; i < 15000; i++)
  {
    EPD_W21_WriteDATA(0xff);
  }

  EPD_W21_WriteCMD(0x13);        //Transfer new data
  for (i = 0; i < 15000; i++)
  {
    EPD_W21_WriteDATA(0xff);
  }
}
void lcd_chkstatus(void)
{
  unsigned char busy;
  do
  {
    EPD_W21_WriteCMD(0x71);
    busy = isEPD_W21_BUSY;
    busy = !(busy & 0x01);
  }
  while (busy);
  driver_delay_xms(200);
}
//////////////////////////////////END/////////////////////////////////////////

and the Ap_29demo.h file: Ap_29demo.h (150.5 KB)

So it really seems like I need to read the files from the sd card byte by byte. The samd21 does have more SRAM, but just not enough to load the whole picture into the SRAM.

This should be read from the sd card, not from the eeprom (internal memory).

void PIC_display(const unsigned char* picData_old,const unsigned char* picData_new)
{
    unsigned int i;
    EPD_W21_WriteCMD(0x10);        //Transfer old data
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(pgm_read_byte(&picData_old[i]));
  }
  
    EPD_W21_WriteCMD(0x13);        //Transfer new data
    for(i=0;i<15000;i++)      
  {
    EPD_W21_WriteDATA(pgm_read_byte(&picData_new[i]));
  }
}

Anybody an idea how to write code that reads byte by byte from the sd card?

I have never found out how to fix the ram issues, but someone did and the code is very much available. I Will post my Final project some day with code.

This topic may be closed now.

Hey @casperdroid5 .

Did you have any luck with the project?

I am trying the very same thing. I want to make a Eink display load images from the SD Card. I have a bigger display. So, I have to load a bigger file from the SD Card

So, I thought of buffering smaller parts of the screen and repeating the process until the entire screen fills up.

But I am new to this and I am having trouble loading the array onto the Code. When I try to read, the array loads as a bunch of hex values and not as it should.

So, Can you please share your code or the code that you found?

Thanks in advance,

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.