Translate

Friday, 30 May 2014

Room Management System – The Menu - part 11


First thing today, we are building the missing function from my last post, adj_seasons(). But before we start doing this, we need to add a few more variables first. As usual we start with the declaration part and this time we jump right down into the “menu and user interface” section. There we add a few lines to the msg_table:

//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.3.7";

prog_char msg_5[] PROGMEM = "Weekday";

prog_char msg_6[] PROGMEM = "On TIMER Off";

prog_char msg_7[] PROGMEM = "Off ";

prog_char msg_8[] PROGMEM = "Active";

prog_char msg_9[] PROGMEM = "PIR Delay R";

prog_char msg_10[] PROGMEM = "T1 On/Off R";

prog_char msg_11[] PROGMEM = "T2 On/Off R";

prog_char msg_12[] PROGMEM = "T3 On/Off R";

prog_char msg_13[] PROGMEM = "ADJ Hour On";

prog_char msg_14[] PROGMEM = "ADJ Minute On";

prog_char msg_15[] PROGMEM = "ADJ Hour Off";

prog_char msg_16[] PROGMEM = "ADJ Minute Off";

prog_char msg_17[] PROGMEM = "Set Sensitivity";

prog_char msg_18[] PROGMEM = "Set photocell R";

prog_char msg_19[] PROGMEM = "Set photocell O";

prog_char msg_20[] PROGMEM = "ADJ Time Minute";

prog_char msg_21[] PROGMEM = "ADJ Time Hour";

prog_char msg_22[] PROGMEM = "ADJ Date Day";

prog_char msg_23[] PROGMEM = "ADJ Date Month";

prog_char msg_24[] PROGMEM = "ADJ Date Year";

prog_char msg_25[] PROGMEM = "T4 On/Off R";

prog_char msg_26[] PROGMEM = "ADJ AC Mode";

prog_char msg_27[] PROGMEM = "AC Set Temp";

prog_char msg_28[] PROGMEM = "AC Switch Delay";

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

prog_char msg_29[] PROGMEM = "AC Seas 1 Start";

prog_char msg_30[] PROGMEM = "AC Seas 1 End";

prog_char msg_31[] PROGMEM = "AC Seas 2 Start";

prog_char msg_32[] PROGMEM = "AC Seas 2 End";

prog_char msg_33[] PROGMEM = "AC Seas 3 Start";

prog_char msg_34[] PROGMEM = "AC Seas 3 End";

prog_char msg_35[] PROGMEM = "AC Seas 4 Start";

prog_char msg_36[] PROGMEM = "AC Seas 4 End";

//>>>>>>>>>>>>>ADDITION ends here<<<<<<<<<<<<<

//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,

msg_6,

msg_7,

msg_8,

msg_9,

msg_10,

msg_11,

msg_12,

msg_13,

msg_14,

msg_15,

msg_16,

msg_17,

msg_18,

msg_19,

msg_20,

msg_21,

msg_22,

msg_23,

msg_24,

msg_25,

msg_26,

msg_27,

msg_28, //<<<<<<<<<<<<<Don't forget to add ','

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

msg_29,

msg_30,

msg_31,

msg_32,

msg_33,

msg_34,

msg_35,

msg_36

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

};

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

While I was writing the Ac-menu part, my little mix up with numbers caught me again. We all now arrays start counting with a 0 and having 20 spaces in an array the last count is 19. Going in to a counting routine, giving a min count of 0 and a max count of 20, it always will go through the 20 spaces in an array from 0 to 19. But using the same routine and counting through 20 event, starting with 1 and giving it min count of 1 and a max count of 20, we always will miss the last event. Doesn't make sense?
OK, lets look at the get_Timer function get_Timer(varForInfoText, actValue, minCount, maxCount).
Lets forget about the first 2 variables we are passing and concentrate on the minCount and maxCount.
The routine dealing with this values is a if-statement:
if(reading >= startVal && reading < maxCount) reading++;

Lets get it apart. If the variable reading is equal to or bigger than the var startVal (minCount) and the variable reading is smaller than maxCount we add 1 to the variable reading.
Back to our array example. If I pass the numbers stepping through an array with a max count of 20 and a min value of 0 because arrays start counting at 0, I always get my 20 values done since the last counter in the array is 19, matches with smaller than 20 (maxCount).
If I need to count through other values like days of a month, the first one is 1 and the last one is 31 and I pass this in to my function minCount 1 and maxCount 31, with the same routine my 31 day will never show since the we are stating “and the variable reading is smaller than maxCount” (&& reading < maxCount). If we want the same routine to step through 31 days we need to pass as maxCount 32! The same goes for month. To step through 12 month, we need to pass as max count 13 cause of “&& reading < maxCount”. Now it make sense I hope. Since we need to change a couple of variables in the get_ac_setup() function and that is also the reason why we are passing on a 13 as max count while going through month etc.

Lets jump into the get_ac_setup() function

void get_ac_setup(){

byte subButton = 0;

acSetup = 1; //submenu counter (AC Mode)

lcd.clear(); //clear screen

//retrieving and printing first sub menu point

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

while(acSetup < acSetups){ //loop through the submenu points

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

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

acSetup++; //add 1 - move to the next point

if(acSetup == 2){ //if we are at submenu 2 (Set Temp)

lcd.clear(); //clear screen

//retrieving and printing second sub menu point

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

}

if(acSetup == 3){ //if we are at submenu 3 (Seasons)

lcd.clear();

//retrieving and printing the third sub menu point

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

}

if(acSetup == 4){ //if we are at submenu 4 (Switch delay)

lcd.clear(); //clear screen

//retrieving and printing sub menu point 4

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

}

}

if(subButton == btnSelect){ //if btn select was pressed

if(acSetup == 1){ //and sub menu is 1

//call function get_Timer

ac_op_mode = get_Timer(26, ac_op_mode, 1, 3); //<<<<<<<<<<<<<CHANGE to get_Timer(26, ac_op_mode, 1, 4)

return;

}

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

//call function get_Timer

ac_set_temp = get_Timer(27, ac_set_temp, 18, 32);

return;

}

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

//call function adj_Seasons

adj_seasons();

return;

}

if(acSetup == 4){

//call function get_Timer

acSwitchDelay = get_Timer(28, acSwitchDelay, 1, 5);

return;

}

}

}

}

OK, now we can go through the 3 available operating modes. Next task today is building the function to adjust the seasonal boundaries.

void adj_seasons(){

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

ac_periode[0][0] = get_Timer(29, ac_periode[0][0], 0, 13); //get the first value

if(ac_periode[0][0] >= 0 && ac_periode[0][0] <= 12){ //check if it is within range

if(ac_periode[0][0] == 12){ //predefine closing value

ac_periode[0][1] = 1;

ac_periode[0][1] = get_Timer(30, ac_periode[0][1], 1, 13);

}

else if(ac_periode[0][0] != 12){

ac_periode[0][1] = ac_periode[0][0] + 1;

ac_periode[0][1] = get_Timer(30, ac_periode[0][1], ac_periode[0][1], 13);

}

if(ac_periode[0][1] >= 0 && ac_periode[0][1] <= 12){

ac_periode[1][0] = get_Timer(31, ac_periode[1][0], 0, 13);

if(ac_periode[1][0] >= 0 && ac_periode[1][0] <= 12){

if(ac_periode[1][0] == 12){

ac_periode[1][1] = 1;

ac_periode[1][1] = get_Timer(32, ac_periode[1][1], 1, 13);

}

else if(ac_periode[1][0] != 12){

ac_periode[1][1] = ac_periode[1][0] + 1;

ac_periode[1][1] = get_Timer(32, ac_periode[1][1], ac_periode[0][1], 13);

}

if(ac_periode[1][1] >= 0 && ac_periode[1][1] <= 12){

ac_periode[2][0] = get_Timer(33, ac_periode[2][0], 0, 13);

if(ac_periode[2][0] >= 0 && ac_periode[2][0] <= 12){

if(ac_periode[2][0] == 12){

ac_periode[2][1] = 1;

ac_periode[2][1] = get_Timer(34, ac_periode[2][1], 1, 13);

}

else if(ac_periode[2][0] != 12){

ac_periode[2][1] = ac_periode[2][0] + 1;

ac_periode[2][1] = get_Timer(34, ac_periode[2][1], ac_periode[2][1], 13);

}

if(ac_periode[2][1] >= 0 && ac_periode[2][1] <= 12){

ac_periode[3][0] = get_Timer(35, ac_periode[3][0], 0, 13);

if(ac_periode[3][0] >= 0 && ac_periode[3][0] <= 12){

if(ac_periode[3][0] == 12){

ac_periode[3][1] = 1;

ac_periode[3][1] = get_Timer(36, ac_periode[3][1], 1, 13);

}

else if(ac_periode[3][0] != 12){

ac_periode[3][1] = ac_periode[2][0] + 1;

ac_periode[3][1] = get_Timer(36, ac_periode[3][1], ac_periode[3][1], 13);

}

lcd.clear();

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

lcd.print(ac_periode[0][0]);

lcd.print(" | ");

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

lcd.print(ac_periode[0][1]);

lcd.print(" ");

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

lcd.print(ac_periode[1][0]);

lcd.print(" | ");

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

lcd.print(ac_periode[1][1]);

lcd.setCursor(0, 1);

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

lcd.print(ac_periode[2][0]);

lcd.print(" | ");

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

lcd.print(ac_periode[2][1]);

lcd.print(" ");

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

lcd.print(ac_periode[3][0]);

lcd.print(" | ");

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

lcd.print(ac_periode[3][1]);

while(subButton != btnSelect){

lcd.clear();

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

delay(1000);

//save the values

return;

}

}

}

}

}

}

}

}

}

The basic structure is also nothing new. We have used the same structure in the date and time adjustment and in the timer adjustments. The only difference is that we add a limitation. If we start a season at February (2) the count for the end of that season will only allow to go through the remaining month of the year March to December.

Let's carry on with the AC submenu and jump back up into the declaration part of the sketch. There we have to add a couple of variables and store the ac submenu points in the program memory. We go right into the “menu and user interface” section:

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

///////////////////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 = 21; //available menu options

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

const byte submenus = 10; //available submenu options

byte acSetup = 0; //var to count current ac setup menu options

const byte acSetups = 4; //available ac setup menu options

//>>>>>>>>>>>>>ADDITION starts here<<<<<<<<<<<<<<<

byte acSubOption = 0; //var to count current ac sub menu options

const byte acSubOptions = 10; //available ac sub menu options

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

from here we go down to the msg_table[] and add a couple of lines:

//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.3.8";

prog_char msg_5[] PROGMEM = "Weekday";

prog_char msg_6[] PROGMEM = "On TIMER Off";

prog_char msg_7[] PROGMEM = "Off ";

prog_char msg_8[] PROGMEM = "Active";

prog_char msg_9[] PROGMEM = "PIR Delay R";

prog_char msg_10[] PROGMEM = "T1 On/Off R";

prog_char msg_11[] PROGMEM = "T2 On/Off R";

prog_char msg_12[] PROGMEM = "T3 On/Off R";

prog_char msg_13[] PROGMEM = "ADJ Hour On";

prog_char msg_14[] PROGMEM = "ADJ Minute On";

prog_char msg_15[] PROGMEM = "ADJ Hour Off";

prog_char msg_16[] PROGMEM = "ADJ Minute Off";

prog_char msg_17[] PROGMEM = "Set Sensitivity";

prog_char msg_18[] PROGMEM = "Set photocell R";

prog_char msg_19[] PROGMEM = "Set photocell O";

prog_char msg_20[] PROGMEM = "ADJ Time Minute";

prog_char msg_21[] PROGMEM = "ADJ Time Hour";

prog_char msg_22[] PROGMEM = "ADJ Date Day";

prog_char msg_23[] PROGMEM = "ADJ Date Month";

prog_char msg_24[] PROGMEM = "ADJ Date Year";

prog_char msg_25[] PROGMEM = "T4 On/Off R";

prog_char msg_26[] PROGMEM = "ADJ AC Mode";

prog_char msg_27[] PROGMEM = "AC Set Temp";

prog_char msg_28[] PROGMEM = "AC Switch Delay";

prog_char msg_29[] PROGMEM = "AC Seas 1 Start";

prog_char msg_30[] PROGMEM = "AC Seas 1 End";

prog_char msg_31[] PROGMEM = "AC Seas 2 Start";

prog_char msg_32[] PROGMEM = "AC Seas 2 End";

prog_char msg_33[] PROGMEM = "AC Seas 3 Start";

prog_char msg_34[] PROGMEM = "AC Seas 3 End";

prog_char msg_35[] PROGMEM = "AC Seas 4 Start";

prog_char msg_36[] PROGMEM = "AC Seas 4 End";

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

prog_char msg_37[] PROGMEM = "AC Off delay";

prog_char msg_38[] PROGMEM = "AC master byp";

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

//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,

msg_6,

msg_7,

msg_8,

msg_9,

msg_10,

msg_11,

msg_12,

msg_13,

msg_14,

msg_15,

msg_16,

msg_17,

msg_18,

msg_19,

msg_20,

msg_21,

msg_22,

msg_23,

msg_24,

msg_25,

msg_26,

msg_27,

msg_28,

msg_29,

msg_30,

msg_31,

msg_32,

msg_33,

msg_34,

msg_35,

msg_36, //<<<<<<<<<<<<<Don't forget to add the ','

//>>>>>>>>>>>>>>ADDITION starts here<<<<<<<<<<<<<<

msg_37,

msg_38

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

};

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

Now we move down a little and just after the ac_setup_table we start building the ac_sub_table:

//storing the AC sub menu

prog_char ac_sub_0[] PROGMEM = "AC off delay R";

prog_char ac_sub_1[] PROGMEM = "AC Master byp R";

prog_char ac_sub_2[] PROGMEM = "AC ST1 Active R";

prog_char ac_sub_3[] PROGMEM = "AC Timer 1 R";

prog_char ac_sub_4[] PROGMEM = "AC ST2 Active R";

prog_char ac_sub_5[] PROGMEM = "AC Timer 2 R";

prog_char ac_sub_6[] PROGMEM = "AC ST3 Active R";

prog_char ac_sub_7[] PROGMEM = "AC Timer 3 R";

prog_char ac_sub_8[] PROGMEM = "AC ST4 Active R";

prog_char ac_sub_9[] PROGMEM = "AC Timer 4 R";

PROGMEM const char *ac_sub_table[] = {

ac_sub_0,

ac_sub_1,

ac_sub_2,

ac_sub_3,

ac_sub_4,

ac_sub_5,

ac_sub_6,

ac_sub_7,

ac_sub_8,

ac_sub_9

};

So far so good, a big jump now, to the end of the selectMenu() function where we have to add also a few statements:

if(menuOption == 16){ //and menu option is 16 (room 10) 

  get_submenu(9); 

}//submenu end 

if(menuOption == 17){ //and menu option is 17 (AC setup) 

   get_ac_setup(); 

  //submenu end 

} 

 //>>>>>>>>>>>>>ADDITION starts here<<<<<<<<<<<<<<

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

  get_ac_sub(10);

  //submenu end 

} 

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

   get_ac_sub(11); 

  //submenu end 

} 

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

   get_ac_sub(12); 

  //submenu end 

}

if(menuOption == 21){ //and menu option is 21 (AC room 4) 

  get_ac_sub(13); 

  //submenu end

} 

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

 

 void get_submenu(byte room){

From here we move down to the end of the get_ac_setup() function where we build the new get_ac_sub() function


void get_ac_sub(byte room){

byte subButton = 0; //resetting the button var

acSubOption = 1; //submenu counter

lcd.clear(); //clear screen

//retrieving and printing first submenu point

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

lcd.print(room - 9); //printing assigned room number

while(acSubOption < acSubOptions){ //loop through the submenu points

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

if(subButton == btnMenu){ //if btn menu was pressed

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

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

lcd.clear();

//retrieving and printing second sub menu point

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

lcd.print(room - 9); //printing assigned room number

}

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

lcd.clear();

//retrieving and printing second sub menu point

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

lcd.print(room - 9); //printing assigned room number

}

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

lcd.clear();

//retrieving and printing second sub menu point

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

lcd.print(room - 9); //printing assigned room number

}

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

lcd.clear();

//retrieving and printing second sub menu point

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

lcd.print(room - 9); //printing assigned room number

}

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

lcd.clear();

//retrieving and printing second sub menu point

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

lcd.print(room - 9); //printing assigned room number

}

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

lcd.clear();

//retrieving and printing second sub menu point

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

lcd.print(room - 9); //printing assigned room number

}

if(acSubOption == 8){ //if we are at sub menu 8

lcd.clear();

//retrieving and printing second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_sub_table[7]))));

lcd.print(room - 9); //printing assigned room number

}

if(acSubOption == 9){ //if we are at sub menu 9

lcd.clear();

//retrieving and printing second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_sub_table[8]))));

lcd.print(room - 9); //printing assigned room number

}

if(acSubOption == 10){ //if we are at sub menu 10

lcd.clear();

//retrieving and printing second sub menu point

lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_sub_table[9]))));

lcd.print(room - 9); //printing assigned room number

}

}

if(subButton == btnSelect){ //if btn select was pressed

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

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

delayTime[room] = get_delay(37, room, delayTime[room]);

return;

}

if(acSubOption == 2){ //and sub menu point is 2

//call the function get_offon to change setting

ac_master_bypass[room - 10] = get_offon(38, room, ac_master_bypass[room - 10]);

return;

}

if(acSubOption == 3){ //and sub menu point is 3

//call the function get_offon to change setting

timer_active[room][0] = get_offon(10, room, timer_active[room][0]);

return;

}

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

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

get_setTime(room_timers[room][0][0], room_timers[room][0][1],

room_timers[room][0][2], room_timers[room][0][3],

room, 0);

return;

}

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

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

timer_active[room][1] = get_offon(11, room, timer_active[room][1]);

return;

}

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

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

get_setTime(room_timers[room][1][0], room_timers[room][1][1],

room_timers[room][1][2], room_timers[room][1][3],

room, 1);

return;

}

if(submenu == 7 && timer_active[room][2] != 2){ //and submenu is 7

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

timer_active[room][2] = get_offon(12, room, timer_active[room][2]);

return;

}

if(submenu == 8 && timer_active[room][2] != 2){ //and submenu == 8

//call function get_setTime() to change timer 3

get_setTime(room_timers[room][2][0], room_timers[room][2][1],

room_timers[room][2][2], room_timers[room][2][3],

room, 2);

return;

}

if(submenu == 9 && timer_active[room][3] != 2){ //and submenu is 9

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

timer_active[room][3] = get_offon(25, room, timer_active[room][3]);

return;

}

if(submenu == 10 && timer_active[room][3] != 2){ //and submenu == 10

//call function get_setTime() to change timer 3

get_setTime(room_timers[room][3][0], room_timers[room][3][1],

room_timers[room][3][2], room_timers[room][3][3],

room, 3);

return;

}

}

}

}


Tuesday, 27 May 2014

Room Management System – The Menu - part 10


Today we start adding our revised AC control into the menu. We can use most of the menu items, we have already build. One small submenu I still had to add. Let's start as usual in the declaration part of our sketch and jump right into the “Menu and user interface section”.


///////////////////////////////////////////////////////////////////////////
///////////////////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 

//<<<<<<<<<<<<menuOptions = 20; changes to menuOptions = 21;<<<<<<<<<<<<<
 
const byte menuOptions = 21;     //available menu options
byte submenu = 0;                //var to count current submenu option
const byte submenus = 10;        //available submenu options
byte acSetup = 0;                //var to count current ac setup menu options
const byte acSetups = 4;          //available ac setup menu options


Now we move down a bit to the declaration of the msg table and add a few entries:


//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.3.7";
prog_char msg_5[] PROGMEM = "Weekday";
prog_char msg_6[] PROGMEM = "On  TIMER  Off";
prog_char msg_7[] PROGMEM = "Off   ";
prog_char msg_8[] PROGMEM = "Active";
prog_char msg_9[] PROGMEM = "PIR Delay R";
prog_char msg_10[] PROGMEM = "T1 On/Off R";
prog_char msg_11[] PROGMEM = "T2 On/Off R";
prog_char msg_12[] PROGMEM = "T3 On/Off R";
prog_char msg_13[] PROGMEM = "ADJ Hour On";
prog_char msg_14[] PROGMEM = "ADJ Minute On";
prog_char msg_15[] PROGMEM = "ADJ Hour Off";
prog_char msg_16[] PROGMEM = "ADJ Minute Off";
prog_char msg_17[] PROGMEM = "Set Sensitivity";
prog_char msg_18[] PROGMEM = "Set photocell R";
prog_char msg_19[] PROGMEM = "Set photocell O";
prog_char msg_20[] PROGMEM = "ADJ Time Minute";
prog_char msg_21[] PROGMEM = "ADJ Time Hour";
prog_char msg_22[] PROGMEM = "ADJ Date Day";
prog_char msg_23[] PROGMEM = "ADJ Date Month";
prog_char msg_24[] PROGMEM = "ADJ Date Year";
prog_char msg_25[] PROGMEM = "T4 On/Off R";

//>>>>>>>>>>>>>Addition starts here<<<<<<<<<<<<<
 
prog_char msg_26[] PROGMEM = "ADJ AC Mode";
prog_char msg_27[] PROGMEM = "AC Set Temp";
prog_char msg_28[] PROGMEM = "AC Switch Delay";

//>>>>>>>>>>>>>Addition ends here<<<<<<<<<<<<
//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,
  msg_6,
  msg_7,
  msg_8,
  msg_9,
  msg_10,
  msg_11,
  msg_12,
  msg_13,
  msg_14,
  msg_15,
  msg_16,
  msg_17,
  msg_18,
  msg_19,
  msg_20,
  msg_21,
  msg_22,
  msg_23,
  msg_24,
  msg_25,  //<<<<<<<<<<<<Don't forget to add ','
 
//>>>>>>>>>>>>>>Addition starts here<<<<<<<<<<<<<
  msg_26,
  msg_27,
  msg_28

//>>>>>>>>>>>>>Addition ends here<<<<<<<<<<<<<
};


From here we go down to the menu table and add:


prog_char menu_16[] PROGMEM = "AC Setup";

right after

prog_char menu_15[] PROGMEM = "Room 10";

Please NOTE, you have now two declarations of “prog_char menu_16[] “

On everything after the added line, you have to update the numbers. What was 16 becomes 17, what was 17 becomes 18 ….
And since the declarations now end with 20 and not with 19 any more, we have to add it also to the table.

The updated menu table looks now like this:

//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 Setup";
prog_char menu_17[] PROGMEM = "AC 1";
prog_char menu_18[] PROGMEM = "AC 2";
prog_char menu_19[] PROGMEM = "AC 3";
prog_char menu_20[] 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,
  menu_20
};

The submenu table is a little easier again. There we just add a line at the end:

//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";
prog_char submenu_7[] PROGMEM = "HT4 Active R";
prog_char submenu_8[] PROGMEM = "Timer 4 R";
//>>>>>>>>>>>>>ADD the line below<<<<<<<<<<<<< 
prog_char submenu_9[] PROGMEM = "Off Delay R";

PROGMEM const char *submenu_table[] = {
  submenu_0,
  submenu_1,
  submenu_2,
  submenu_3,
  submenu_4,
  submenu_5,
  submenu_6,
  submenu_7,
  submenu_8,  //<<<<<<<<<<<<<Don't forget to add the ','
//>>>>>>>>>>>>>ADD the line below<<<<<<<<<<<<< 
  submenu_9
};

Right after we have done this, we add another table for the General AC setup:

prog_char ac_setup_0[] PROGMEM = "AC Mode";
prog_char ac_setup_1[] PROGMEM = "Set Temp";
prog_char ac_setup_2[] PROGMEM = "Seasons";
prog_char ac_setup_3[] PROGMEM = "Switch Delay";

PROGMEM const char *ac_setup_table[] = {
  ac_setup_0,
  ac_setup_1,
  ac_setup_2,
  ac_setup_3
};

from here we jump down into the selectMenu() function. Check carefully the addition at the end of the first part of the function.

void selectMenu(){
  //Serial.println("SelectMenu");
  byte button = 0;                    //var holding the button value
  byte subButton = 0;                 //var holding the subButton value
  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]))));
  
  while(menuOption <= menuOptions){   //loop through menu options
    button = read_act_buttons();      //check if button was pressed
    if(button == btnMenu){            //if it was btn menu
      menuOption++;                   //add 1 to menu option
      if(menuOption == 2){             //if it's menu option 2
        lcd.clear();                   //clear display
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[1]))));
      }
      if(menuOption == 3){             //if it's menu option 3
        lcd.clear();                   //clear display
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[2]))));
      }
      if(menuOption == 4){            //if it's menu option 4
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[3]))));
        lcd.setCursor(0, 1);          //set cursor to row 2 column 1
        //print tetrieved message sdting
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(msg_table[0]))));
      }
      if(menuOption == 5){            //if it's menu option 5
        lcd.clear();                  //clear display
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[4]))));
      }
      if(menuOption == 6){            //if it's menu option 6
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[5]))));
        lcd.setCursor(0, 1);          //set cursor to row 2 column 1
        //print retrieved message string
        lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(msg_table[0]))));
      }
      if(menuOption == 7){            //if it's menu option 7
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[6]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 8){            //if it's menu option 8
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[7]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 9){            //if it's menu option 9
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[8]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 10){            //if it's menu option 10
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[9]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 11){            //if it's menu option 12
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[10]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 12){            //if it's menu option 13
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[11]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message sdting
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 13){            //if it's menu option 14
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[12]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 14){            //if it's menu option 15
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[13]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 15){            //if it's menu option 16
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[14]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print retrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 16){            //if it's menu option 17
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[15]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 17){            //if it's menu option 18
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[16]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print retrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 18){            //if it's menu option 19
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[17]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message sdting
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      if(menuOption == 19){            //if it's menu option 20
        lcd.clear();                  //clear lcd
        //print retrieved string on lcd
        lcd.print(strcpy_P(buffer_M, (char*)pgm_read_word(&(menu_table[18]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print tetrieved message sdting
        lcd.write(pgm_read_byte(&char_table[0]));
      }
      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(menuOption == 21){            //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[20]))));
        lcd.setCursor(15, 0);          //set cursor to row 2 column 1
        //print retrieved message string
        lcd.write(pgm_read_byte(&char_table[0]));
      }
 //>>>>>>>>>>>>>>ADDITION ENDS HERE<<<<<<<<<<<<<<< 
    }
    
    if(button == btnSelect){ //if the select button is pressed
      if(menuOption == 1){   //amd menu option is 1
        adjust_date_time();  //go to adust date and time
        return;
      }
      if(menuOption == 2){   //and menu option is 2
        sensitivity = get_Timer(17, sensitivity, 0, 1000); //go to function 
        return;

From here we go down to the end of the selectMenu() function and add:

      if(menuOption == 13){ //and menu option is 13 (room 7)
        get_submenu(6);
      } //submenu end
      if(menuOption == 14){ //and menu option is 14 (room 8)
        get_submenu(7);
      } //submenu end
      if(menuOption == 15){ //and menu option is 15 (room 9)
        get_submenu(8);
      } //submenu end
      if(menuOption == 16){ //and menu option is 16 (room 10)
        get_submenu(9);
      } //submenu end 
//>>>>>>>>>>>>>ADDITION STARTS HERE<<<<<<<<<<<<< 
      if(menuOption == 17){ //and menu option is 17 (AC setup)
        get_ac_setup();
        //submenu end
      }
//>>>>>>>>>>>>>ADDITION ENDS HERE<<<<<<<<<<<<<< 
    }   
  }
}

void get_submenu(byte room){
  byte subButton = 0;        //resetting the button var
  submenu = 1;       //submenu counter
  lcd.clear();       //clear screen

Everything as we have already done before. And if you did pay attention again, you now already that I was cheeky and sneaked another function in. Since we run that piece of code only once, I could have put it right into the selectMenu() function but since it is so nice structured, I decided to put that extra submenu also into a function, get_ac_setup. The menu structure is the same as in the other sub menu and three out of the four menu points are using the get_Timer function to adjust the values. Only to set up the seasonal boundaries we need to add another function but that's done in the next post. As to days final the get_ac_setup() function.

void get_ac_setup(){
  byte subButton = 0;
  acSetup = 1;                     //submenu counter (AC Mode)
  lcd.clear();                       //clear screen
  //retrieving and printing first sub menu point
  lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_setup_table[0]))));
  while(acSetup < acSetups){       //loop through the submenu points
    subButton = read_act_buttons();  //checking for pressed buttons
    if(subButton == btnMenu){        //if button menu was pressed
      acSetup++;                     //add 1 -  move to the next point
      if(acSetup == 2){              //if we are at submenu 2 (Set Temp)
        lcd.clear();                 //clear screen
        //retrieving and printing second sub menu point
        lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_setup_table[1]))));
      }
      if(acSetup == 3){              //if we are at submenu 3 (Seasons)
        lcd.clear();
        //retrieving and printing the third sub menu point
        lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_setup_table[2]))));
      }
      if(acSetup == 4){             //if we are at submenu 4 (Switch delay)
        lcd.clear();                  //clear screen
        //retrieving and printing sub menu point 4
        lcd.print(strcpy_P(buffer, (char*)pgm_read_word(&(ac_setup_table[3]))));
      }
    }
    if(subButton == btnSelect){      //if btn select was pressed
      if(acSetup == 1){              //and sub menu is 1
        //call function get_Timer
        ac_op_mode = get_Timer(26, ac_op_mode, 1, 3);
        return;
      }
      if(acSetup == 2){              //and sub menu is 2
        //call function get_Timer
        ac_set_temp = get_Timer(27, ac_set_temp, 18, 32);
        return;
      }
      if(acSetup == 3){              //and submenu is 3
        //call function adj_Seasons
        //still being worked on
        return;
      }
      if(acSetup == 4){
        //call function get_Timer
        acSwitchDelay = get_Timer(28, acSwitchDelay, 1, 5);
        return;
      }
    }  
  }
}