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:
- seconds 0 - 59
- minutes 0 - 59
- hour 0 - 24
- weekday 1 - 7
- day of the month 1 – 31
- month 1 -12
- 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