Translate

Saturday 10 May 2014

Room Management System – The Menu – part 4


For time being, I have decided not to use the EEPROM since it doesn't help freeing RAM or program memory. In fact it's using up some program memory for reading the stored data and since the lifetime of the EEPROM is quite restricted to about 100.000 read/write cycles, we still need to store the retrieved data in a variable being stored in RAM. Let's get everything else working and worry about storing the timer values in EEPROM later.

While looking through our sketch at this point, I found another 2 variables which we need to change. Lets jump into the declaration section, pretty much in the beginning where we have all the timer and sensitivity settings:

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

unsigned int sensitivity = 400; //should be between 200 and 1000 as

//lower the number as more responsive

//the system will be

unsigned int photoCellCutOn = 320; //var holding the switching limit for the photocell

unsigned int photoCellCutOff = 280; //var holding the value where the photocell cuts off

unsigned int photoOutsideOn = 220; //var holding the value which the photocell reading

unsigned int photoOutsideOff = 260;

//>>>>>>>>>>>>>Change below here<<<<<<<<<<<<<

//The following two variables need to be changed from

//unsigned int to byte as shown below.

byte hourOutsideOff = 23; //var holding the time (full hours) in which the lights have

//to switch off

byte minuteOutsideOff = 30; //var holding the time (minutes) in which the lights

//have to switch off

//>>>>>>>>>>>>>Change ends here<<<<<<<<<<<<<

int dBed1 = 60; //delay time in seconds for bedroom 1

From here we move down to “defining Arduino pins”. There we look for

const int lightSensor = A0; //defining the input for the photocell

and change it to

const byte lightSensor = 0; //defining the input for the photocell

OK, in the last post we managed to change the system time and date. Let's change a few more variables and implement the first submenu. There fore we move down to the last part of our selectMenu() function.

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]));

}

}

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;

}

// to make the system recognise the changed value we need to pass it back and this we do

//simply in adding the variable we change in front of the function call

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

//>>>>>>>>add sensitivity = in front of function call get_Timer()<<<<<<

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

return;

}

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

//>>>>>>>>add photoCellCutOff = in front of get_Timer()<<<<<<

photoCellCutOff = 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

//>>>>>>>>add photoOutsideOff = in front of get_Timer()<<<<<<

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

return;

}

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

}

}

}

Setting up the rooms we have various variables to take care of. First we have the delay timer, being responsible for the time delay to switch off the lights after the last detection of movement in a room. Second, we have multiple timers and there activation for the automated light switching while nobody is home. Since we have a menu point for every room, we handle the settings for the rooms in a sub menu.
There fore we make a quick d-tour to the declaration part where we have all the variables for the menu and user interface, where we store the sub menu points in the program memory:

///////////////////Menu and user interface/////////////////////////////////

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

const byte btnMenu = 1; //defining the menu button – moves through the menu

const byte btnSearch = 2; //defining the search button – moves through values

const byte btnSelect = 3; //defining the select button – selects a menu or a value

const byte btnNone = 0; //defining the non button pressed var

int act_key_in = 0; //var holding the key related sensor reading

byte menuOption = 0; //var to count current menu option

const byte menuOptions = 20; //available menu options

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

//first we need to add 2 variables to count through the sub menu options

byte submenu = 0; //var to count current submenu option

const byte submenus = 7; //available submenu options

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

char buffer_M[20]; //var holding the menu strings retrieved from

//the program memory

Now we move down in the same section just a bit.

//storing the main menu points in the program memory

prog_char menu_0[] PROGMEM = "Date/Time";

prog_char menu_1[] PROGMEM = "Sensitivity";

prog_char menu_2[] PROGMEM = "Room photo cut";

prog_char menu_3[] PROGMEM = "Room photo limit";

prog_char menu_4[] PROGMEM = "OS photo cut";

prog_char menu_5[] PROGMEM = "OS photo limit";

prog_char menu_6[] PROGMEM = "Room 1";

prog_char menu_7[] PROGMEM = "Room 2";

prog_char menu_8[] PROGMEM = "Room 3";

prog_char menu_9[] PROGMEM = "Room 4";

prog_char menu_10[] PROGMEM = "Room 5";

prog_char menu_11[] PROGMEM = "Room 6";

prog_char menu_12[] PROGMEM = "Room 7";

prog_char menu_13[] PROGMEM = "Room 8";

prog_char menu_14[] PROGMEM = "Room 9";

prog_char menu_15[] PROGMEM = "Room 10";

prog_char menu_16[] PROGMEM = "AC 1";

prog_char menu_17[] PROGMEM = "AC 2";

prog_char menu_18[] PROGMEM = "AC 3";

prog_char menu_19[] PROGMEM = "AC 4";

PROGMEM const char *menu_table[] = {

menu_0,

menu_1,

menu_2,

menu_3,

menu_4,

menu_5,

menu_6,

menu_7,

menu_8,

menu_9,

menu_10,

menu_11,

menu_12,

menu_13,

menu_14,

menu_15,

menu_16,

menu_17,

menu_18,

menu_19

};

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

//storing the sub menu points in the program memory

prog_char submenu_0[] PROGMEM = "PIR delay R";

prog_char submenu_1[] PROGMEM = "HT1 Active R";

prog_char submenu_2[] PROGMEM = "Timer 1 R";

prog_char submenu_3[] PROGMEM = "HT2 Active R";

prog_char submenu_4[] PROGMEM = "Timer 2 R";

prog_char submenu_5[] PROGMEM = "HT3 Active R";

prog_char submenu_6[] PROGMEM = "Timer 3 R";

PROGMEM const char *submenu_table[] = {

submenu_0,

submenu_1,

submenu_2,

submenu_3,

submenu_4,

submenu_5,

submenu-6

};

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

void setup() {

//////////////Start Serial for Debugging/////////////////////

//Serial.begin(9600);

//printing initialisation message

Nothing new here. As we have done before, we store the strings in the program memory and set up a table to retrieve the stored data at a later point. Let's move on to the more interesting part, putting it in the main menu. First we go to the beginning of the selectMenu() function and add a sub button variable.

void selectMenu(){

//Serial.println("SelectMenu");

byte button = 0; //var holding the button value

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

byte subButton = 0; //var holding the subButton value

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

menuOption = 1; //current menu option

lcd.clear(); //clear screen

//print the retrieved string on lcd

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

We stay inside the selectMenu() function but we move down to the end where we add the actual sub menu structure:

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

photoOutsideOff = 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 starts here<<<<<<<<<<<<<

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

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

subButton = 0; //resetting the button var

submenu = 1; //submenu counter

lcd.clear(); //clear screen

//retrieving and printing first sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[0]))));

lcd.print("1"); //printing assigned room number

while(submenu < submenus){ //loop through the sub menu points

subButton = read_act_buttons(); //checking for pressed buttons

if(subButton == btnMenu){ //if button Menu was pressed

submenu++; //add 1 - move to the next sub menu point

if(submenu == 2){ //if we are at sub menu 2

lcd.clear();

//retrieve and print second sub menu point

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

lcd.print("1"); //printing assigned room number

}

if(submenu == 3){ //if we are at sub menu 3

lcd.clear();

//retrieve and print second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[2]))));

lcd.print("1"); //printing assigned room number

}

if(submenu == 4){ //if we are at sub menu 4

lcd.clear();

//retrieve and print second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[3]))));

lcd.print("1"); //printing assigned room number

}

if(submenu == 5){ //if we are at sub menu 5

lcd.clear();

//retrieve and print second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[4]))));

lcd.print("1"); //printing assigned room number

}

if(submenu == 6){ //if we are at sub menu 6

lcd.clear();

//retrieve and print second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[5]))));

lcd.print("1"); //printing assigned room number

}

if(submenu == 7){ //if we are at sub menu 7

lcd.clear();

//retrieve and print second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[6]))));

lcd.print("1"); //printing assigned room number

}

}

}

} //submenu end

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

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

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

}

}

}

What happens here? If we are at the main menu point 7 (room 1) and press select it brings us not as before to the point where we changed the values. Now we just enter another menu with the same structure as the main menu with just different options. We set the sub menu button to 0 and wait again for button input. According to which button is pressed, we move through the sub menu or go to the point where we change a value if the button “select” is pressed.

Let's move on and add the part where we change the values if the button select is pressed. We start again, where we just left off.

if(submenu == 7){ //if we are at sub menu 7

lcd.clear();

//retrieve and print second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(submenu_table[6]))));

lcd.print("1"); //printing assigned room number

}

}

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

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

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

if(subButton == btnSelect){ //if we pressed btnSelect

if(submenu == 1){ //and submenu is 1

//call the function get_delay() to change the setting

delayTime[0] = get_delay("R1 PIR Delay", dBed1);

return;

}

if(submenu == 2){ //and sub menu is 2

//call the function get_offon to change the setting

room1MActive = get_offon("R1 T1 On/Off", room1MActive);

return;

}

if(submenu == 3){ //and submenu is 3

//call the function get_setTime() to change timer 1

get_setTime("R1 T1 On/Off", room1OnM[0], room1OnM[1],

room1OffM[0], room1OffM[1], room1SubMenu);

return;

}

if(submenu == 4){ //and submenu is 4

//call the function get_offon() to change the setting

room101Active = get_offon("R1 T2 On Off", room101Active);

return;

}

if(submenu == 5){ //and submenu is 5

//call the function get_setTime() to change timer 2

get_setTime("R1 T2 On/Off", room1On1[0], room1On1[1],

room1Off1[0], room1Off1[1], room1SubMenu);

return;

}

if(submenu == 6){ //and submenu is 6

//call the function get_offon() to change the setting

room102Active = get_offon("R1 T3 On Off", room102Active);

return;

}

if(submenu == 7){ //and submenu == 7

//call function get_setTime() to change timer 3

get_setTime("R1 T3 On/Off", room1On2[0], room1On2[1],

room1Off2[0], room1Off2[1], room1SubMenu);

return;

}

}

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

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

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

}

} //submenu end

}

}

}

The structure and how it works is the same principal as what I have done in the main menu. To have a working sketch again, we need to have a quick look at 3 new functions to change the values. Since this are values which might be changed more often, we add them straight after the selectMenu() function. The first one to look at is the function get_delay(). Here we pass the changed value back into a array and not in a variable. That's why the variable we declare in the beginning of the sketch is packed into a array and the program is working with the array better then with single variables. Let's got to the end of the selectMenu() function and have a closer look at get_delay().

//function to adjust the pir delay time

int get_delay(char delayText[], int reading){

byte subButton = 0; //resetting the button value

byte value = raeding / 60; //converting to Minutes

lcd.clear(); //clear screen

lcd.print(delayText); //print passed message

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

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

lcd.print(value); //print the passed value in minutes

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

lcd.print("Min"); //just print Min.

while(subButton != btnSelect){ //wait for select btn

subButton = read_act_buttons(); //check if a button was pressed

if(subButton == btnSearch){ //if btnSearch was pressed

if(value > 0 && value < 30){ //we are within allowed range

value++; //add 1 to value while btnSearch is pressed

}

if(value >= 30) value = 1; //if reaches upper limit set to lower limit

lcd.setCursor(0, 1); //setting the cursor

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

lcd.print(value); //printing the updated value

}

}

return value*60;

}

The way we work the adjustment is nothing new. We enter the function with passing a informative message to be printed on LCD and the variable we want to change. In this case we do not need to pass minimum and maximum value, since this function is only be used for the delay timers and a maximum adjustable range of 30 minutes is more than sufficient. If you need a larger range, just change the 30 in the following two statement to whatever maximum range you need.

if(value > 0 && value < 30){ //we are within allowed range

value++; //add 1 to value while btnSearch is pressed

}

if(value >= 30) value = 1; //if reaches upper limit set to lower limit

The statement “byte value = reading / 60; //converting to Minutes” needs the division by 60 to convert the seconds we are working with in the program to minutes, to make it easier to adjust for the user. Also, in the end we need to reconvert to seconds in which the value is stored ( return value*60;).

The next function we have is get_offon(). We can build this one again right at the end of the selectMenu() function.


//function to set a timer active / inactive

byte get_offon(char offonText[], byte reading){

byte subButton = 0; //resetting button value

lcd.clear(); //clear screen

lcd.print(offonText); //print passed info text

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

if(reading != 1) lcd.print("Off "); //if value is not 1 timer is off

//print off

if(reading == 1) lcd.print("Active"); //if timer is 1, timer is active

//print active

while(subButton != btnSelect){ //waiting for btnSelect to be pressed

subButton = read_act_buttons(); //checking for pressed buttons

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

if(reading == 1) reading = 0; //set to 0 if 1

else

if(reading == 0) reading = 1; //set to 1 if 0

}

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

if(reading != 1) lcd.print("Off "); //print off if not set to 1

if(reading == 1) lcd.print("Active"); //print Active if set to 1

}

return reading;

}

The on off function is pretty straight forward and I think everything is pretty clear just going through the comments in the function it self. The only thing which might need a little attention is the addition of 3 blanks after the word “Off”. Not to rebuild the whole screen after putting in a lcd.clear(), we just overwrite Off and Active. Again, the lcd does not replace not used spaces with blanks. If you overwrite Active with Off it will read Offive on the LCD. Here we need the 3 blanks again to get rid of the left overs from Active.
Now the last function for today get_setTime(), which again, we just build at the end of the selectMenu() function:


byte get_setTime(char timeText[], byte onTimeH,

byte onTimeM, byte offTimeH,

byte offTimeM, byte room){

byte subButton = 0;

onTimeH = get_Timer("ADJ Hour On", onTimeH, 0, 23);

if(onTimeH >= 0 && onTimeH < 24){

onTimeM = get_Timer("ADJ Minute On", onTimeM, 0, 59);

if(onTimeM < 60){

offTimeH = get_Timer("ADJ Hour Off", offTimeH, 0, 23);

if(offTimeH >= 0 && offTimeH < 24){

offTimeM = get_Timer("ADJ Minute Off", offTimeM, 0, 59);

if(offTimeM < 60){

lcd.clear();

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

lcd.setCursor(0, 1);

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

lcd.print(onTimeH);

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

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

lcd.print(onTimeM);

lcd.setCursor(11, 1);

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

lcd.print(offTimeH);

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

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

lcd.print(offTimeM);

while(subButton != btnSelect){

subButton = read_act_buttons();

if(subButton == btnMenu) return 0;

if(subButton == btnSelect){

lcd.clear();

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

delay(1000);

if(room == 1){

room1OnM[0] = onTimeH;

room1OnM[1] = onTimeM;

room1OffM[0] = offTimeH;

room1OffM[1] = offTimeM;

}

return 0;

}

}

}

}

}

}

}

The structure reminds us a bit on the function adjust_date_time(), which is build in the same manner. The only difference is that we are not really saving the values, we are just giving them back to their respective places in the originating arrays.

In the next post, we will do a little more house keeping and putting it all together.

No comments:

Post a Comment