Having trouble making a clock out of the SparkFun epaper display

I picked up the epaper display and breakout board that SparkFun sells. I’m trying to make a clock out of it. It’s been pretty slow-going trying to figure out how to interface with the display, but I’m making progress.

I’m pulling the time from a Chronodot DS3231 RTC breakout board. That’s easy enough. Since the epaper display library seems to want to have a char sent to it (instead of int or string), I’m passing the hours and minutes from the RTC library into strings along with my am/pm designator. I convert the 24 hour time to 12 hours and pass that into a string too. I take the hrs, minutes, and am/pm and pass those into a string and convert the whole thing into a char array for the epaper display.

The problem is, I seem to be ending up with a 0 at the end of my display. I can’t figure it out. My char array should be 10 chars. I add a leading space (or two, as required) to center the text on the display. I have the char array configured to a length of 10. If I make it 9, it just displays a bunch of zeros. If I make it 11 or greater, it seems to have no effect. I just can’t seem to figure out where the zero is coming from.

Is there an error in my code or methodology that’s causing this?

Another problem is that at some point a wrote a “K” to the last char of the top line of the display and part of the “K” stuck there. I haven’t been able to get rid of it. So I wonder if I have a bad display. I don’t think that’s the case though, because the 0 doesn’t appear there when I display my startup splash screen. It’s only there during my loop.

Here’s my code…

#include <Wire.h>
#include "ePaper.h"  // This file includes defines for each displayable character

int EIOpin = 8;     // Input/output pin for chip selection
int XCKpin = 9;     // Clock input pin for taking display data
int LATCHpin = 10;   // Latch pulse input pin for display data
int SLEEPBpin = 11;  // Sleep Pin for the display
int DI0pin = 12;     // Input pin for display data

//setup display with pin definitions
ePaper epaper = ePaper(EIOpin, XCKpin, LATCHpin, SLEEPBpin, DI0pin);

int seconds; 
int minutes; 
int hours;
int adjHours;
int dayOfWeek;
int dayOfMonth;
int month;
int year;

String hrStr;
String minStr;
String timeStr;
String space;
String ampm;

void setup()
{
  Wire.begin();
  Serial.begin(9600);

  //epaper.writeTop("EPaper");
  epaper.writeTop("  EPAPER  ");
  epaper.writeBottom(" Clock V1 ");
  epaper.writeDisplay();
  delay(3000);

  epaper.deleteDisplay();
}

void loop()
{
  //poll the DS3231 for the date and time
  get_time();
  get_date();

  //display hours
  switch(hours) {
  case 0: 
    adjHours = 12;
    break;
  case 1: 
    adjHours = 1;
    break;
  case 2: 
    adjHours = 2;
    break;
  case 3: 
    adjHours = 3;   
    break;
  case 4: 
    adjHours = 4;
    break;
  case 5: 
    adjHours = 5;
    break;
  case 6: 
    adjHours = 6;  
    break;
  case 7: 
    adjHours = 7;   
    break;
  case 8: 
    adjHours = 8;  
    break;
  case 9: 
    adjHours = 9;   
    break;
  case 10: 
    adjHours = 10;   
    break;
  case 11: 
    adjHours = 11;
    break;
  case 12: 
    adjHours = 12;
    break;
  case 13: 
    adjHours = 1;
    break;
  case 14: 
    adjHours = 2;
    break;
  case 15: 
    adjHours = 3;
    break;
  case 16: 
    adjHours = 4;   
    break;
  case 17: 
    adjHours = 5;
    break;
  case 18: 
    adjHours = 6;
    break;
  case 19: 
    adjHours = 7;  
    break;
  case 20: 
    adjHours = 8;   
    break;
  case 21: 
    adjHours = 9;  
    break;
  case 22: 
    adjHours = 10;   
    break;
  case 23: 
    adjHours = 11;   
    break;
  }
  
  //pass adjusted hours int inro a string
  hrStr = String(adjHours);  
  minStr = String(minutes);

  //figure out AM/PM 
  if (hours < 12) {
    ampm = " AM ";
  } 
  else if (hours = 0) {
    ampm = " AM ";
  } 
  else { 
    ampm = " PM "; 
  }

  //figure out leading spaces
  if (adjHours > 9) {
    space = " "; //1 space
  } 
  else {
    space = "  "; //2 spaces
  }
  
  //construct the string to be displayed
  String timeStr = (space + hrStr + minStr + ampm);

  //convert the string to be displayed into a char array, limit to 10 places
  char timeChr[10];
  timeStr.toCharArray(timeChr, 10);

  //clear the display and write the char array
  epaper.deleteDisplay();
  epaper.writeTop(timeChr);
  epaper.writeDisplay();
  
  //wait almost a minute to update time
  delay(50000);

} //end of loop

/////////////
//DS3231 RTC interface
void initChrono()
{
  set_time();
  set_date();
}

void set_date()
{
  Wire.beginTransmission(104);
  Wire.write(3);
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

void get_date()
{
  Wire.beginTransmission(104); 
  Wire.write(3);//set register to 3 (day)
  Wire.endTransmission();
  Wire.requestFrom(104, 4); //get 5 bytes(day,date,month,year,control);
  dayOfWeek   = bcdToDec(Wire.read());
  dayOfMonth  = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year  = bcdToDec(Wire.read());
}

void set_time()
{
  Wire.beginTransmission(104);
  Wire.write(0);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.endTransmission();
}

void get_time()
{
  Wire.beginTransmission(104); 
  Wire.write(0);//set register to 0
  Wire.endTransmission();
  Wire.requestFrom(104, 3);//get 3 bytes (seconds,minutes,hours);
  seconds = bcdToDec(Wire.read() & 0x7f);
  minutes = bcdToDec(Wire.read());
  hours = bcdToDec(Wire.read() & 0x3f);
}

///////////////////////////////////////////////////////////////////////

byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}
String hrStr;
String minStr;
String timeStr;
String space;
String ampm;

Unnecessary!

Your code for converting an hour value to be in the range 1 to 12 is way to long. Numbers over 13 need to have 12 subtracted from them. 0 needs to be changed to 14. 4 lines of code, not 40.

Instead of Strings, use sprintf() to populate the char array directly. Serial.print() the char array, between markers, so you KNOW what is in it.

Serial.print("timeChr: [");
Serial.print(timeChr);
Serial.println("]");

If there is anything funny between the [ and the ], fix that problem. If not, the problem is in the ePaper class.

Excellent! Thanks for the pointers. I can't wait to get out of work and try them when I get home.

Funny, I did start with something like that for the hours:

if (hours >12) {
adjHours = hours - 12;
}

But it did weird stuff when the time hit midnight. I guess because the hours are really 0. So I ended up with an "if >12" and and an "if = 0" but it just went squirrelly. I can't remember what the problem was, but I said to hell with it and just hard coded all the numbers. You're right though, it's a poorly written hack. I'll go back and change that.

An alternative way to convert 0 to 23 into the correct hour for display would be to put the values in an array and use the hours variable as an index to the array to retrieve the value. One array and one line of code.

Strings work unreliably on Arduino. Use c-style arrays of chars.

I'm beginning to agree with on on the unreliability of strings. I'm at work so I can't really try this out until I get home, but I'm thinking of something like this:

        suffix = "AM";

        if (hours > 11) {
            suffix = "PM";
        } else {
            suffix = "AM";
        }

        if (hours > 12) {
            hours = hours-12;
        } else if (hours == 0) {
            hours = 12;
        }
        time = hours + minutes + suffix;

..again, don't know if it'll work. Will have to wait until I get home tonight to try things out.

I'm seriously starting to think there's something wrong with this display. First of all, within an hour of me using it, 2 segments on the last digit of the top row flipped to white and have stayed like that all week. I can't seem to clear them no matter what I do. Even if I invert the display.

That last zero keeps displaying on the bottom and top rows. I put in Z's instead of white spaces so I can see if they're really showing up or not.

The serial window shows "Z1229ZAMZ" as my string, but I get "Z1229ZAM0" on the display.

But it's strange that the zero only shows up with this data. My splash screen looks OK. (With the exception of those two wonky segments.)

Made some more progress. Got the date now, but dang it those 0’s at the end keep showing up!

Hers my code:

//V1 - original release

#include <Wire.h>
#include "ePaper.h"  // This file includes defines for each displayable character

int EIOpin = 8;     // Input/output pin for chip selection
int XCKpin = 9;     // Clock input pin for taking display data
int LATCHpin = 10;   // Latch pulse input pin for display data
int SLEEPBpin = 11;  // Sleep Pin for the display
int DI0pin = 12;     // Input pin for display data

//setup display with pin definitions
ePaper epaper = ePaper(EIOpin, XCKpin, LATCHpin, SLEEPBpin, DI0pin);

int seconds; 
int minutes; 
int hours;
int adjHours;
int dayOfWeek;
int dayOfMonth;
int month;
int year;

String hrStr;
String minStr;
String timeStr;
String space;
String suffix;

void setup()
{

  Wire.begin();
  Serial.begin(9600);

  ////////////////////////////////
  // force time setting:
  seconds = 00;
  minutes = 29;
  hours = 0;
  dayOfWeek = 3;
  dayOfMonth = 14;
  month = 03;
  year = 13;
  initChrono();//just set the time once on your RTC

  ///////////////////////////////

  epaper.writeTop("  EPAPER  ");
  epaper.writeBottom(" Clock V1 ");
  epaper.writeDisplay();
  delay(5000);

  epaper.deleteDisplay();

}

void loop()
{
  //poll the DS3231 for the date and time
  get_time();
  get_date();

  /////////////////////////////////////////////////////
  // time
  //display hours
  if (hours > 11) {
    suffix = " PM ";
  } 
  else {
    suffix = " AM ";
  }

  if (hours > 12) {
    hours -= 12;
  } 
  else if (hours == 0) {
    hours = 12;
  }

  //pass adjusted hours int inro a string
  hrStr = String(hours);  
  minStr = String(minutes);

  //figure out leading spaces
  if (hours > 9) {
    space = " "; //1 space
  } 
  else {
    space = "  "; //2 spaces
  }

  //construct the string to be displayed
  String timeStr = (space + hrStr + " " + minStr + suffix);

  //convert the string to be displayed into a char array, limit to 10 places
  char timeChr[10];
  timeStr.toCharArray(timeChr, 10);

  Serial.print(timeChr);

  /////////////////////////////////////////////////////
  //date
  String dayOfWeekStr;
  switch(dayOfWeek){
  case 1: 
    dayOfWeekStr ="Sun";
    break;
  case 2: 
    dayOfWeekStr ="Mon";
    break;
  case 3: 
    dayOfWeekStr ="Tue";
    break;
  case 4: 
    dayOfWeekStr ="Wed";
    break;
  case 5: 
    dayOfWeekStr ="Thu";
    break;
  case 6: 
    dayOfWeekStr ="Fri";
    break;
  case 7: 
    dayOfWeekStr ="Sat";
    break;
  }

  String dateStr = (dayOfWeekStr + " " + month + " " + dayOfMonth);

  Serial.print (dateStr);

  //convert the string to be displayed into a char array, limit to 10 places
  char dateChr[10];
  dateStr.toCharArray(dateChr, 10);

  Serial.print(dateChr);

  //clear the display and write the char array
  epaper.writeBottom(dateChr);
  epaper.writeTop(timeChr);
  epaper.writeDisplay();

  delay(50000);
} //end of loop

/////////////
//DS3231 RTC interface
void initChrono()
{
  set_time();
  set_date();
}

void set_date()
{
  Wire.beginTransmission(104);
  Wire.write(3);
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

void get_date()
{
  Wire.beginTransmission(104); 
  Wire.write(3);//set register to 3 (day)
  Wire.endTransmission();
  Wire.requestFrom(104, 4); //get 5 bytes(day,date,month,year,control);
  dayOfWeek   = bcdToDec(Wire.read());
  dayOfMonth  = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year  = bcdToDec(Wire.read());
}

void set_time()
{
  Wire.beginTransmission(104);
  Wire.write(0);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.endTransmission();
}

void get_time()
{
  Wire.beginTransmission(104); 
  Wire.write(0);//set register to 0
  Wire.endTransmission();
  Wire.requestFrom(104, 3);//get 3 bytes (seconds,minutes,hours);
  seconds = bcdToDec(Wire.read() & 0x7f);
  minutes = bcdToDec(Wire.read());
  hours = bcdToDec(Wire.read() & 0x3f);
}

///////////////////////////////////////////////////////////////////////
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

My guess is it's printing the end of string NULL terminator as a 0.

Try padding all your strings to be longer than the display, so that any terminating character is off the side of the screen.

but dang it those 0's at the end keep showing up!

So, why are these still here?

String hrStr;
String minStr;
String timeStr;
String space;
String suffix;

Because I pass the hour and minutes ints into those strings:

  hrStr = String(hours);  
  minStr = String(minutes);

//construct the string to be displayed
  String timeStr = (space + hrStr + "-" + minStr + suffix);

  //convert the string to be displayed into a char array, limit to 10 places
  char timeChr[10];
  timeStr.toCharArray(timeChr, 10);

So you think it's the display that's padding it with zeros and not the Arduino? Is that why the serial window shows my string to be " 1229 AM ", but I get " 1229 AM0" on the display?

Why do you think you need to use Strings at all? Have you looked at sprintf()?

I received the replacement display from SparkFun so I spent an hour tonight playing around with sprintf some more. I managed to get it to work:

The code can use some more improvements, but for now it works.

#include <Wire.h>
#include "ePaper.h"  // This file includes defines for each displayable character

int EIOpin = 8;     // Input/output pin for chip selection
int XCKpin = 9;     // Clock input pin for taking display data
int LATCHpin = 10;   // Latch pulse input pin for display data
int SLEEPBpin = 11;  // Sleep Pin for the display
int DI0pin = 12;     // Input pin for display data

//setup display with pin definitions
ePaper epaper = ePaper(EIOpin, XCKpin, LATCHpin, SLEEPBpin, DI0pin);

int seconds; 
int minutes; 
int hours;
int dayOfWeek;
int dayOfMonth;
int month;
int year;

char *space;
char *suffix;
char *dayOfWeekChar;
char *seperator; 

void setup()
{

  Wire.begin();
  Serial.begin(9600);

  //////////////////////////////// force time setting:
  /*
  seconds = 00;
  minutes = 50;
  hours = 22;
  dayOfWeek = 6;
  dayOfMonth = 22;
  month = 03;
  year = 13;
  initChrono();//just set the time once on your RTC
  */
  ///////////////////////////////

  epaper.writeTop("  EPAPER  ");
  epaper.writeBottom(" Clock v1 ");
  epaper.writeDisplay();
  delay(1000);
  epaper.writeTop("  EPAPER  ");
  epaper.writeBottom(" Clock v1 ");
  epaper.writeDisplay();
  delay(1000);
  
  epaper.deleteDisplay();
}

void loop()
{
  //poll the DS3231 for the date and time
  get_time();
  get_date();

  /////////////////////////////////////////////////////
  // time
  //display hours
  if (hours > 11) {
    suffix = " PM ";
  } 
  else {
    suffix = " AM ";
  }

  if (hours > 12) {
    hours -= 12;
  } 
  else if (hours == 0) {
    hours = 12;
  }

  //figure out leading spaces
  if (hours > 9) {
    space = " "; //1 space
  } 
  else {
    space = "  "; //2 spaces
  }

if (minutes < 10) {
  seperator = "z0";
} else {
  seperator = "z";
}

  //construct the string to be displayed
  char timeChr[10];
  sprintf(timeChr, "%s%d%s%d%s", space, hours, seperator, minutes, suffix);

  /////////////////////////////////////////////////////
  //date
  switch(dayOfWeek){
  case 1: 
    dayOfWeekChar ="Sun";
    break;
  case 2: 
    dayOfWeekChar ="Mon";
    break;
  case 3: 
    dayOfWeekChar ="Tue";
    break;
  case 4: 
    dayOfWeekChar ="Wed";
    break;
  case 5: 
    dayOfWeekChar ="Thu";
    break;
  case 6: 
    dayOfWeekChar ="Fri";
    break;
  case 7: 
    dayOfWeekChar ="Sat";
    break;
  }

  //construct the string to be displayed
  char dateChr[10];
  sprintf(dateChr, " %s %dz%d ", dayOfWeekChar, month, dayOfMonth);

  //clear the display and write the strings for the time and date
  epaper.writeTop(timeChr);
  epaper.writeBottom(dateChr);
  epaper.writeDisplay();

  delay(30000);
} //end of loop

/////////////
//DS3231 RTC interface
void initChrono()
{
  set_time();
  set_date();
}

void set_date()
{
  Wire.beginTransmission(104);
  Wire.write(3);
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}

void get_date()
{
  Wire.beginTransmission(104); 
  Wire.write(3);//set register to 3 (day)
  Wire.endTransmission();
  Wire.requestFrom(104, 4); //get 5 bytes(day,date,month,year,control);
  dayOfWeek   = bcdToDec(Wire.read());
  dayOfMonth  = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year  = bcdToDec(Wire.read());
}

void set_time()
{
  Wire.beginTransmission(104);
  Wire.write(0);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.endTransmission();
}

void get_time()
{
  Wire.beginTransmission(104); 
  Wire.write(0);//set register to 0
  Wire.endTransmission();
  Wire.requestFrom(104, 3);//get 3 bytes (seconds,minutes,hours);
  seconds = bcdToDec(Wire.read() & 0x7f);
  minutes = bcdToDec(Wire.read());
  hours = bcdToDec(Wire.read() & 0x3f);
}

///////////////////////////////////////////////////////////////////////
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

Thanks for all your help!