// include the SPI library:
#include <SPI.h>

//include font
#include "Arial_round_16x24.c"
#include "SmallFont.c"
#include "Ubuntubold.c"
#include "images.c"

// D1 Mini (SPI on HW pins, D5,D6,D7)
#define RSPIN (D3)
#define CSPIN (D8)
#define RSTPIN (D4)
#define BUSYPIN (D2)
//D1 mini:  3V3 D8  D7  D5  D4  D3  D2  G
//EPAPER:   3V3 CS  SDI SCK RES D/C BUS GND

byte orientation=0;

void setup() {
  Serial.begin(115200);
  epaperInit();
  epaperSetFullRefresh();
  epaperClear(255);
  epaperUpdateScreen();
  epaperSetPartialRefresh();
}

void loop() { 
  int i;
  epaperSetFullRefresh();

  epaperClear(0x22);              //00100010 pattern
  epaperBox(6,50,13,100,255);     //white
  epaperBox(13,100,20,150,0xAA);  //10101010 pattern
  epaperBox(10,75,15,125,0);      //black
  epaperUpdateScreen();

  digitalWrite(RSTPIN,LOW);   //set low power modes between updates
  delay(5000);
  epaperInit();
  epaperSetFullRefresh();

  epaperClear(0);
  epaperCharArray(4,8,"WHITE ON",255,Arial_round_16x24);
  epaperCharArray(7,40,"BLACK",255,Arial_round_16x24);
  epaperCharArray(6,80,"WHITE ON BLACK",255,SmallFont);  
  epaperBox(0,100,24,199,255);    //set bottom half of screen to white
  epaperCharArray(4,108,"BLACK ON",0,Arial_round_16x24);
  epaperCharArray(7,140,"WHITE",0,Arial_round_16x24);
  epaperCharArray(6,180,"BLACK ON WHITE",0,SmallFont);
  epaperUpdateScreen();

  digitalWrite(RSTPIN,LOW);   //set low power modes between updates
  delay(5000);
  epaperInit();
  epaperSetFullRefresh();
  epaperSetOrientation(0);
  
  epaperClear(255);
  epaperCharArray(4,0,"Chapter 1",0,Arial_round_16x24);
  epaperCharArray(0,32,"Lorem ipsum dolor sit",0,SmallFont);
  epaperCharArray(0,44,"amet, consectetur",0,SmallFont);
  epaperCharArray(0,56,"adipiscing elit. Aenean",0,SmallFont);
  epaperCharArray(0,68,"arcu augue, consectetur",0,SmallFont);
  epaperCharArray(0,80,"eu sagittis vel,",0,SmallFont);
  epaperCharArray(0,92,"pellentesque in justo.",0,SmallFont);
  epaperCharArray(0,104,"Curabitur dignissim nisl",0,SmallFont);
  epaperCharArray(0,116,"a augue luctus, sed",0,SmallFont);
  epaperCharArray(0,128,"egestas erat dignissim.",0,SmallFont);
  epaperCharArray(0,140,"Mauris neque lacus,",0,SmallFont);
  epaperCharArray(0,152,"interdum et tellus et,",0,SmallFont);
  epaperCharArray(0,164,"facilisis aliquet lorem.",0,SmallFont);
  epaperCharArray(0,176,"Aliquam rhoncus vitae",0,SmallFont);
  epaperCharArray(0,188,"tortor et porttitor.",0,SmallFont);
  epaperUpdateScreen();

  digitalWrite(RSTPIN,LOW);   //set low power modes between updates
  delay(5000);
  epaperInit();
  epaperSetFullRefresh();
  epaperSetOrientation(1);
  
  epaperClear(255);
  epaperCharArray(2,0,"Chapter",0,UbuntuBold);
  epaperCharArray(0,56,"Lorem ipsum ",0,Arial_round_16x24);
  epaperCharArray(0,80,"dolor sit   ",0,Arial_round_16x24);
  epaperCharArray(0,104,"amet,       ",0,Arial_round_16x24);
  epaperCharArray(0,128,"consectetur",0,Arial_round_16x24);
  epaperCharArray(0,152,"adipiscing ",0,Arial_round_16x24);
  epaperCharArray(0,176,"elit.",0,Arial_round_16x24);
  epaperUpdateScreen();

  digitalWrite(RSTPIN,LOW);   //set low power modes between updates
  delay(5000);
  epaperInit();
  epaperSetFullRefresh();
  epaperSetOrientation(0);
    
  epaperClear(255);
  epaperCharArray(6,10,"APPLES",0,Arial_round_16x24);
  epaperCharArray(4,187,"*Conditions apply",0,SmallFont);
  epaperDrawImage(6,45,apple,0);
  epaperCharArray(0,150,"$3.99/kg",0,UbuntuBold);
  epaperUpdateScreen();

  digitalWrite(RSTPIN,LOW);   //set low power modes between updates
  delay(5000);
  epaperInit();
  epaperSetFullRefresh();

  epaperClear(255);
  epaperCharArray(3,10,"MUSHROOMS",0,Arial_round_16x24);
  epaperCharArray(4,187,"*Conditions apply",0,SmallFont);
  epaperDrawImage(6,45,mushroom,0);
  epaperCharArray(4,150,"$12/kg",0,UbuntuBold);
  epaperCharArray(6,90,"SALE!",0,UbuntuBold);  
  epaperUpdateScreen();
  delay(500);

  epaperClear(255);
  epaperCharArray(3,10,"MUSHROOMS",0,Arial_round_16x24);    //display uses two buffers, so if we write to both we can alternate easily by using update only
  epaperCharArray(4,187,"*Conditions apply",0,SmallFont);
  epaperDrawImage(6,45,mushroom,0);
  epaperCharArray(4,150,"$12/kg",0,UbuntuBold);
  epaperCharArray(6,90,"SALE!",255,UbuntuBold);  
  epaperSetPartialRefresh();
  epaperUpdateScreen();
  delay(500);

  epaperUpdateScreen();
  delay(500);
  epaperUpdateScreen();
  delay(500);
  epaperUpdateScreen();
  delay(500);
  epaperUpdateScreen();
  delay(500);
  epaperUpdateScreen();
  delay(500);
  epaperUpdateScreen();
  delay(500);
}

void epaperInit(){
  SPI.begin();
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));  //assume no-one else is using SPI
  pinMode(RSTPIN,OUTPUT);
  pinMode(CSPIN,OUTPUT);
  pinMode(RSPIN,OUTPUT);
  digitalWrite(RSTPIN,HIGH);
  digitalWrite(CSPIN,HIGH);
  digitalWrite(RSPIN,HIGH);
  pinMode(BUSYPIN,INPUT);
  digitalWrite(RSTPIN,LOW);
  delay(10);
  digitalWrite(RSTPIN,HIGH);
  delay(10);
  digitalWrite(CSPIN,LOW);
  cmd8(0x01);
  data8(0xc7);//number of gates = 200
  data8(0x00);
  data8(0x00);
  cmd8(0x3A);//dummy line period=0x1A per sample code
  data8(0x1A);
  cmd8(0x3B);//gate line width=0x8 per sample code
  data8(0x08);
  cmd8(0x2C);//VCOM=0x9B per sample code
  data8(0x9B);
  cmd8(0x0c);//Booster soft start per sample code
  data8(0xd7);
  data8(0xd6);
  data8(0x9d);
  digitalWrite(CSPIN,HIGH);  
  epaperSetOrientation(0);
}

void epaperSetFullRefresh(){    //no artifacts, but takes a few seconds
  digitalWrite(CSPIN,LOW);
  cmd8(0x32);                            
  data8(0x50);  data8(0xAA);  data8(0x55);  data8(0xAA);  data8(0x11);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);
  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0xFF);  data8(0xFF);  data8(0x1F);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);
  digitalWrite(CSPIN,HIGH);  
}

void epaperSetPartialRefresh(){   //minor artifacts, less than a second
  digitalWrite(CSPIN,LOW);
  cmd8(0x32);                            
  data8(0x10);  data8(0x18);  data8(0x18);  data8(0x08);  data8(0x18);  data8(0x18);  data8(0x08);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);
  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x13);  data8(0x14);  data8(0x44);  data8(0x12);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);  data8(0x00);
  digitalWrite(CSPIN,HIGH);  
}

void epaperUpdateScreen(){
  digitalWrite(CSPIN,LOW);
  cmd8(0x22);   //Master activate control reg
  data8(0xC4);  //enable clock, display pattern
  cmd8(0x20);   //Master active start
  cmd8(0xFF);   //NOP, terminate commands
  digitalWrite(CSPIN,HIGH);  
}

void epaperSetArea(int x0,int y0,int x1, int y1){
  int t;
  if(x1<x0){t=x1;x1=x0;x0=t;}
  if(y1<y0){t=y1;y1=y0;y0=t;}  
  if(orientation==0){
    x0=24-x0;
    x1=24-x1;
    y0=199-y0;
    y1=199-y1;
  }
  digitalWrite(CSPIN,LOW);
  cmd8(0x44);      //set ram x range
  data8(x0 & 31);  //start (blocks of 8)
  data8(x1 & 31);  //end (blocks of 8
  cmd8(0x45);      //set ram y range
  data8(y0 & 255); //LSB start
  data8(0);        //MSB start (will be 0 for <256)
  data8(y1 & 255); //LSB end
  data8(0x0);      //MSB end (will be 0 for <256)
  cmd8(0x4E);      //set ram X address
  data8(x0 & 31);  //position
  cmd8(0x4F);      //set ram y address
  data8(y0 & 255); //LSB
  data8(0x0);      //MSB
  cmd8(0x24);      //Start RAM write
  digitalWrite(CSPIN,HIGH);    
}

void epaperSetOrientation(byte o){    //because x and y are treated differently, we can't easily rotate
  if(o==0){   //normal
    digitalWrite(CSPIN,LOW);
    cmd8(0x11);
    data8(0x0);
    digitalWrite(CSPIN,HIGH);  
    orientation=0;   
  }
  if(o==1){   //rotated 180
    digitalWrite(CSPIN,LOW);
    cmd8(0x11);
    data8(0x3);
    digitalWrite(CSPIN,HIGH);  
    orientation=1;   
  }
}

void epaperClear(byte c){
  int i;
  epaperSetArea(0,0,24,199);
  digitalWrite(CSPIN,LOW);
  for(i=0;i<5000;i++){
    data8(c);
  }
  digitalWrite(CSPIN,HIGH);      
}

int epaperChar(int x,int y, char c, byte col, const unsigned char* font){
  byte width, height,i;
  char c0,c1;
  width = font[0]/8;    //groups of 8 monochrome pixels
  height = font[1];
  c0 = font[2];
  c1 = c0+font[3]-1;
  if(c<c0){c=c0;}       //keep within character range
  if(c>c1){c=c0;}       //keep within character range
  c=c-c0;               //set to zero base
  col=col^255;          //to match font logic
  epaperSetArea(x,y,x+width-1,y+height-1);
  digitalWrite(CSPIN,LOW);
  for(i=0;i<width*height;i++){
    data8(font[4+width*height*c+i]^col);
  }
  digitalWrite(CSPIN,HIGH);      
  return width;   //for where to place next character
}

int epaperCharArray(int x,int y, char* c, byte col, const unsigned char* font){
  int x0=x;
  while(*c){
    x=x+epaperChar(x,y,*c++,col,font);
  }
  return x0-x;  
}

void epaperDrawImage(int x, int y, const unsigned char* im, byte col){
  byte width,height;
  int i;
  width = (im[0]+7)/8;    //groups of 8 monochrome pixels
  height = im[1];  
  col=col^255;          //to match font logic
  epaperSetArea(x,y,x+width-1,y+height-1);
  digitalWrite(CSPIN,LOW);
  for(i=0;i<width*height;i++){
    data8(im[2+i]^col);
  }
  digitalWrite(CSPIN,HIGH);        
}

void epaperBox(int x0,int y0,int x1, int y1,byte col){
  int i,t;
  t=(abs(x1-x0)+1)*(abs(y1-y0)+1);
  Serial.println(t);
  epaperSetArea(x0,y0,x1,y1);
  digitalWrite(CSPIN,LOW);
  for(i=0;i<t;i++){
    data8(col);    
  }
  digitalWrite(CSPIN,HIGH);        
}

void data8(byte d){
  SPI.transfer(d);
}

void cmd8(byte d){
  while(digitalRead(BUSYPIN)){    //wait for busy
    Serial.print("WAIT:0x");
    Serial.println(d,HEX);
    delay(100);
  }
  digitalWrite(RSPIN,LOW);
  data8(d);
  digitalWrite(RSPIN,HIGH);  
}


