#include <Wire.h>
#include <LiquidCrystal_I2C.h>
/////Rotary Encoder Pin Assignment/////
#define rotaryPin_A 2
#define rotaryPin_B 3
#define rotaryButtonPin 4
#define inputTimeOut 10000 //time out for human input session, milliseconds
unsigned int maxMenuItems; //number of menu items
unsigned char encoder_A = 0;
unsigned char encoder_B = 0;
unsigned char encoder_A_prev = 0;
unsigned char encoder_C = 1; //encoder button
unsigned char encoder_C_prev = 1;
unsigned long currentTime;
unsigned long loopTime;
/////End of Rotary Encoder Declarations/////
/////Global time variable, Hour : Minute Settings
unsigned int setHour = 0;
unsigned int setMinute = 0;
////////
//////Menu declarations///////
unsigned int menuPos = 0; //current menu position
unsigned int lastMenuPos = 0; //previous menu position
unsigned int parentMenuPos = 0; //parent menu position
bool humanInputActive = false; //flag to indicate if input session is active
unsigned subMenuActive = 0; //flag to indicate a sub selection menu session is active: 0 - main menu; 1 - number selection 1- 255; 2 - binary selection yes/no; 3 - time setting;
unsigned int subMenuPos = 0; //sub menu rotary position
unsigned int subMenuClick = 0; //sub menu click counter
//unsigned long inputTimeOut = 10000; //time out for human input session, milliseconds
unsigned long lastInputTime = millis(); //keep track of time of last human input
typedef struct menu_type
{
menu_type()
: code(0), text("")
{
//do nothing
}
unsigned int code; //code that indicate address (position) in the menu tree
String text; //text to be displayed for the menu selection
} menu_type;
menu_type menu[100] = {}; //initilizing menu array, use a number >= than the number of menu options
/////End of Menu declarations////
LiquidCrystal_I2C lcd(0x27,20,4);
void setup() {
Serial.begin(9600);
rotaryEncoderInit();
menuInit();
lcd.init();
lcd.backlight();
}
void loop() {
// put your main code here, to run repeatedly:
rotaryEncoderUpdate();
lcdBackLight();
}
void rotaryEncoderInit() {
//Rotary encoder initialization
pinMode(rotaryPin_A, INPUT_PULLUP);
pinMode(rotaryPin_B, INPUT_PULLUP);
pinMode(rotaryButtonPin, INPUT_PULLUP);
currentTime = millis();
loopTime = currentTime;
}
void rotaryEncoderUpdate() {
//rotary encoder update, to be called from main loop()
currentTime = millis();
if (currentTime >= (loopTime + 1) ) {
encoder_A = digitalRead(rotaryPin_A);
encoder_B = digitalRead(rotaryPin_B);
encoder_C = digitalRead(rotaryButtonPin);
//handling knob rotation
if ( (encoder_A == 0) && (encoder_A_prev == 1) ) { //encoder changed position
lastInputTime = millis();
if (encoder_B == 1) { //B is high, so encoder moved clockwise
//Serial.println("encoder rotated CW");
switch (subMenuActive) { //send encoder action to appropriate menu handler
case 0: //main menu
menuUpdate(2);
break;
case 1: //subMenu1
subMenu1Update(2);
break;
case 2: //subMenu2
subMenu2Update(2);
break;
case 3:
subMenu3Update(2);
break;
default:
menuUpdate(2);
break;
}
} else { //else, encoder moved counter-clockwise
//Serial.println("encoder rotated CCW");
switch (subMenuActive) { //call the appropriate menuupdater depending on which sub menu is active
case 0:
menuUpdate(3);
break;
case 1:
subMenu1Update(3);
break;
case 2:
subMenu2Update(3);
break;
case 3:
subMenu3Update(3);
break;
default:
menuUpdate(3);
break;
}
}
}
//handling push button
if ( (encoder_C == 0) && (encoder_C_prev == 1) ) { //button pushed
//Serial.println("encoder button closed.");
lastInputTime = millis();
switch (subMenuActive) { //call the appropriate menuupdater depending on which sub menu is active
case 0:
menuUpdate(1);
break;
case 1:
subMenu1Update(1);
break;
case 2:
subMenu2Update(1);
break;
case 3:
subMenu3Update(1);
break;
default:
menuUpdate(1);
break;
}
} else if ( (encoder_C == 1) && (encoder_C_prev == 0) ) { //button
//Serial.println("encoder button opened.");
}
encoder_A_prev = encoder_A;
encoder_C_prev = encoder_C;
loopTime = currentTime;
//input time out
if ( (millis() - lastInputTime) > inputTimeOut ) {
humanInputActive = false;
menuPos = 0;
lastMenuPos = 0;
menuUpdate(0);
subMenuActive = 0;
} else {
humanInputActive = true;
}
}
}
unsigned int menuVerifyPos(unsigned int menuCode) {
//accepts a code that represents position in the menu
//checks against the menu, verify it exist, and returns it
//if the menu code given does not exist,
//returns the closest code smaller than the one given
bool confirmCode = false; //flag to keep track of whether code has been confirmed in menu tree
for (unsigned int k = 0; k <= (maxMenuItems - 1); k++) {
if (menuCode == menu[k].code) { //found exact code, returns it
menuPos = menu[k].code;
confirmCode = true;
lastMenuPos = menuCode;
return menuPos;
}
}
if (confirmCode == false) {
menuPos = lastMenuPos;
return menuPos; //cannot find menu option, go back to previous menu option
}
}
void updateMenuDisplay(unsigned int menuCode) {
//prints menu selection to the LCD display
//in order to have a scrolling menu effect, this code looks at item before and after current menu item and display them in a row
String curMenu;
String curMenu2;
String prevMenu;
String nextMenu;
curMenu = findMenuTextFromCode(menuCode);
prevMenu = findMenuTextFromCode(menuCode - 1);
nextMenu = findMenuTextFromCode((menuCode + 1));
//if string is 20 or more characters long (for 20 char wide LCD display)
if( prevMenu.length() >= 20) {
prevMenu.remove(19); //remove anything after the 19th character from string
}
if(nextMenu.length() >= 20) {
nextMenu.remove(19); //remove anything after the 19th character from string
}
//starts printing to LCD
lcd.clear();
lcd.setCursor(1,0); //char index, row index, on LCD
lcd.print(prevMenu);
lcd.setCursor(0,1);
lcd.print(">");
//current menu option text is longer than 20 characters (on a 20 char wide LCD)
if(curMenu.length() >= 20) {
curMenu2 = curMenu;
curMenu.remove(19); //remove char from index 19 (20th char)
curMenu2.remove(0,19); //remove the first 19 char starting from index 0
lcd.setCursor(1,1); //print first 19 char of current menu text
lcd.print(curMenu);
lcd.setCursor(0,2);
lcd.print("-");
lcd.setCursor(1,2);
lcd.print(curMenu2); //print remainig char of current menu text
lcd.setCursor(1,3); //print the next menu text in the 4th row
lcd.print(nextMenu);
} else {
lcd.print(curMenu); //print the next menu text in the 3rd row
lcd.setCursor(1,2);
lcd.print(nextMenu);
}
}
String findMenuTextFromCode(unsigned int menuCode) {
//accepts a code representing the code in menu, and returns the corresponding text
for (unsigned int j = 0; j <= (maxMenuItems - 1); j++ ) {
if (menuCode == menu[j].code) {
return menu[j].text;
break;
}
}
}
void lcdBackLight(){
//human inout timeout to turn off lcd back light
if (humanInputActive == true) {
lcd.backlight();
} else {
lcd.noBacklight();
}
}
void menuInit() {
//menu text display
unsigned int i = 0;
menu[i].code = 0;
menu[i++].text = "EXIT MENU";
menu[i].code = 1;
menu[i++].text = "SET CURRENT TIME";
menu[i].code = 11;
menu[i++].text = "CURRENT TIME:";
menu[i].code = 2;
menu[i++].text = "SET OPERATING MODE";
menu[i].code = 20;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 21;
menu[i++].text = "CONTINUOUS MODE";
menu[i].code = 211;
menu[i++].text = "ACTIVATE";
menu[i].code = 22;
menu[i++].text = "DAILY MODE";
menu[i].code = 220;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 221;
menu[i++].text = "ACTIVATE";
menu[i].code = 222;
menu[i++].text = "TARGET VOLUME (L)";
menu[i].code = 2221;
menu[i++].text = "SET LITRES PER DAY:";
menu[i].code = 223;
menu[i++].text = "SCHEDULE TIME";
menu[i].code = 2231;
menu[i++].text = "START DAILY WATER CHANGE AT:";
menu[i].code = 23;
menu[i++].text = "TOP-OFF ONLY MODE";
menu[i].code = 230;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 231;
menu[i++].text = "ACTIVATE";
menu[i].code = 24;
menu[i++].text = "DAILY TIMER MODE";
menu[i].code = 240;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 241;
menu[i++].text = "ACTIVATE";
menu[i].code = 242;
menu[i++].text = "DRAIN DURATION";
menu[i].code = 2421;
menu[i++].text = "DRAIN FOR THIS MANY MINUTES:";
menu[i].code = 243;
menu[i++].text = "SCHEDULE TIME";
menu[i].code = 2431;
menu[i++].text = "START DAILY WATER CHANGE AT:";
menu[i].code = 3;
menu[i++].text = "FLOW METER";
menu[i].code = 30;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 31;
menu[i++].text = "CALIBRATION FACTOR";
menu[i].code = 4;
menu[i++].text = "LOW PRESSURE SWITCH";
menu[i].code = 40;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 41;
menu[i++].text = "ENABLE/DISABLE LOW PRESSURE SWITCH";
menu[i].code = 411;
menu[i++].text = "DELAY FILLING IF PRESSURE IS LOW?";
menu[i].code = 42;
menu[i++].text = "DELAY DURATION";
menu[i].code = 421;
menu[i++].text = "DELAY DURATION IN MINUTES:";
menu[i].code = 5;
menu[i++].text = "DRAIN PUMP/VALVE";
menu[i].code = 50;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 51;
menu[i++].text = "DRAIN PUMP SPEED";
menu[i].code = 511;
menu[i++].text = "SET DRAIN PUMP POWER (DEFAULT 255):";
menu[i].code = 6;
menu[i++].text = "EMERGENCY SHUTDOWN";
menu[i].code = 60;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 61;
menu[i++].text = "DRAIN DURATION";
menu[i].code = 611;
menu[i++].text = "EMERGENCY DRAIN DURATION (MINUTES):";
menu[i].code = 62;
menu[i++].text = "ALLOW AUTO RESET";
menu[i].code = 621;
menu[i++].text = "ENABLE AUTO RESET?";
menu[i].code = 63;
menu[i++].text = "AUTO RESET DELAY";
menu[i].code = 631;
menu[i++].text = "SET AUTO RESET DELAY (HOURS):";
menu[i].code = 64;
menu[i++].text = "MAX # AUTO RESETS";
menu[i].code = 641;
menu[i++].text = "MAX # OF AUTO RESETS ALLOWED:";
menu[i].code = 7;
menu[i++].text = "RESET ERRORS";
menu[i].code = 71;
menu[i++].text = "RESET ALL ERRORS?";
menu[i].code = 8;
menu[i++].text = "MAINTENANCE";
menu[i].code = 80;
menu[i++].text = "PREVIOUS MENU";
menu[i].code = 81;
menu[i++].text = "HOLD TO OPEN DRAIN";
menu[i].code = 82;
menu[i++].text = "HOLD TO OPEN INLET";
maxMenuItems = i + 1;
}
void menuAction(unsigned int menuCode) {
//gets called when rotary encoder is clicked,
//check the current location inside the menu,
//execute approriate actions
//returns true if some action is executed
//returns false if nothing is done
Serial.print("menuAction called with menuCode = ");
Serial.println(menuCode);
switch (menuCode) {
case 11: //set current time
if (subMenuActive != 3) { //initialize the subMenu3
lcd.clear();
lcd.setCursor(0,0);
lcd.print("CURRENT TIME:");
subMenuActive = 3;
subMenuPos = 0;
subMenuClick = 0;
subMenu3Update(0); //calls subMenu3Update with 0 (no action)
} else if (subMenuActive == 3) { //already initialized
subMenuActive = 0; //deactivate submenu, activate main menu
lcd.clear();
menuPos = 1;
updateMenuDisplay(menuPos);
//code to set time variables
}
break;
case 211: //set opMode to continuous mode
lcd.clear();
lcd.setCursor(0,0);
lcd.print("CONTINUOUS MODE");
lcd.setCursor(0,1);
lcd.print("ACTIVATED..");
menuPos = 2; //return to a previous menu location
delay(1000);
updateMenuDisplay(menuPos);
break;
case 221: //set opMode to daily volume target mode
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DAILY VOLUME MODE");
lcd.setCursor(0,1);
lcd.print("ACTIVATED..");
menuPos = 222; //return to a previous menu location
delay(1000);
updateMenuDisplay(menuPos);
break;
case 2221: //setting daily target volume
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DAILY TARGET VOLUME:");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DAILY TARGET VOLUME");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print(" LITRES");
menuPos = 222; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 2231: //set time for daily water change
if (subMenuActive != 3) { //initialize the subMenu3
lcd.clear();
lcd.setCursor(0,0);
lcd.print("START DAILY WATER CHANGE AT");
subMenuActive = 3;
subMenuPos = 0;
subMenuClick = 0;
subMenu3Update(0); //calls subMenu3Update with 0 (no action)
} else if (subMenuActive == 3) { //already initialized
subMenuActive = 0; //deactivate submenu, activate main menu
lcd.clear();
menuPos = 1;
updateMenuDisplay(menuPos);
//code to set time variables
}
break;
case 231:
//set opMode to top-off only mode
lcd.clear();
lcd.setCursor(0,0);
lcd.print("TOP-OFF ONLY MODE");
lcd.setCursor(0,1);
lcd.print("ACTIVATED..");
menuPos = 2; //return to a previous menu location
delay(1000);
updateMenuDisplay(menuPos);
break;
case 241:
//set opMode to daily timer mode
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DAILY TIMER MODE");
lcd.setCursor(0,1);
lcd.print("ACTIVATED..");
menuPos = 242; //return to a previous menu location
delay(1000);
updateMenuDisplay(menuPos);
break;
case 2421: //setting drain duration in minutes
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DRAIN FOR THIS MANY MINUTES:");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DRAIN DURATION");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print(" MINUTES");
menuPos = 242; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 2431: //set time for daily water change
if (subMenuActive != 3) { //initialize the subMenu3
lcd.clear();
lcd.setCursor(0,0);
lcd.print("START DAILY WATER CHANGE AT");
subMenuActive = 3;
subMenuPos = 0;
subMenuClick = 0;
subMenu3Update(0); //calls subMenu3Update with 0 (no action)
} else if (subMenuActive == 3) { //already initialized
subMenuActive = 0; //deactivate submenu, activate main menu
lcd.clear();
menuPos = 1;
updateMenuDisplay(menuPos);
//code to set time variables
}
break;
case 311: //setting calibration factor
break;
case 411: //low pressure enable/disable
//menuPos = 41; //return to a previous menu position;
if (subMenuActive != 2) { //if subMenu2 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ENABLE LOW PRESSURE SWITCH?");
subMenuActive = 2; //activate subMenu2
subMenuPos = 1; //sub menu initialization, set it to current setting
subMenuClick = 0;
subMenu2Update(0); //calls subMenu2Update with zero (no action)
} else if (subMenuActive == 2) { //if subMenu2 is already active, execute action from sub menu2
subMenuActive = 0; //activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
if (subMenuPos == 1) {
lcd.print("LOW PRESSURE SWITCH ENABLED");
} else {
lcd.print("LOW PRESSURE SWITCH DISABLED");
}
menuPos = 41; //next menu position;
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 421: //setting delay duration in minutes
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DELAY FILLING FOR THIS MANY MINUTES:");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DELAY DURATION");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print(" MINUTES");
menuPos = 4; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 511: //setting drain pump power level
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SET DRAIN PUMP POWER (DEFAULT 255):");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("DRAIN PUMP POWER");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print("");
menuPos = 5; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 611: //ESD drain duration
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SET EMERGENCY DRAIN DURATION:");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ESD DRAIN DURATION");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print(" MINUTES");
menuPos = 61; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 621: //ESD auto reset enable/disable
//menuPos = 41; //return to a previous menu position;
if (subMenuActive != 2) { //if subMenu2 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ENABLE AUTO RESET ESD?");
subMenuActive = 2; //activate subMenu2
subMenuPos = 1; //sub menu initialization, set it to current setting
subMenuClick = 0;
subMenu2Update(0); //calls subMenu2Update with zero (no action)
} else if (subMenuActive == 2) { //if subMenu2 is already active, execute action from sub menu2
subMenuActive = 0; //activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
if (subMenuPos == 1) {
lcd.print("ESD AUTO RESET ENABLED");
} else {
lcd.print("ESD AUTO RESET DISABLED");
}
menuPos = 63; //next menu position;
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 631: //ESD auto reset delay
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SET ESD AUTO RESET DELAY (HOURS):");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ESD AUTO RESET DELAY");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print(" HOURS");
menuPos = 64; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 641: //Setting # of ESD auto reset allowed
if (subMenuActive != 1) { //if subMenu1 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("MAX # OF ESD AUTO RESETS ALLOWED:");
subMenuActive = 1; //activate subMenu1
subMenuPos = 10; //sub menu initialization, set it to current daily volume target
subMenuClick = 0;
subMenu1Update(0); //calls subMenu1Update with 0 (no action)
} else if (subMenuActive == 1) { //if subMenu1 is already active, execute action from sub menu1 and activate main menu
subMenuActive = 0; //deactivate submenu, activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("MAX # OF AUTO RESET");
lcd.setCursor(0,1);
lcd.print("SET TO ");
lcd.print(subMenuPos);
lcd.print("");
menuPos = 6; //return to previous main menu position
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 71: //master reset
if (subMenuActive != 2) { //if subMenu2 is not active, activate it
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RESET ALL ERRORS?");
subMenuActive = 2; //activate subMenu2
subMenuPos = 1; //sub menu initialization, set it to current setting
subMenuClick = 0;
subMenu2Update(0); //calls subMenu2Update with zero (no action)
} else if (subMenuActive == 2) { //if subMenu2 is already active, execute action from sub menu2
subMenuActive = 0; //activate main menus
//set daily target variable to subMenuPos;
lcd.clear();
lcd.setCursor(0,0);
if (subMenuPos == 1) {
lcd.print("RESET COMPLETED");
} else {
}
menuPos = 7; //next menu position;
delay(1000);
updateMenuDisplay(menuPos);
}
break;
case 81: //manual drain
break;
case 82: //manual fill
break;
default:
Serial.print("Main menu case not found: ");
Serial.println(menuCode);
break;
}
}
void menuUpdate(unsigned char encoderAction) { //main menu navigation updater
//called on by rotary encoder actions
//parameter 0: no action from encoder
//parameter 1: encoder click
//parameter 2: encoder CW rotation
//parameter 3: encoder CCW rotation
//unsigned int verifiedMenuPos;
if (encoderAction == 0) {
//no action
} else { //triggered by action
if (encoderAction == 1) { //encoder click button
//code below takes menu position to the next sub branch
if (menuPos % 10 == 0) { //if menu selection is at XX0 (zero at the end)
menuPos = menuPos / 10; //go back up one level of menu (we use zero as the 'return' menu selection)
} else {
menuPos = menuPos * 10 + 1; //else, go further in one level of menu from the current selection
}
menuPos = menuVerifyPos(menuPos);
menuAction(menuPos); //click execution
updateMenuDisplay(menuPos);
} else if (encoderAction == 2) { //encoder CW rotation
if (menuPos % 10 == 9) { //if current selection is at XX9
//do nothing
} else {
menuPos ++;
}
menuPos = menuVerifyPos(menuPos);
updateMenuDisplay(menuPos);
} else if (encoderAction == 3) { //encoder CCW rotation
if (menuPos % 10 == 0) { //if current selection is at XX0
//do nothing
} else {
menuPos --;
}
menuPos = menuVerifyPos(menuPos);
updateMenuDisplay(menuPos);
}
lastMenuPos = menuPos;
Serial.print("menuPos = ");
Serial.println(menuPos);
}
}
void subMenu1Update(unsigned char encoderAction) { //sub menu type 1 updater: number selection 1 - 255
//called on by rotary encoder actions with a parameter to indicate:
//parameter 0: no action from encoder
//parameter 1: encoder click
//parameter 2: encoder CW rotation
//parameter 3: encoder CCW rotation
if (encoderAction == 0) {
// no action
} else {
if (encoderAction == 1) { //encoder click
subMenuClick++;
//Serial.print("subMenu1Update: click++; click = ");
//Serial.println(subMenuClick);
if(subMenuClick == 1) { //single click to exit sub menu
lcd.clear();
menuAction(menuPos); //calls menuAction with last main menu tree position
}
} else if (encoderAction == 2) { //CW rotation
if (subMenuPos >= 255) {
subMenuPos = 1;
} else {
subMenuPos++;
}
} else if (encoderAction == 3) { //CCW rotation
if (subMenuPos <= 1) {
subMenuPos = 255;
} else {
subMenuPos--;
}
}
//update 4th row of LCD to display sub menu position
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print(subMenuPos);
}
}
void subMenu2Update(unsigned char encoderAction){ //sub menu type 2, yes-no selection
//called on by rotary encoder actions
//parameter 0: no action from encoder
//parameter 1: encoder click
//parameter 2: encoder CW rotation
//parameter 3: encoder CCW rotation
if (encoderAction == 1) { //encoder click
subMenuClick++;
if(subMenuClick == 1) { //single click to exit sub menu
lcd.clear();
menuAction(menuPos); //calls menuAction with last main menu tree position
}
} else { //encoder rotation
if (encoderAction != 0) {
if (subMenuPos != 1) { //flip subMenuPos between 0 and 1
subMenuPos = 1;
} else {
subMenuPos = 0;
}
}
//update 4th row of LCD to display sub menu position
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
if (subMenuPos == 1) {
lcd.print("YES");
} else if (subMenuPos == 0) {
lcd.print("NO");
}
}
}
void subMenu3Update(unsigned char encoderAction) { //sub menu type 3 updater: time setting
//called on by rotary encoder actions with a parameter to indicate:
//parameter 0: no action from encoder
//parameter 1: encoder click
//parameter 2: encoder CW rotation
//parameter 3: encoder CCW rotation
if (encoderAction == 0) {
// no action
} else {
if (encoderAction == 1) { //encoder click
subMenuClick++;
if(subMenuClick == 1) { //first click to finish setting hour, move on to set minutes
subMenuPos = 0; //reset subMenuPos to be used for setting minute
} else if(subMenuClick == 2) { //second click to record minute, exit menu
//:::code to set hour and minute to global variables:::
lcd.clear();
menuAction(menuPos); //calls menuAction with last main menu tree position
}
} else if (encoderAction == 2) { //CW rotation
if (subMenuClick == 0) { //setting hour
if (subMenuPos >= 24) { //scrolling end limits
subMenuPos = 0;
} else {
subMenuPos++;
}
setHour = subMenuPos;
} else if (subMenuClick == 1) { //setting minute
if (subMenuPos >= 59) { //scrolling end limits
subMenuPos = 0;
} else {
subMenuPos++;
}
setMinute = subMenuPos;
}
} else if (encoderAction == 3) { //CCW rotation
if (subMenuClick == 0) { //setting hour
if (subMenuPos <= 0) { //scrolling end limits
subMenuPos = 24;
} else {
subMenuPos--;
}
setHour = subMenuPos;
} else if (subMenuClick == 1) { //setting minute
if (subMenuPos <= 0) { //scrolling end limits
subMenuPos = 59;
} else {
subMenuPos--;
}
setMinute = subMenuPos;
}
}
//update 4th row of LCD to display sub menu position
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print(setHour);
lcd.print(":");
lcd.print(setMinute);
}
}
mega:SCL
mega:SDA
mega:AREF
mega:GND.1
mega:13
mega:12
mega:11
mega:10
mega:9
mega:8
mega:7
mega:6
mega:5
mega:4
mega:3
mega:2
mega:1
mega:0
mega:14
mega:15
mega:16
mega:17
mega:18
mega:19
mega:20
mega:21
mega:5V.1
mega:5V.2
mega:22
mega:23
mega:24
mega:25
mega:26
mega:27
mega:28
mega:29
mega:30
mega:31
mega:32
mega:33
mega:34
mega:35
mega:36
mega:37
mega:38
mega:39
mega:40
mega:41
mega:42
mega:43
mega:44
mega:45
mega:46
mega:47
mega:48
mega:49
mega:50
mega:51
mega:52
mega:53
mega:GND.4
mega:GND.5
mega:IOREF
mega:RESET
mega:3.3V
mega:5V
mega:GND.2
mega:GND.3
mega:VIN
mega:A0
mega:A1
mega:A2
mega:A3
mega:A4
mega:A5
mega:A6
mega:A7
mega:A8
mega:A9
mega:A10
mega:A11
mega:A12
mega:A13
mega:A14
mega:A15
lcd1:GND
lcd1:VCC
lcd1:SDA
lcd1:SCL
encoder1:CLK
encoder1:DT
encoder1:SW
encoder1:VCC
encoder1:GND