Translate

Thursday, 8 May 2014

Room Management System – The Menu – part 3


Ready for some adventure? OK, today we change the first values. Apart from adjusting the time, all other variables change currently as variables and will be lost after powering down or a reset. The time and date changes are permanent or until we change them again.
As usual. We start at the top of our sketch, this time right at the top in the “includes” section, where we add a definition and a variable.

/////////////////////Includes/////////////////////////////

#include <DS1307RTC.h>

#include <Time.h>

#include <Wire.h>

#include <ShiftLCD.h>

#include <avr/pgmspace.h>

//>>>>>>>>>>>>>addition starts here<<<<<<<<<<<<<

#define DS1307_ADDRESS 0x68 //Address of our real time clock

//needed to write to

byte zero = 0x00; //work around for an issue with the RTC

//>>>>>>>>>>>>>>addition ends here<<<<<<<<<<<<<

/////////////////////Declaring the Variables/////////////////

///////////Timer and Sensitivity Settings to be changed to individual needs////////////////

Next, we move own to “//RTC and Holiday switch timers///”, where we add:

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

/////////////////////////////RTC and Holiday switch timers///////////////////////////////

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

//>>>>>>>>>>>>>addition starts here<<<<<<<<<<<<<

tmElements_t tm; //initializing RTC

//>>>>>>>>>>>>>addition ends here<<<<<<<<<<<<<

byte room1Lights = 0; //var to hold the on command for room light

byte room2Lights = 0; //var to hold the on command for room light

Since we pulled the RTC initialisation up to the declaration part, we need to delete it in the main loop and find the part where it says processing the input;

//Serial.println(sensorValue);

//////////////////processing the input/////////////////////

//>>>>>>>>>>>>>DELETE the line below<<<<<<<<<<<<<

tmElements_t tm; //initializing RTC

//>>>>>>>>>>>>>DELETE ends here<<<<<<<<<<<<<

if(RTC.read(tm)) { //Reading the clock

currentHour = tm.Hour; //passing the time into a var

currentMinute = tm.Minute; //passing the time into a var

currentDay = tm.Wday - 1; //passing Weekday 

I am sorry for making you jump around a bit but I try to keep things together in a way, to make it as easy as possible to follow. Well, we have to go back up in the declaration part and find “Menu and user interface”. There we need to add a few strings and characters into the program memory://Storing some menu messages in the program memory

prog_char msg_0[] PROGMEM = "Not Used";

prog_char msg_1[] PROGMEM = "Saving....";

prog_char msg_2[] PROGMEM = "Setup mode";

prog_char msg_3[] PROGMEM = "Starting....";

prog_char msg_4[] PROGMEM = "RMU 1.2.7";

//>>>>>>>>>>>>>ADD Weekday<<<<<<<<<<<<<

prog_char msg_5[] PROGMEM = "Weekday";

//>>>>>>>>>>>>>addition ends here<<<<<<<<<<<

//Don't forget, we need to add msg_5 to the table below

//Creating the table for the stored menu messages

PROGMEM const char *msg_table[] = {

msg_0,

msg_1,

msg_2,

msg_3,

msg_4,

msg_5 //<<<<<<<<Add to msg_table

};

//>>>>>>>>>>>>>Add to table below 0, :, .<<<<<<<<<<<<<

//storing some special char's in the program memory

const byte char_table[] PROGMEM = {

B01111110, //Arrow right

B01111111, //Arrow left

B00110000, //0 //<<<add 0

B00111010, //separator //<<<add :

B00101110 //dot //<<<add .

};

From here we move down to the end of the sketch and there to the end of the function selectMenu(), where we add the part what happens if a menu point is selected.

if(menuOption == 20){ //if it's menu option 21

lcd.clear(); //clear lcd

//print retrieved string on lcd

lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[19]))));

lcd.setCursor(15, 0); //set cursor to row 2 column 1

//print retrieved message string

lcd.write(pgm_read_byte(&char_table[0]));

}

}

//>>>>>>>>>>>>>Addition starts here<<<<<<<<<<<<<<

if(button == btnSelect){ //if the select button is pressed

if(menuOption == 1){ //and menu option is 1

adjust_date_time(); //go to adjust date and time

return;

}

if(menuOption == 2){ //and menu option is 2

get_Timer("Set Sensitivity", sensitivity, 0, 1000); //go to function

return;

}

if(menuOption == 3){ //and menu option is 3

get_Timer("Set photocell R", photoCellCutOff, 0, 1024); //go to function

return;

}

if(menuOption == 4) return; //and menu option is 4, return (not used)

if(menuOption == 5){ //and menu option is 5

get_Timer("Set photocell O", photoOutsideOff, 0, 1024); //go to function

return;

}

if(menuOption == 6) return; //and menu option is 6, return (not used)

}

//>>>>>>>>>>>>>Addition ends here<<<<<<<<<<<<

}

}

Let's have a quick look in to what we are doing here. Remember, just after we entered the selectMenu() function, we have the two lines of code:


byte button = 0;

button = read_act_button();

In this part we are looking for the button “btnSelect”. If the button select was pressed, we check at which menu point and call a function according to the data we need to change. What happens in the function call adjust_date_time() I will explain a little later when we write the function. The function call for the following menu options is a little more flexible and needs various variables. In get_Timer(“Message text”, valueOfVariableToChange, minValue, maxValue) we pass 4 variables.
First variable is a message text to be displayed while we are changing the value. The second variable is the current value of the variable we want to change. The next variable is the minimum mark of the changeable range and the last variable is the maxmark of the changeable range. Now lets build the first new function, adjust_Date_Time(). We do this right after the selectMenu() function.


void adjust_date_time(){

byte button = 0; //set button var to 0

//function call to adjust the minute part of the system time

byte minuteT = get_Timer("ADJ Time Minute", tm.Minute, 0, 59);

if(minuteT >= 0 && minuteT < 60){ //check the returned result

//function call to adjust the hour part of the system time

byte hourT = get_Timer("ADJ Time Hour", tm.Hour, 0, 23);

if(hourT >= 0 && hourT < 24){ //check the returned result

//function call to adjust the day of the week

byte weekDay = get_weekday();

if(weekDay > 0 && weekDay <= 7){ //check the returned result

//function call to adjust the day of the month

byte monthDay = get_Timer("ADJ Date Day", tm.Day, 1, 31);

if(monthDay >= 1 && monthDay <=31){ //check the returned result

//function call to adjust the month

byte monthT = get_Timer("ADJ Date Month", tm.Month, 1, 12);

if(monthT >= 1 && monthT <= 12){ //check returned result

//function call to adjust the year

byte yearT = get_Timer("ADJ Date Year", tmYearToCalendar(tm.Year)-2000, 0, 99);

if(yearT >= 0 && yearT <= 99){ //check the returned results

byte value = weekday; //passing on the variable for calculations

//the following lcd statements print the adjusted results

//on the LCD

lcd.clear();

if(hourT < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(hourT);

lcd.write(pgm_read_byte(&char_table[3])); //print separator

if(minuteT < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(minuteT);

lcd.setCursor(0, 1);

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(weekday_table[weekday]))));

lcd.print(" ");

if(monthDay < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(monthDay);

lcd.write(pgm_read_byte(&char_table[4])); //print dot

if(monthT < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(monthT);

lcd.write(pgm_read_byte(&char_table[4])); //print dot

lcd.print(yearT);

while(button != btnSelect){ //loop until button select is pressed

button = read_act_buttons();

if(button == btnMenu) return; //if button Menu is pressed return without saving

if(button == btnSelect){ //if button select is pressed

lcd.clear();

lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(msg_table[1])))); //print saving

delay(1000);

//function call to save the adjusted values to RTC

save_time(minuteT, hourT, weekDay, monthDay, monthT, yearT);

}

}

return;

}

}

}

}

}

}

}

The function adjust_date_time() is just a little helper to walk through the single values contained in time (hours and minutes) and date (weekday, day, month and year). Starting with the minute portion of time, calling the function get_Timer() where the value is changed and move on to changes the hour portion of time if a proper value is returned. The hour portion of time is calling again the get_Timer() function where the value is changed and we move on to adjust the day of the week if a valid result is returned and so on until we have been through all the steps. The actual changes are made in the function get_Timer() and get_weekday(). After we have done all the changes, we print the updated time and date and again, we run into a loop, waiting for a user input, a button to be pressed. If we don't like the results, we press the menu button and we go back to the main menu without changing anything. Other wise we press select and do a function call to save the new results to the RTC.
Having just called three new function, lets have a look at the one mostly used one first, get_Timer().
Since this function is the most used one after selectMenu(), we put it right after the function selectMenu().

int get_Timer(char timerText[], int reading, int startVal, int maxCount){

byte button = 0; //resetting the button var

lcd.clear(); //clear the screen

lcd.print(timerText); //print the passed on info text

lcd.setCursor(0, 1); //set the cursor to second row first column

lcd.print(reading); //print the passed on value to be changed

while(button != btnSelect){ //wait for a button to be pressed

button = read_act_buttons(); //check if a button is pressed

if(button == btnSearch){ //if btnSearch is pressed

//if the reading is somewhere between the limit we add 1

//to the value with every loop through while the btnSearch

//is pressed

if(reading > startVal && reading < maxCount) reading++;

if(reading >= maxCount){ //if the value to be changed reaches

//the max limit

reading = startVal - 1; //reset the value to be changed to

//the lower limit

reading++; //and add 1 with each pass through the loop

//while btnSearch is pressed

}

lcd.setCursor(0, 1); //setting the cursor to first column, second row

lcd.print(reading); //print the updating value

lcd.print(" "); //print 4 empty spaces to delete left overs

//if we the value changes from high to

//low limit

}

}

return reading; //return the value

}

Even there is no real magic. We print the received info text and value on our LCD, and wait for a button to be pressed. If it's the button search, we check if we are above and below the respective limits and add 1 with every pass through the while loop as long as btnSearch is pressed. Soon as we reach the max limit, we set the value to be changed to the lower limit and again add 1 until we are happy with the result or we reach the max limit again. In the end we print again the updated result so we can follow the updating process. The 4 empty spaces are needed to remove left overs if the value changes from max value to minimum since the LCD display does not remove digits if they are not used any more. If the last number was 1024 and the next one to print is a 1 it would still read 1024 and adding 1 would read 2024, adding another one would read 3024. Got the point? We need to blank out the digits we don't need any more and that would be 024 in that case. If we are happy with the result and press the select button gets us out of the loop and back to where we came from. In most cases we would get away with a simple return in the end. However, in some cases where we have to store values permanent updating the RTC, we need to pass the updated value back to the calling function.

Let's move on to the next function, get_weekday(), witch we put right at the end of the sketch.

byte get_weekday(){

byte button = 0; //resetting the button var

byte value = currentDay; //passing the global weekday variable to a

//local one for calculations

//Serial.println(currentDay); //debug only

lcd.clear(); //clear the screen

//print what we are updating (weekday)

lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(msg_table[5]))));

lcd.setCursor(0, 1); //set the cursor to second row, first column

//print the current value for weekday

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(weekday_table[value]))));

while(button != btnSelect){ //wait for a button to be pressed

button = read_act_buttons(); //checking for a button pressed

if(button == btnSearch){ //if it was btnSearch

//The minimum and maximum values are a little tricky

//We need 1 to 7 for Sun to Sat to be passed back to RTC

//and wee need 0 to 6 to retrieve the printed strings from the

//program memory and that's why the following range check

//looks a little strange

if(value >= 0 && value < 7) value++; //checking between 0 and 6

if(value >= 7){ //if we come up to 7

value = -1; //setting the value to one since it did not want

//to start at 0

value++; //again adding 1 while btnSearch is pressed

}

//Serial.print("Value: "); //debug only

//Serial.println(value); //debug only

lcd.setCursor(0, 1); //set cursor to second row, first column

//printing the updating value

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(weekday_table[value]))));

}

}

return value + 1; //return the value and add 1 to be stored in RTC memory

}

The function get_weekday() works basic the same way as the get_Timer() function. The only difference is that we are not printing a numeric value, we print the names of the weekdays as we are changing them. The only trick is, to get the values right since the RTC needs the numbers from 1 to 7 where 1 stands for Sunday, 2 for Monday …......and 7 for Saturday. It's not very nice to deal with numbers while everybody is used to the common names. But the day names are stored in a table and the processor always starts counting at 0 and not at 1. It's not a big deal at all. The global variable currentDay is already converted to the value needed to retrieve the correct day name from our table, – so we just keep on working with it. The only thing we need to think of is adding 1 to the numeric value to get the correct weekday being stored in the RTC memory.

Before we carry on to the next function, a quick review and a small change in the get_Timer() function. Let's go back to it and find the part:


if(reading > startVal && reading < maxCount) reading++;

if(reading >= maxCount){ //if the value to be changed reaches

//the max limit

reading = startVal-1; //reset the value to be changed to

//the lower limit

reading++; //and add 1 with each pass through the loop

//while btnSearch is pressed

}

Even here we have to add -1 to the start value since we add straight away 1 after we have changed the value from upper to lower limit. That means setting the value to lower limit and adding 1 right away, will never let us start at the lower limit. Thinking it over, we don't need to add 1 right away after we have set the value to the lower limit. Taking it out saves us from that part: reading = startVal-1;

Let's change it as follows:

if(reading >= startVal && reading < maxCount) reading++;

if(reading >= maxCount) reading = startVal; //reset the value to be changed to

//the lower limit

And why we are still in the set_weekday() function, we change the respective part:

if(value >= 0 && value < 7) value++; //checking between 0 and 6

if(value >= 7){ //if we come up to 7

value = -1; //setting the value to one since it did not want

//to start at 0

value++; //again adding 1 while btnSearch is pressed

}

to:

if(value >= 0 && value < 7) value++; //checking between 0 and 6

if(value >= 7) value = 0; //setting the value back to 0 

This will do the job even faster and saves us a couple of bytes on memory which we are in need of anyway.

Now we move on to the next function. save_time(), which we put at the end of the complete sketch, that this one will not be used very often.


void save_time(byte mi, byte hr, byte wkDay, byte da, byte mo, byte yr){

Wire.beginTransmission(DS1307_ADDRESS); //starting transmission to DS 1307 RTC

Wire.write(zero); //needed leading zero byte

Wire.write(decToBcd(0)); //set seconds to 0

Wire.write(decToBcd(mi)); //set minutes to updated value

Wire.write(decToBcd(hr)); //set hour to updated value

Wire.write(decToBcd(wkDay)); //set weekday to updated value

Wire.write(decToBcd(da)); //set month day to updated value

Wire.write(decToBcd(mo)); //set month to updated value

Wire.write(decToBcd(yr)); //set year to updated value

Wire.write(zero); //send finishing zero byte

Wire.endTransmission(); //close transmission

}

This function is pretty straight forward. We pass the updated time and date values, start transmission to the RTC and send the values using Wire.write. How you name the variables is not important as far as you know what you are transmitting. Important is the order in which you transmit.
The RTC needs it:

  1. seconds 0 - 59
  2. minutes 0 - 59
  3. hour 0 - 24
  4. weekday 1 - 7
  5. day of the month 1 – 31
  6. month 1 -12
  7. year 0-99

Please check the data sheet and accompanying examples if you use a different RTC chip
Since the data has to be sent in binary code, we have to convert the numbers to binary, using the little function decToBcd(value), which again, can be put right on the end of our sketch.


byte decToBcd(byte val){

return ((val/10*16) + (val%10));

}

The rest of the implemented menu options 2 – 6 are again pretty straight forward since they are all using the get_Timer() function, passing a info text, the value to change, the Min and max allowed value. The only issue is, that we are changing the values, but we are not yet passing them back to the system. I am still in a deciding state if the changed values are to be passed back as variables and are lost soon as something goes wrong or to play around a bit with EPROM memory to store them permanent.

There more in the next post.

Since part of the addition might be a little confusing, I add the complete code of the added functions. It might be still in a bit a different order, as I just wrote to put it but the functions it self are complete and working.


selectMenu(){

.................

................

.................

if(menuOption == 20){ //if it's menu option 21

lcd.clear(); //clear lcd

//print retrieved string on lcd

lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[19]))));

lcd.setCursor(15, 0); //set cursor to row 2 column 1

//print retrieved message srting

lcd.write(pgm_read_byte(&char_table[0]));

}

}

if(button == btnSelect){ //if the select button is pressed

if(menuOption == 1){ //and menu option is 1

adjust_date_time(); //go to adjust date and time

return;

}

if(menuOption == 2){ //and menu option is 2

get_Timer("Set Sensitivity", sensitivity, 0, 1000); //go to function

return;

}

if(menuOption == 3){ //and menu option is 3

get_Timer("Set photocell R", photoCellCutOff, 0, 1024); //go to function

return;

}

if(menuOption == 4) return; //and menu option is 4 return (not used)

if(menuOption == 5){ //and menu option is 5

get_Timer("Set photocell O", photoOutsideOff, 0, 1024); //go to function

return;

}

if(menuOption == 6) return; //and menu option is 6 return (not used)

}

}

}

void adjust_date_time(){

byte button = 0; //set button var to 0

//function call to adjust the minute part of the system time

byte minuteT = get_Timer("ADJ Time Minute", tm.Minute, 0, 59);

if(minuteT >= 0 && minuteT < 60){ //check the returned result

//function call to adjust the hour part of the system time

byte hourT = get_Timer("ADJ Time Hour", tm.Hour, 0, 23);

if(hourT >= 0 && hourT < 24){ //check the returned result

//function call to adjust the day of the week

byte weekDay = get_weekday();

if(weekDay > 0 && weekDay <= 7){ //check the returned result

//function call to adjust the day of the month

byte monthDay = get_Timer("ADJ Date Day", tm.Day, 1, 31);

if(monthDay >= 1 && monthDay <=31){ //check the returned result

//function call to adjust the month

byte monthT = get_Timer("ADJ Date Month", tm.Month, 1, 12);

if(monthT >= 1 && monthT <= 12){ //check returned result

//function call to adjust the year

byte yearT = get_Timer("ADJ Date Year", tmYearToCalendar(tm.Year)-2000, 0, 99);

if(yearT >= 0 && yearT <= 99){ //check the returned results

byte value = weekDay; //passing on the variable for calculations

//the following lcd statements print the adjusted results

//on the LCD

lcd.clear();

if(hourT < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(hourT);

lcd.write(pgm_read_byte(&char_table[3])); //print separator

if(minuteT < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(minuteT);

lcd.setCursor(0, 1);

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(weekday_table[value-1]))));

lcd.print(" ");

if(monthDay < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(monthDay);

lcd.write(pgm_read_byte(&char_table[4])); //print dot

if(monthT < 10) lcd.write(pgm_read_byte(&char_table[2])); //print 0

lcd.print(monthT);

lcd.write(pgm_read_byte(&char_table[4])); //print dot

lcd.print(yearT);

while(button != btnSelect){ //loop until button select is pressed

button = read_act_buttons();

if(button == btnMenu) return; //if button Menu is pressed return without saving

if(button == btnSelect){ //if button select is pressed

lcd.clear();

lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(msg_table[1])))); //print saving

delay(1000);

//function call to save the adjusted values to RTC

save_time(minuteT, hourT, weekDay, monthDay, monthT, yearT);

}

}

return;

}

}

}

}

}

}

}

void save_time(byte mi, byte hr, byte wkDay, byte da, byte mo, byte yr){

Wire.beginTransmission(DS1307_ADDRESS); //starting transmission to DS 1307 RTC

Wire.write(zero); //needed leading zero byte

Wire.write(decToBcd(0)); //set seconds to 0

Wire.write(decToBcd(mi)); //set minutes to updated value

Wire.write(decToBcd(hr)); //set hour to updated value

Wire.write(decToBcd(wkDay)); //set weekday to updated value

Wire.write(decToBcd(da)); //set month day to updated value

Wire.write(decToBcd(mo)); //set month to updated value

Wire.write(decToBcd(yr)); //set year to updated value

Wire.write(zero); //send finishing zero byte

Wire.endTransmission(); //close transmission

}

byte decToBcd(byte val){

return ((val/10*16) + (val%10));

}

int get_Timer(char timerText[], int reading, int startVal, int maxCount){

byte button = 0; //resetting the button var

lcd.clear(); //clear the screen

lcd.print(timerText); //print the passed on info text

lcd.setCursor(0, 1); //set the cursor to second row first column

lcd.print(reading); //print the passed on value to be changed

while(button != btnSelect){ //wait for a button to be pressed

button = read_act_buttons(); //check if a button is pressed

if(button == btnSearch){ //if btnSearch is pressed

//if the reading is somewhere between the limit we add 1

//to the value with every loop through while the btnSearch

//is pressed

if(reading >= startVal && reading < maxCount) reading++;

if(reading >= maxCount)reading = startVal; //reset the value to be changed to

//the lower limit

lcd.setCursor(0, 1); //setting the cursor to first column, second row

lcd.print(reading); //print the updating value

lcd.print(" "); //print 4 empty spaces to delete left overs

//if we the value changes from high to

//low limit

}

}

return reading; //return the value

}

byte get_weekday(){

byte button = 0; //resetting the button var

byte value = currentDay; //passing the global weekday variable to a

//local one for calculations

//Serial.println(currentDay); //debug only

lcd.clear(); //clear the screen

//print what we are updating (weekday)

lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(msg_table[5]))));

lcd.setCursor(0, 1); //set the cursor to second row, first column

//print the current value for weekday

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(weekday_table[value]))));

while(button != btnSelect){ //wait for a button to be pressed

button = read_act_buttons(); //checking for a button pressed

if(button == btnSearch){ //if it was btnSearch

if(value >= 0 && value < 7) value++; //checking between 0 and 6

if(value >= 7) value = 0; //setting the value back to 0

//Serial.print("Value: "); //debug only

//Serial.println(value); //debug only

lcd.setCursor(0, 1); //set cursor to second row, first column

//printing the updating value

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(weekday_table[value]))));

}

}

return value + 1; //return the value and add 1 to be stored in RTC memory

}

No comments:

Post a Comment