Please rip this code apart

Hi there, I have recently acquired my first Arduino and have bought a real time clock and LCD shield. I got those items as I want to learn how to use the Arduino and thought I'd build a set-able clock to start with, kinda dip my toe in scenario.

Anyway, I managed to put the hardware together and hack some code together (I'm coming from c#) and the resultant sketch is listed below.

I'm really new to writing sketch and thought my code seemed quite long simply to achieve what I have so I thought I'd post it here and see if some kindly experienced coder can look over it and tell me if I'm even in the ball park of what it should(could) look like.

Please feel free to rip it to peaces, any feed back would be much appreciated even if it's just to say it's rubbish, I'm kinda isolated where I live and so have no one to bounce off.

As I say any feedback is welcome, I appreciate your time for looking :slight_smile:

Arduino Mega
RTC - http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&item=140925902277
LCDKeypad - http://cgi.ebay.co.uk/ws/eBayISAPI.dll?ViewItem&item=290784693919


#include <LiquidCrystal.h>
#include <LCDKeypad.h>
#include <stdio.h>
#include <string.h>
#include <DS1302.h>
LCDKeypad lcd;

/*
Sketch for showing and setting the time via LCD Keypad shield.

2013, By Rick Bushill

Based on sketches for DS1302 Real Time Clock
http://quadpoint.org/projects/arduino-ds1302
and 
LCDKeypad
http://www.dfrobot.com/wiki/index.php?title=Arduino_LCD_KeyPad_Shield_%28SKU:_DFR0009%29
*/

/* Set the appropriate digital I/O pin connections for the rtc */
uint8_t CE_PIN   = 11;
uint8_t IO_PIN   = 12;
uint8_t SCLK_PIN = 13;

//menu control
boolean editingMode = false;
unsigned long blinkBuf = 0;
int buttonPressed = -1;

/*
Befine a byte to use as an indicator to which part of the clock we are editing
in the format : - year, month, date, hour, min, sec, day
*/
byte editingBit = B0000000;

//define LCD butons
#define btnUP       1
#define btnDOWN     2
#define btnSELECT   4
#define btnLEFT     3
#define btnRIGHT    0
#define btnNONE     5

/* Create buffers to hold date strings */
char day[3];
char timeBuf[10];
char dateBuf[40];

/* Create a DS1302 object */
DS1302 rtc(CE_PIN, IO_PIN, SCLK_PIN);

//temp time to hold date we are setting
Time tempTime(0, 0, 0, 0, 0, 0, 0);

//function shows the time and dat on the LCD
void print_time(Time& t)
{
  /* Name the day of the week */
  memset(day, 0, sizeof(day));  /* clear day buffer */
  switch (t.day) {
    case 1:
      strcpy(day, "Sun");
      break;
    case 2:
      strcpy(day, "Mon");
      break;
    case 3:
      strcpy(day, "Tue");
      break;
    case 4:
      strcpy(day, "Wed");
      break;
    case 5:
      strcpy(day, "Thu");
      break;
    case 6:
      strcpy(day, "Fri");
      break;
    case 7:
      strcpy(day, "Sat");
      break;
  }
  
  //print date to first row of LCD
  snprintf(dateBuf, sizeof(dateBuf), "%s %02d-%02d-%04d",
           day, t.date, t.mon, t.yr); 
  
  //print time to 2nd row of LCD
  snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d:%02d",
           t.hr, t.min, t.sec);
 
 lcd.setCursor(0,0);          
 lcd.write(dateBuf); 
 lcd.setCursor(0,1);
 lcd.write(timeBuf); 
}

void setup()
{
  lcd.begin(16, 2);
  lcd.clear();
  
  /* Initialize a new chip by turning off write protection and clearing the
     clock halt flag. These methods needn't always be called. See the DS1302
     datasheet for details. */
  rtc.write_protect(false);
  rtc.halt(false);
}


/* Loop and print the time every second */
void loop()
{
  int buttonPressed = -1;
  buttonPressed =lcd.button();  // read the buttons
  delay(200);
 
  //if we pressed the select button, put the sketch into time set mode
  if(buttonPressed == btnSELECT)
  {
    if(editingMode)
    {
      //if we are in edit mode, exit edit mode and send time to rtc
      editingMode = false;
      editingBit = B0000000;
      rtc.time(tempTime);
    }
    else
    {
      //if we are not in edit mode, enter edit mode and set edit pointer to the first item to edit, ie day of week
      tempTime = rtc.time();
      editingBit = B1000000;
      editingMode = true;
      blinkBuf = 0;
    }
  }// end if buttonPressed == btnSELECT
  else
  {
    if(editingMode)
    {
      //left or right buttons simply bit shift the ponter byte to indicate which bit of the date we are setting
      //year, month, date, hour, min, sec, day
      switch (buttonPressed)
      {
        case btnNONE:
          break; 
        case btnRIGHT:
          editingBit = editingBit >> 1;
          if(editingBit == B0000000)
            editingBit =   B1000000;
          break;
        case btnLEFT:
          editingBit = editingBit << 1;
          if(editingBit == B0000000)
            editingBit =   B0000001;
          break;
        case btnUP:
          incrementSelectedTimeItem();
          break;
        case btnDOWN:
          decrementSelectedTimeItem();
          break;
      }//switch buttonPress
      
      //print the temp time we have adjusted
      print_time(tempTime);
      
      //ensure the item we are editing is blinking on screen
      blinkSelectedTimeItem();
    }//end if editingMode
    else
    {
      /*Not Editing so get the current time and date from the chip and print to LCD */
        Time t = rtc.time();
        print_time(t);
    }//end else editingMode
  } //end else buttonPressed == btnSELECT
} 
 
//function increments the date element we are setting
void incrementSelectedTimeItem()
{
        switch(editingBit)
          {
            case B0001000:
              tempTime.yr++;
              break;
            case B0010000:
              tempTime.mon++;
              if(tempTime.mon >= 12)
                tempTime.mon = 1;
              break;
            case B0100000:
              tempTime.date++;
              if(tempTime.date >= 31)
                tempTime.date = 1;
              break;
            case B0000100:
              tempTime.hr++;
              if(tempTime.hr >= 24)
                tempTime.hr = 0;
              break;
            case B0000010:
              tempTime.min++;
              if(tempTime.min >= 60)
                tempTime.min = 0;
              break;
            case B0000001:
              tempTime.sec++;
              if(tempTime.sec >= 60)
                tempTime.sec = 0;
              break;
            case B1000000:
              tempTime.day++;
              if(tempTime.day >= 7)
                tempTime.day = 1;
              break;
          }
  }
  
  //decrements the date element we are setting
  void decrementSelectedTimeItem()
  {
    switch(editingBit)
          {
            case B0001000:
              tempTime.yr--;
              break;
            case B0010000:
              tempTime.mon--;
              if(tempTime.mon <= 0)
                tempTime.mon = 12;
              break;
            case B0100000:
              tempTime.date--;
              if(tempTime.date <= 0)
                tempTime.date = 31;
              break;
            case B0000100:
              tempTime.hr--;
              if(tempTime.hr <= -1)
                tempTime.hr = 23;
              break;
            case B0000010:
              tempTime.min--;
              if(tempTime.min <= -1)
                tempTime.min = 59;
              break;
            case B0000001:
              tempTime.sec--;
              if(tempTime.sec <= -1)
                tempTime.sec = 59;
              break;
            case B1000000:
              tempTime.day--;
              if(tempTime.day <= -1)
                tempTime.day = 6;
              break;
          }
  }
  
  //blinks the date element we are setting
  void blinkSelectedTimeItem()
  {
    if((millis() - blinkBuf) < 700)
      return;

      blinkBuf = millis();
    
        switch (editingBit)
        {
          case B0001000://year
            lcd.setCursor(10,0);
            lcd.write("    ");
            break;
          case B0100000://date
            lcd.setCursor(4,0);
            lcd.write("  ");
            break;
          case B0010000://month
            lcd.setCursor(7,0);
            lcd.write("  ");
            break;
          case B1000000://day
            lcd.setCursor(0,0);
            lcd.write("   ");
            break;
          case B0000100://hr
            lcd.setCursor(0,1);
            lcd.write("  "); 
            break;
          case B00000010://min
            lcd.setCursor(3,1);
            lcd.write("  ");
            break;
          case B0000001://sec
            lcd.setCursor(6,1);
            lcd.write("   ");
            break;
        }
}

The only things I see that is "wrong" is that you have a global version of buttonPressed whose scope is overridden by the local version of buttonPressed inside of loop (which may not be as long-lived as you expect), and you have some >= checks that I think look like they should be > checks:

    if(tempTime.mon >= 12)
    if(tempTime.date >= 31)
    if(tempTime.day >= 7)

I'm not sure about the "<= -1" checks on time fields. Are you sure those are signed?

Minor nits include:
buttonPressed can probably be a "byte."
you don't need to memset() the day array, since strcpy() will add the terminating null for you.
If you're going to use snprintf(), you might as well use strncpy() as well.
You consistently use a 7bit value for editingBit, including implementing a 7bit rotate. I find that a bit worrisome, since I'm not sure that it's intended. I guess you're using shifts to reflect the "shift" of the cursor from one field to another, but I don't see any reason for this to be a bitmask at all...

I didn't check the LCD or RTC specifics; I'm not familiar with those off the top of my head.

Hi, thanks for you comments.

I need to read up a bit more on strcpy, snprintf and strncpy, I must admit, I just used them parrot fashion when looking at another example so thanks for pointing that out.

The 7bit value for editingBit was just an idea I had for representing the the position in a line of date data that looked like this :

Mon 03-04-2013 09:30:00

You were correct in saying it is a cursor, the 7 bits represent which of the above we are incrementing or decrementing. I thought that using a byte of data was neat as I could shift the pointer using the bit-wise operator, I could just as easily have used an integer I guess but coming form c# I don't get to use bits and bytes that often so this just seemed clever to me. Do you think I could have taken a better approach?

Must admit, I missed the scoping of buttonPressed, thanks for picking that up :slight_smile:

I'll check the data type of the time properties but I think your right and they are probably are unsigned.

Anyway, thanks so much for your time, it's really helpful to get some feedback as most of the time I'm the only one who sees my own code and in this early stage of learning it's really helpful to have nice people like you, Thank you :slight_smile: