Uno stack with Display Shield menu
A control unit comprising menu-driven Uno stack with LCD display shield and Adafruit protoyping shield for interface control circuits
Components and supplies
Arduino UNO
Tools and machines
Soldering iron (generic)
Solder Wire, Lead Free
Apps and platforms
Arduino IDE
Project description
Code
Ken_Pump_Ctrl_with_display__Rev5.ino
arduino
Menu-driven code for Water Pump Controller. Note that this code was developed to meet functionality requirements and is likely not the most efficient as the author's background is not in software. However, it expands on earlier code developed by Paul Siewert. Improvements to meet efficiency, etc welcome.
1/*************************************************************************************** 2 3 Name: Pump Ctrl with Display 4 Author: Ken Li 5 Created : June 2020 6 7 Last Modified: June 26, 2021 8 Version : 1.0 9 Based on: LCD Button 10 Shield Menu created by Paul Siewert on June 14, 2016 with the following notes: 11 12 Notes : 1) This code is for use with an Arduino Uno and LCD/button shield. 13 The intent is for anyone to use this program to give them a starting 14 program 15 with a fully functional menu with minimal modifications required by the user. 16 17 2) This program is designed to get you as close as possible to a 18 finished menu for the standard Arduino Uno LCD/button shield. The only required 19 modifications 20 are to add as menu items to the master menu (menuItems 21 array) and then modify/adjust the void functions below for each of those selections. 22 23 License : This program is free software. You can redistribute it and/or modify 24 it under the terms of the GNU General Public License as published by 25 the 26 Free Software Foundation, either version 3 of the License, or (at your option) any 27 later version. 28 This program is distributed in the hope that it 29 will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 30 31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 32 General Public License for more details. 33 ***************************************************************************************/ 34/* 35 Ken Li notes/rev history 36 37 REV 3-5 (and part of REV2): 38 May 25, 39 2021. To do: 40 1) Add code to cover instantaneous on (DONE) 41 2) Add code 42 to perform RUN mode operation (decrement days and turn on pump when days duration 43 is reached) (DONE, 6/4/21 ) 44 3) Fix Up/DN arrow indicator in lowest menus 45 ( to select DURATION or # of Days) (DONE, 6/10/21) 46 4) Clean up Code - remove 47 commented out code and unneeded monitor code (REV 4) 48 5) Streamline Code - 49 made UP/DN arrow code into a function (DONE 6/23/2021) (REV 5) 50 6) Additional 51 Streamline Code (Go to REV 6 which is work in progress) 52 53 REV 1: 54 55 May 25, 2021 56 1) Fixed majority of menu operation. Started to add actual 57 pump control. Will do that in Rev 2. 58 59 Feb 21, 2021 60 1) Update 61 Display to show current Settings and inform users how to change 62 63 Nov 9, 64 2020. This program has been modified to provide control of a plant watering pump. 65 There will be four modes of operation: 66 1) RUN mode where pump operates 67 a 'watering moment' based on day interval and water duration settings (default or 68 user defined) 69 2) INSTANT ON/OFF control to provide for off-schedule immediate 70 turn on of pump 71 3) DAY INTERVAL which sets how many days to wait before a 72 'watering moment' is initiated. 73 4) WATER DURATION which sets how long water 74 is pumped when a 'watering moment' is reached 75 76EEPROM will be used to store 77 DAYS and WATER DURATION that will be read by Pump Control code 78 79*/ 80// 81 You can have up to 10 menu items in the menuItems[] array below without having to 82 change the base programming at all. Name them however you'd like. 83// Beyond 84 10 items, you will have to add additional "cases" in the switch/case section of 85 the operateMainMenu() function below. You will also have to 86// add additional 87 void functions (i.e. menuItem11, menuItem12, etc.) to the program. 88// String 89 menuItems[] = {"ITEM 1", "ITEM 2", "ITEM 3", "ITEM 4", "ITEM 5", "ITEM 90 6"}; // saved from original code 91 92// Main menu variables will be prefixed 93 by 'menu'; Submenu variables will be prefixed by 'submenu' 94 95#include<EEPROM.h> 96 97 bool Out1 = false; 98 byte Day_countdown = 0; 99 int Duration = 0; 100 101 int x = 1; 102 int a; 103 int b; 104 105String menuItems[] = {"chg MODE(RN/PRG)", 106 "INSTANT BURST", "chg DAYS INTRVL", "Chg WATER DUR"}; 107String submenuItems_MODE[] 108 = {"RUN <sel>", "PROG <sel)"}; 109String submenuItems_INSTANT[] = {"OFF", 110 "ON"}; 111String submenuItems_DAYSINTERVAL[] = {"2 Days", "3 Days", "4 112 Days", "5 Days", "6 Days", "7 Days", "8 Days", "9 Days", "10 Days", 113 "11 Days", "12 Days", "13 Days", "14 Days"}; 114 // 13 entries [0-12]; 115 so entry # and Days #s are offset by 2 (0-12 entries and 2-14 days) 116String submenuItems_WATERDURATION[] 117 = {"4secs", "6secs", "8secs", "10secs"}; // 4 entries [0-3]. So entry 118 # and Secs # are offset by TBD (0-3 entries and 4-10 secs) 119 120// Navigation 121 button variables 122int readKey; 123int button; 124 125// Main Menu control variables: 126int 127 menuPage = 0; 128int maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) 129 / 2) + .5); 130int cursorPosition = 0; 131int mode = 0; // Operating mode: 0 = 132 Run; 1 = Program/change settings; Initialized to RUN for operating case where pump 133 power is controlled by timer. 134int Day_Interval; 135int Water_duration; 136 137// 138 Sub Menu control variables: 139int submenuPage = 0; 140int submaxMenuPages = round(((sizeof(submenuItems_DAYSINTERVAL) 141 / sizeof(String)) / 2) + .5); 142 143// Creates 3 custom characters for the menu 144 display 145byte downArrow[8] = { 146 0b00100, // * 147 0b00100, // * 148 0b00100, 149 // * 150 0b00100, // * 151 0b00100, // * 152 0b10101, // * * * 153 0b01110, 154 // *** 155 0b00100 // * 156}; 157 158byte upArrow[8] = { 159 0b00100, // * 160 161 0b01110, // *** 162 0b10101, // * * * 163 0b00100, // * 164 0b00100, // 165 * 166 0b00100, // * 167 0b00100, // * 168 0b00100 // * 169}; 170 171byte 172 menuCursor[8] = { 173 B01000, // * 174 B00100, // * 175 B00010, // * 176 177 B00001, // * 178 B00010, // * 179 B00100, // * 180 B01000, // * 181 182 B00000 // 183}; 184 185#include <Wire.h> 186#include <LiquidCrystal.h> 187 188// 189 Setting the LCD shields pins 190LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 191 192void 193 setup() { 194 195// initialize digital pins 196 pinMode(LED_BUILTIN, OUTPUT); 197 // Indicator 198 pinMode(12, OUTPUT); // sets the digital pin 12 as output 199 to control Pump transistor switch 200 digitalWrite(12, LOW); // set Transistor 201 control low to turn off water 202 digitalWrite(LED_BUILTIN, LOW); // turn the 203 LED off (INDICATOR) 204 205 // Initializes serial communication 206 Serial.begin(9600); 207 208 Serial.print(F("1) Hello World ")); // Store string in FLASH instead of RAM 209 to save space 210 211 // Initializes and clears the LCD screen 212 lcd.begin(16, 213 2); 214 lcd.clear(); 215 216 // Creates the byte for the 3 custom characters 217 218 lcd.createChar(0, menuCursor); 219 lcd.createChar(1, upArrow); 220 lcd.createChar(2, 221 downArrow); 222 223 lcd.clear(); 224 lcd.setCursor(0, 0); 225 lcd.write("The 226 Programmable"); 227 lcd.setCursor(0, 1); 228 lcd.write("Pump Control-R5"); 229 230 delay (2000); 231 232 lcd.clear(); 233 lcd.setCursor(0, 0); 234 lcd.write("Pumps 235 X secs of"); 236 lcd.setCursor(0, 1); 237 lcd.write("H20 per Y days"); 238 239 delay (2000); 240} 241 242void loop() { // Modify this to show display 243 244 245 // The following code will check to see if it's time to run the pump based on 246 countdown of the Days. If not, proceed to display 247 // Turn on LED to indicate 248 start 249 Serial.print(F(" Turn on Circuit, (flash LED, half-sec on, half-sec 250 off) ")); // Store string in FLASH instead of RAM to save space 251 Serial.println(); 252 // End the line 253 do { 254 digitalWrite(LED_BUILTIN, Out1); // turn 255 the LED on (HIGH is the voltage level) (INDICATOR) 256 delay(500); // wait 257 for 0.5 seconds 258 Out1 = !Out1; 259 digitalWrite(LED_BUILTIN, Out1); // 260 turn the LED off by making the voltage LOW 261 delay(500); // 262 wait for 0.5 seconds 263 Out1 = !Out1; 264 x = x-1; 265 } while (x > 266 0); 267 268// ----- Initial Test Done 269 270 Day_countdown = EEPROM.read(1); 271 // Read Countdown from EEPROM location 1 272 unsigned int var3 = Day_countdown; 273 274 var3 = var3 - 1 ; // Decrement 275 Day_countdown = byte(var3); 276 Serial.print(F(" 277 NORMAL Operation - Count down days (EEPROM value) = ")); // Store string in FLASH 278 instead of RAM to save space 279 Serial.println(Day_countdown); 280 281 if(Day_countdown 282 == 0) { 283 284 // the following turns on the transistor switch to pump 285 water; 286 lcd.clear(); 287 lcd.setCursor(0, 0); 288 lcd.write("PUMP 289 ON"); 290 digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the 291 voltage level) (INDICATOR) 292 digitalWrite(12, HIGH); // set transistor control 293 output HIGH to turn on pump 294 unsigned int var4 = EEPROM.read(2); 295 var4 296 = var4*1000; // convert to seconds 297 delay(var4); // 298 Pump on time value 299 Serial.print(F(" PUMP OFF")); // Store string in 300 FLASH instead of RAM to save space 301 Serial.println(); // End the line 302 303 digitalWrite(LED_BUILTIN, LOW); // turn the LED off (INDICATOR) 304 digitalWrite(12, 305 LOW); // set transistor control output LOW to turn pump off 306 Day_countdown 307 = EEPROM.read(0); 308 } 309 else { 310 lcd.clear(); 311 lcd.setCursor(0, 312 0); 313 lcd.print("Days until next"); 314 lcd.setCursor(0, 1); 315 316 lcd.print("watering:"); 317 lcd.setCursor(11, 1); 318 lcd.print(Day_countdown); 319 320 delay (2000); 321 } 322 EEPROM.write(1, Day_countdown); // 323 Write new value to EEPROM location 1 324 325 326 /* The following will toggle 327 between two display messages while pump is in run/on mode 328 * a) Displaying 329 the Current Settings for a few seconds 330 * b) Alerting the user to hit the 331 <SELECT> button to Change Settings 332 * 333 * The User can hit the SELECT 334 button anytime when the display is toggling between the two messages 335 */ 336 337 338 while (mode == 0) { 339 // Display "Current Settings" 340 lcd.clear(); 341 342 lcd.setCursor(0, 0); 343 lcd.print("Pump settings:"); 344 lcd.setCursor(0, 345 1); 346 lcd.print(EEPROM.read(2)); // Display WATER DURATION value 347 lcd.setCursor(2,1); 348 349 lcd.print("s/"); 350 lcd.setCursor(4,1); 351 lcd.print( EEPROM.read(0)); 352 // Display DAYS INTERVAL value 353 lcd.setCursor(6,1); 354 lcd.print("days 355 & "); 356 lcd.setCursor(13,1); 357// if(Mode == 0) { 358 lcd.print("ON 359 "); 360// } 361// lcd.print("OFF"); 362 delay (50); 363 364 // Wait 365 for <select> button to be pushed 366 for (int i = 0; i <= 15000; i++) { 367 368 button = evaluateButton(readKey); 369 switch (button) { 370 case 5: // 371 SELECT button depressed; proceed to the program mode 372 button = 0; 373 374 Serial.print(F("2a) SELECT button hit to 'Change Settings' ")); // 375 Store string in FLASH instead of RAM to save space 376 int menuPage = 0; 377 378 mainMenuDraw(); 379 delay(100); 380 operateMainMenu(); // This 381 enables user to go into PROGRAM mode in order to change settings or to run pump 382 immediately 383 mainMenuDraw(); 384 delay (100); 385 break; 386 387 } 388 } 389 390 // Display "Instruction for user to hit SELECT button 391 to enable changing settings". 392 lcd.clear(); 393 lcd.setCursor(0, 394 0); 395 lcd.print("Hit <select> to"); 396 lcd.setCursor(0, 1); 397 398 lcd.print("change settings"); // Display value 399 delay (50); 400 401 402 // Wait for <select> button to be pushed 403 for (int i = 0; i <= 10000; i++) { 404 405 button = evaluateButton(readKey); 406 switch (button) { 407 case 408 5: // SELECT button depressed; proceed to the program mode 409 button = 0; 410 411 Serial.print(F("2b) SELECT button hit to 'Change Settings' ")); 412 int 413 menuPage = 0; 414 mainMenuDraw(); 415 delay(100); 416 operateMainMenu(); 417 // This enables user to go into PROGRAM mode in order to change settings or to 418 run pump immediately 419 mainMenuDraw(); 420 delay (100); 421 break; 422 423 } 424 } 425} 426} 427 428 429// This function, for the MAIN menu, will 430 generate the 2 menu items that can fit on the screen. They will change as you scroll 431 through your menu. Up and down arrows will indicate your current menu position. 432void 433 mainMenuDraw() { 434 Serial.print("menuPage= "); 435 Serial.println(menuPage); 436 437 lcd.clear(); 438 lcd.setCursor(0, 0); 439 lcd.print(menuItems[menuPage]); 440 441 lcd.setCursor(0,1); 442 if (mode == 0) { 443 lcd.print("Use SEL & U/D"); 444 445 } 446 else { 447 if (menuPage == 1) { 448 lcd.print("Use U/D or SEL"); 449 450 } else 451 { 452 lcd.print("Use U/D & SEL"); 453 } 454 } 455 456 457 UpDnArrowDraw(menuPage, maxMenuPages); 458 459} 460 461 462void operateMainMenu() 463 { 464 int activeButton = 0; 465 while (activeButton == 0) { 466 int button; 467 468 int button_temp; // test 469 readKey = analogRead(0); 470 while (readKey 471 > 790) { // Wait here if no buttons pushed; read value should be max (~1023) 472 473 delay(50); 474 readKey = analogRead(0); 475 } 476 button = evaluateButton(readKey); 477 478 switch (button) { 479 case 0: // NO button returns as 0 there is no action 480 taken 481 break; 482 case 1: // RIGHT button returns as 0 there is no 483 action taken 484 break; 485 486 case 2: // UP button 487 button 488 = 0; 489 if(mode==1) { 490 cursorPosition = cursorPosition - 1; 491 492 cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) 493 - 1)); 494 menuPage = menuPage - 1; 495 menuPage = constrain(menuPage, 496 0, maxMenuPages); 497 mainMenuDraw(); 498 } 499 break; 500 501 502 503 case 3: // DOWN Key 504 button = 0; 505 if(mode==1) 506 { 507 cursorPosition = cursorPosition + 1; 508 cursorPosition = 509 constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1)); 510 menuPage 511 = menuPage + 1; 512 menuPage = constrain(menuPage, 0, maxMenuPages); 513 514 mainMenuDraw(); 515 } 516 break; 517 518 case 4: // 519 LEFT button returns as 0 there is no action taken 520 button = 0; 521 activeButton 522 == 1; 523 break; 524 525 case 5: // SELECT button will proceed to 526 the lower level sub-menu 527 button = 0; 528 switch (cursorPosition) 529 { // The case that is selected here is dependent on which menu page you are on and 530 where the cursor is. 531 case 0: 532 menuItem1(); // Submenu 533 for MODE 534 if (mode == 0) { 535 activeButton = 1; 536 } 537 538 break; 539 case 1: 540 if (mode == 1) { 541 menuItem2(); 542 // submenuItems_INSTANT 543 } 544 break; 545 case 546 2: 547 if (mode == 1) { 548 menuItem3(); // Submenu for DAYS 549 INTERVAL 550 } 551 break; 552 case 3: 553 if 554 (mode == 1) { 555 menuItem4(); // Submenu for WATER DURATION 556 } 557 558 break; 559 case 4: 560// menuItem5(); // Spare 561 562 break; 563 } 564 mainMenuDraw(); 565 break; 566 567 568 } 569 } 570} 571 572// This function is called whenever a button 573 is pressed and determines which of the 5 buttons was pressed, based on the voltage 574 drop across the buttons all hooked up to A0. 575int evaluateButton(int x) { 576 577 int result = 0; 578 x = analogRead(0); 579 if (x < 790) { 580 delay(100); 581 582 x = analogRead(0); // Performs glitch filtering 583 } 584 if (x < 50) { 585 586 result = 1; // right button 587 } else if (x < 195) { 588 result = 2; // 589 up button 590 } else if (x < 380) { 591 result = 3; // down button 592 } else 593 if (x < 555) { // Added this temporarily to implement 'Select' Fct. Swap this 594 with result 4 later to maintain order. 595 result = 4; // Left button 596 } 597 else if (x < 790) { 598 result = 5; // Select button 599 } 600 return result; 601} 602 603// 604 If there are common usage instructions on more than 1 of your menu items you can 605 call this function from the sub 606// menus to make things a little more simplified. 607 If you don't have common instructions or verbage on multiple menus 608// I would 609 just delete this void. You must also delete the drawInstructions()function calls 610 from your sub menu functions. 611/* 612void drawInstructions() { 613 lcd.setCursor(0, 614 1); // Set cursor to the bottom line 615 lcd.print("Use "); 616 lcd.print(byte(1)); 617 // Up arrow 618 lcd.print("/"); 619 lcd.print(byte(2)); // Down arrow 620 lcd.print(" 621 buttons"); 622} 623 624*/ 625 626 627 628void menuItem1() { // Function executes 629 when you select the 1st item from main menu 630 int activeButton = 0; 631 submaxMenuPages 632 = (sizeof(submenuItems_MODE)/ sizeof(String)-1); 633 submenuPage = 0; // set to 634 run mode 635 readKey = analogRead(0); // Wait until last push button operation 636 (likely SELECT) is over so that submenu functions can take over 637 while (readKey 638 < 790) { 639 delay(10); 640 readKey = analogRead(0); 641 } 642 lcd.clear(); 643 644 lcd.setCursor(0, 0); 645 lcd.print("UP/DN to chg"); 646 lcd.setCursor(0, 1); 647 648 lcd.print(submenuItems_MODE[submenuPage]); 649 UpDnArrowDraw(submenuPage, submaxMenuPages); 650 651 while (activeButton == 0) { 652 int button; // Debounce 653 654 readKey = analogRead(0); 655 while (readKey > 790) { // Wait here if no buttons 656 pushed; read value should be max (~1023) 657 delay(10); 658 readKey = 659 analogRead(0); 660 } 661 button = evaluateButton(readKey); 662 switch 663 (button) { 664 case 0: // When button returns as 0 there is no action taken 665 666 break; 667 668 case 1: // When button returns as 1 there is no 669 action taken 670 break; 671 672 case 2: // Up arrow control 673 674 button = 0; 675 if (submenuPage == 0) { 676 break; 677 } 678 679 submenuPage = submenuPage - 1; 680 submenuPage = constrain(submenuPage, 681 0, submaxMenuPages); 682 UpDnArrowDraw(submenuPage, submaxMenuPages); 683 684 lcd.clear(); 685 lcd.setCursor(0, 0); 686 lcd.print("UP/DN 687 to chg"); 688 lcd.setCursor(0, 1); 689 lcd.print(submenuItems_MODE[submenuPage]); 690 // Display value 691 Serial.println("submenuPage = "); 692 Serial.println(submenuPage); 693 694 Serial.println("submaxMenuPages = "); 695 Serial.println(submaxMenuPages); 696 697 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the 698 UP/DN arrows 699 break; // Exit Case 2 700 701 case 3: // 702 Down arrow control 703 button = 0; 704 if (submenuPage == submaxMenuPages) 705 { 706 break; 707 } 708 submenuPage = submenuPage + 1; 709 710 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 711 UpDnArrowDraw(submenuPage, 712 submaxMenuPages); 713 lcd.clear(); 714 lcd.setCursor(0, 0); 715 lcd.print("UP/DN 716 to chg"); 717 lcd.setCursor(0, 1); 718 lcd.print(submenuItems_MODE[submenuPage]); 719 720 Serial.println("submenuPage = "); 721 Serial.println(submenuPage); 722 723 Serial.println("submaxMenuPages = "); 724 Serial.println(submaxMenuPages); 725 726 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the 727 UP/DN arrows 728 break; // Exit Case 3 729 730 case 4: // This 731 case will execute if the "back" button (e.g. LEFT) is pressed to go back to the 732 Main menu 733 button = 0; 734 activeButton = 1; 735 break; 736 737 738 case 5: // This case will execute if the "select" button is 739 pressed. CODE NEEDED HERE 740 button = 0; 741 lcd.setCursor(5, 1); 742 743 lcd.print(" selected"); //Indicates that current displayed value has 744 been selected 745 mode = submenuPage; // Mode controls 746 Serial.print(F("Selected 747 Mode (0=RUN; 1=Program)= ")); // Store string in FLASH instead of RAM to save 748 space 749 Serial.println(mode); 750 delay(2000); // Wait 2 seconds 751 752 activeButton = 1; 753 break; 754 } 755 } 756} 757 758void menuItem2() 759 { // Enables immediate Turn-on of water for duration based on value stored in EEPROM. 760 No menu selection here. 761 762 int duration; 763 duration = EEPROM.read(2); 764 // Retrieve duration value 765 duration = duration*1000; // convert to ms 766 767 768 delay(1000); // wait for 1 second 769 Serial.print(" 770 PUMP ON"); 771 Serial.println(); // End the line 772 digitalWrite(LED_BUILTIN, 773 HIGH); // turn the LED on (HIGH is the voltage level) (INDICATOR) 774 digitalWrite(12, 775 HIGH); // set transistor control output HIGH to turn on pump 776 lcd.clear(); 777 778 lcd.setCursor(1, 0); 779 lcd.print("PUMP running"); 780 delay(duration); 781 // wait for 10 seconds 782 Serial.print(" PUMP OFF"); 783 784 Serial.println(); // End the line 785 digitalWrite(LED_BUILTIN, LOW); 786 // turn the LED off (INDICATOR) 787 digitalWrite(12, LOW); // set transistor 788 control output LOW to turn pump off 789 lcd.clear(); 790 lcd.setCursor(1, 791 0); 792 lcd.print("PUMP done"); 793 delay(2000); 794 } 795 796void menuItem3() 797 // Function executes to set the # of Days between turn-on of water (DAYS INTERVAL) 798 799 { 800 int activeButton = 0; 801 submenuPage = EEPROM.read(0)-2; // remove 802 offset due to values starting with 2 Days 803 submaxMenuPages = (sizeof(submenuItems_DAYSINTERVAL)/ 804 sizeof(String)-1); 805 Serial.print (F("sizeof(submenuItems_DAYSINTERVAL) = ")); 806 807 Serial.println (sizeof(submenuItems_DAYSINTERVAL)); 808 Serial.print (F("sizeof(String) 809 is ")); 810 Serial.println (sizeof(String)); 811 Serial.println("submaxMenuPages 812 = "); 813 Serial.println(submaxMenuPages); 814 readKey = analogRead(0); // Wait 815 until last push button operation (likely SELECT) is over so that submenu functions 816 can take over 817 while (readKey < 790) { 818 delay(10); 819 readKey 820 = analogRead(0); 821 } 822 823 Serial.print("submaxMenuPages= "); 824 Serial.print(submaxMenuPages); 825 826 Serial.println(" entering MenuItem3"); 827 lcd.clear(); 828 lcd.setCursor(1, 829 0); 830 lcd.print("UP/DN to change"); 831 lcd.setCursor(0, 1); 832 lcd.print(EEPROM.read(0)); 833 834 lcd.print(" days(curr)"); 835 UpDnArrowDraw(submenuPage, submaxMenuPages); 836 837 while (activeButton == 0) { 838 button; // Debounce 839 readKey 840 = analogRead(0); 841 while (readKey > 790) { // Wait here if no buttons pushed; 842 read value should be max (~1023) 843 delay(10); 844 readKey = analogRead(0); 845 846 } 847 848 button = evaluateButton(readKey); 849 switch (button) { 850 851 case 0: // When button returns as 0 there is no action taken 852 break; 853 854 855 case 1: // When button returns as 1 there is no action taken 856 break; 857 858 859 case 2: // Up arrow control 860 button = 0; 861 if (submenuPage 862 == 0) { 863 UpDnArrowDraw(submenuPage, submaxMenuPages); 864 break; 865 866 } 867 submenuPage = submenuPage - 1; 868 submenuPage = constrain(submenuPage, 869 0, submaxMenuPages+1); 870 lcd.clear(); 871 lcd.setCursor(0, 0); 872 873 lcd.print("Pick 2-14 Days"); 874 lcd.setCursor(0, 1); 875 lcd.print(submenuItems_DAYSINTERVAL[submenuPage]); 876 // TEST FOR NOW TO GET BASIC FUNCTIONALITY WITH ONE SUBMENU - MIGHT NEED ARRAY 877 LATER 878 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just 879 draws the UP/DN arrows 880 881 break; // Exit Case 2 882 883 884 case 3: // Down arrow control 885 button = 0; 886 if (submenuPage 887 == submaxMenuPages) { 888 UpDnArrowDraw(submenuPage, submaxMenuPages); 889 890 break; 891 } 892 submenuPage = submenuPage + 1; 893 894 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 895 lcd.clear(); 896 897 lcd.setCursor(0, 0); 898 lcd.print("Pick 2-14 Days"); 899 lcd.setCursor(0, 900 1); 901 lcd.print(submenuItems_DAYSINTERVAL[submenuPage]); // TEST FOR 902 NOW TO GET BASIC FUNCTIONALITY WITH ONE SUBMENU - MIGHT NEED ARRAY LATER 903 UpDnArrowDraw(submenuPage, 904 submaxMenuPages); // This now just draws the UP/DN arrows 905 break; // 906 Exit Case 3 907 908 case 4: // This case will execute if the "back" 909 button (e.g. LEFT) is pressed to go back to the Main menu 910 button = 0; 911 912 activeButton = 1; 913 break; 914 915 case 5: // This 916 case will execute if the "select" button is pressed. 917 button = 0; 918 919 byte Day; 920 lcd.setCursor(8, 1); 921 lcd.print("selected"); 922 923 Day_Interval = submenuPage + 2; // Offset because days options 924 starts at 2 instead of 0 925 EEPROM.write(0, Day_Interval); // EEPROM 926 Address 0 holds DAY INTERVAL value 927 EEPROM.write(1, Day_Interval); // 928 EEPROM Address 1 holds Count down of days until next watering 929 Day_countdown 930 = EEPROM.read(1); 931 delay(2000); // Wait 2 seconds 932 activeButton 933 = 1; 934 break; 935 } 936 } 937} 938 939void menuItem4() { // To 940 set duration of pump turn-on (e.g WATER DURATION), in seconds 941 int activeButton 942 = 0; 943 submenuPage = (EEPROM.read(2) - 4)/2; 944 submaxMenuPages = (sizeof(submenuItems_WATERDURATION)/ 945 sizeof(String)-1); 946 readKey = analogRead(0); // Wait until last push button 947 operation (likely SELECT) is over so that submenu functions can take over 948 while 949 (readKey < 790) { 950 delay(10); 951 readKey = analogRead(0); 952 } 953 954 955 lcd.clear(); 956 lcd.setCursor(1, 0); 957 lcd.print("UP/DN to change"); 958 959 lcd.setCursor(0, 1); 960 lcd.print(EEPROM.read(2)); 961 lcd.print(" secs(curr)"); 962 963 UpDnArrowDraw(submenuPage, submaxMenuPages); 964 while (activeButton == 0) { 965 966 button; // Debounce 967 readKey = analogRead(0); 968 while 969 (readKey > 790) { // Wait here if no buttons pushed; read value should be max (~1023) 970 971 delay(10); 972 readKey = analogRead(0); 973 } 974 975 button 976 = evaluateButton(readKey); 977 switch (button) { 978 case 0: // When button 979 returns as 0 there is no action taken 980 break; 981 982 case 1: 983 // When button returns as 1 there is no action taken 984 break; 985 986 987 case 2: // Up arrow control 988 button = 0; 989 if (submenuPage 990 == 0) { 991 UpDnArrowDraw(submenuPage, submaxMenuPages); 992 break; 993 994 } 995 submenuPage = submenuPage - 1; 996 submenuPage = constrain(submenuPage, 997 0, submaxMenuPages); 998 Serial.print("Days Value from UP key (submenuPage)= 999 "); 1000 Serial.println(submenuPage); 1001 lcd.clear(); 1002 lcd.setCursor(0, 1003 0); 1004 lcd.print("Pick 4-10 secs"); 1005 lcd.setCursor(0, 1); 1006 1007 lcd.print(submenuItems_WATERDURATION[submenuPage]); // TEST FOR NOW TO 1008 GET BASIC FUNCTIONALITY WITH ONE SUBMENU - MIGHT NEED ARRAY LATER 1009 UpDnArrowDraw(submenuPage, 1010 submaxMenuPages); // This now just draws the UP/DN arrows 1011 break; // 1012 Exit Case 2 1013 1014 case 3: // Down arrow control 1015 button 1016 = 0; 1017 if (submenuPage == submaxMenuPages) { 1018 UpDnArrowDraw(submenuPage, 1019 submaxMenuPages); 1020 break; 1021 } 1022 submenuPage = 1023 submenuPage + 1; 1024 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 1025 1026 lcd.clear(); 1027 lcd.setCursor(0, 0); 1028 lcd.print("Pick 1029 4-10 secs"); 1030 lcd.setCursor(0, 1); 1031 lcd.print(submenuItems_WATERDURATION[submenuPage]); 1032 1033 Serial.print(F("Days Value from DN key (submenuPage)= ")); // 1034 Test 1035 Serial.println(submenuPage); 1036 UpDnArrowDraw(submenuPage, 1037 submaxMenuPages); // This now just draws the UP/DN arrows 1038 1039 break; 1040 // Exit Case 3 1041 1042 case 4: // This case will execute if the "back" 1043 button (e.g. LEFT) is pressed to go back to the Main menu 1044 button = 0; 1045 1046 activeButton = 1; 1047 break; 1048 1049 case 5: // This 1050 case will execute if the "select" button is pressed. CODE NEEDED HERE 1051 button 1052 = 0; 1053 byte Water; 1054 lcd.setCursor(8, 1); 1055 lcd.print("selected"); 1056 1057 Water_duration = (submenuPage +2) * 2; // Adjusted for available 1058 options 1059 EEPROM.write(2, Water_duration); // EEPROM Address 1 holds WATER 1060 DURATION value 1061 Water = EEPROM.read(2); 1062 Serial.print(F("Selected 1063 Water Duration(Byte)after WRITE & READ to EEPROM= ")); 1064 Serial.println(Water); 1065 1066 delay(2000); // Wait 2 seconds 1067 activeButton = 1068 1; 1069 break; 1070 } 1071 } 1072} 1073 1074 1075 1076// This function will 1077 generate the arrows that indicate which UP/DN buttons to press. 1078 1079 int 1080 UpDnArrowDraw(int a,int b) { 1081 if (a == 0) { // IF for Up/Dn arrows 1082 lcd.setCursor(15, 1083 1); 1084 lcd.write(byte(2)); // Display DN arrow only ( BYTE 1 is UP arrow; BYTE 1085 2 is DN arrow) 1086 lcd.setCursor(15, 0); 1087 lcd.print(" "); 1088 } 1089 1090 else if (a > 0 and a < b ) { 1091 lcd.setCursor(15, 1); 1092 lcd.write(byte(2)); 1093 1094 lcd.setCursor(15, 0); 1095 lcd.write(byte(1)); // Display both UP and DN 1096 arrow 1097 } 1098 else if (a == b) { 1099 lcd.setCursor(15, 0); 1100 lcd.write(byte(1)); 1101 // Display UP arrow only 1102 lcd.setCursor(15, 1); 1103 lcd.print(" "); 1104 1105 } 1106} 1107
Ken_Pump_Ctrl_with_display__Rev5.ino
arduino
Menu-driven code for Water Pump Controller. Note that this code was developed to meet functionality requirements and is likely not the most efficient as the author's background is not in software. However, it expands on earlier code developed by Paul Siewert. Improvements to meet efficiency, etc welcome.
1/*************************************************************************************** 2 Name: Pump Ctrl with Display 3 Author: Ken Li 4 Created : June 2020 5 Last Modified: June 26, 2021 6 Version : 1.0 7 Based on: LCD Button Shield Menu created by Paul Siewert on June 14, 2016 with the following notes: 8 Notes : 1) This code is for use with an Arduino Uno and LCD/button shield. The intent is for anyone to use this program to give them a starting 9 program with a fully functional menu with minimal modifications required by the user. 10 2) This program is designed to get you as close as possible to a finished menu for the standard Arduino Uno LCD/button shield. The only required modifications 11 are to add as menu items to the master menu (menuItems array) and then modify/adjust the void functions below for each of those selections. 12 License : This program is free software. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by 13 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 14 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 ***************************************************************************************/ 17/* Ken Li notes/rev history 18 19 REV 3-5 (and part of REV2): 20 May 25, 2021. To do: 21 1) Add code to cover instantaneous on (DONE) 22 2) Add code to perform RUN mode operation (decrement days and turn on pump when days duration is reached) (DONE, 6/4/21 ) 23 3) Fix Up/DN arrow indicator in lowest menus ( to select DURATION or # of Days) (DONE, 6/10/21) 24 4) Clean up Code - remove commented out code and unneeded monitor code (REV 4) 25 5) Streamline Code - made UP/DN arrow code into a function (DONE 6/23/2021) (REV 5) 26 6) Additional Streamline Code (Go to REV 6 which is work in progress) 27 28 REV 1: 29 May 25, 2021 30 1) Fixed majority of menu operation. Started to add actual pump control. Will do that in Rev 2. 31 32 Feb 21, 2021 33 1) Update Display to show current Settings and inform users how to change 34 35 Nov 9, 2020. This program has been modified to provide control of a plant watering pump. There will be four modes of operation: 36 1) RUN mode where pump operates a 'watering moment' based on day interval and water duration settings (default or user defined) 37 2) INSTANT ON/OFF control to provide for off-schedule immediate turn on of pump 38 3) DAY INTERVAL which sets how many days to wait before a 'watering moment' is initiated. 39 4) WATER DURATION which sets how long water is pumped when a 'watering moment' is reached 40 41EEPROM will be used to store DAYS and WATER DURATION that will be read by Pump Control code 42 43*/ 44// You can have up to 10 menu items in the menuItems[] array below without having to change the base programming at all. Name them however you'd like. 45// Beyond 10 items, you will have to add additional "cases" in the switch/case section of the operateMainMenu() function below. You will also have to 46// add additional void functions (i.e. menuItem11, menuItem12, etc.) to the program. 47// String menuItems[] = {"ITEM 1", "ITEM 2", "ITEM 3", "ITEM 4", "ITEM 5", "ITEM 6"}; // saved from original code 48 49// Main menu variables will be prefixed by 'menu'; Submenu variables will be prefixed by 'submenu' 50 51#include<EEPROM.h> 52 bool Out1 = false; 53 byte Day_countdown = 0; 54 int Duration = 0; 55 int x = 1; 56 int a; 57 int b; 58 59String menuItems[] = {"chg MODE(RN/PRG)", "INSTANT BURST", "chg DAYS INTRVL", "Chg WATER DUR"}; 60String submenuItems_MODE[] = {"RUN <sel>", "PROG <sel)"}; 61String submenuItems_INSTANT[] = {"OFF", "ON"}; 62String submenuItems_DAYSINTERVAL[] = {"2 Days", "3 Days", "4 Days", "5 Days", "6 Days", "7 Days", "8 Days", "9 Days", "10 Days", "11 Days", "12 Days", "13 Days", "14 Days"}; 63 // 13 entries [0-12]; so entry # and Days #s are offset by 2 (0-12 entries and 2-14 days) 64String submenuItems_WATERDURATION[] = {"4secs", "6secs", "8secs", "10secs"}; // 4 entries [0-3]. So entry # and Secs # are offset by TBD (0-3 entries and 4-10 secs) 65 66// Navigation button variables 67int readKey; 68int button; 69 70// Main Menu control variables: 71int menuPage = 0; 72int maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) / 2) + .5); 73int cursorPosition = 0; 74int mode = 0; // Operating mode: 0 = Run; 1 = Program/change settings; Initialized to RUN for operating case where pump power is controlled by timer. 75int Day_Interval; 76int Water_duration; 77 78// Sub Menu control variables: 79int submenuPage = 0; 80int submaxMenuPages = round(((sizeof(submenuItems_DAYSINTERVAL) / sizeof(String)) / 2) + .5); 81 82// Creates 3 custom characters for the menu display 83byte downArrow[8] = { 84 0b00100, // * 85 0b00100, // * 86 0b00100, // * 87 0b00100, // * 88 0b00100, // * 89 0b10101, // * * * 90 0b01110, // *** 91 0b00100 // * 92}; 93 94byte upArrow[8] = { 95 0b00100, // * 96 0b01110, // *** 97 0b10101, // * * * 98 0b00100, // * 99 0b00100, // * 100 0b00100, // * 101 0b00100, // * 102 0b00100 // * 103}; 104 105byte menuCursor[8] = { 106 B01000, // * 107 B00100, // * 108 B00010, // * 109 B00001, // * 110 B00010, // * 111 B00100, // * 112 B01000, // * 113 B00000 // 114}; 115 116#include <Wire.h> 117#include <LiquidCrystal.h> 118 119// Setting the LCD shields pins 120LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 121 122void setup() { 123 124// initialize digital pins 125 pinMode(LED_BUILTIN, OUTPUT); // Indicator 126 pinMode(12, OUTPUT); // sets the digital pin 12 as output to control Pump transistor switch 127 digitalWrite(12, LOW); // set Transistor control low to turn off water 128 digitalWrite(LED_BUILTIN, LOW); // turn the LED off (INDICATOR) 129 130 // Initializes serial communication 131 Serial.begin(9600); 132 Serial.print(F("1) Hello World ")); // Store string in FLASH instead of RAM to save space 133 134 // Initializes and clears the LCD screen 135 lcd.begin(16, 2); 136 lcd.clear(); 137 138 // Creates the byte for the 3 custom characters 139 lcd.createChar(0, menuCursor); 140 lcd.createChar(1, upArrow); 141 lcd.createChar(2, downArrow); 142 143 lcd.clear(); 144 lcd.setCursor(0, 0); 145 lcd.write("The Programmable"); 146 lcd.setCursor(0, 1); 147 lcd.write("Pump Control-R5"); 148 delay (2000); 149 150 lcd.clear(); 151 lcd.setCursor(0, 0); 152 lcd.write("Pumps X secs of"); 153 lcd.setCursor(0, 1); 154 lcd.write("H20 per Y days"); 155 delay (2000); 156} 157 158void loop() { // Modify this to show display 159 160 // The following code will check to see if it's time to run the pump based on countdown of the Days. If not, proceed to display 161 // Turn on LED to indicate start 162 Serial.print(F(" Turn on Circuit, (flash LED, half-sec on, half-sec off) ")); // Store string in FLASH instead of RAM to save space 163 Serial.println(); // End the line 164 do { 165 digitalWrite(LED_BUILTIN, Out1); // turn the LED on (HIGH is the voltage level) (INDICATOR) 166 delay(500); // wait for 0.5 seconds 167 Out1 = !Out1; 168 digitalWrite(LED_BUILTIN, Out1); // turn the LED off by making the voltage LOW 169 delay(500); // wait for 0.5 seconds 170 Out1 = !Out1; 171 x = x-1; 172 } while (x > 0); 173 174// ----- Initial Test Done 175 176 Day_countdown = EEPROM.read(1); // Read Countdown from EEPROM location 1 177 unsigned int var3 = Day_countdown; 178 var3 = var3 - 1 ; // Decrement 179 Day_countdown = byte(var3); 180 Serial.print(F(" NORMAL Operation - Count down days (EEPROM value) = ")); // Store string in FLASH instead of RAM to save space 181 Serial.println(Day_countdown); 182 183 if(Day_countdown == 0) { 184 185 // the following turns on the transistor switch to pump water; 186 lcd.clear(); 187 lcd.setCursor(0, 0); 188 lcd.write("PUMP ON"); 189 digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) (INDICATOR) 190 digitalWrite(12, HIGH); // set transistor control output HIGH to turn on pump 191 unsigned int var4 = EEPROM.read(2); 192 var4 = var4*1000; // convert to seconds 193 delay(var4); // Pump on time value 194 Serial.print(F(" PUMP OFF")); // Store string in FLASH instead of RAM to save space 195 Serial.println(); // End the line 196 digitalWrite(LED_BUILTIN, LOW); // turn the LED off (INDICATOR) 197 digitalWrite(12, LOW); // set transistor control output LOW to turn pump off 198 Day_countdown = EEPROM.read(0); 199 } 200 else { 201 lcd.clear(); 202 lcd.setCursor(0, 0); 203 lcd.print("Days until next"); 204 lcd.setCursor(0, 1); 205 lcd.print("watering:"); 206 lcd.setCursor(11, 1); 207 lcd.print(Day_countdown); 208 delay (2000); 209 } 210 EEPROM.write(1, Day_countdown); // Write new value to EEPROM location 1 211 212 213 /* The following will toggle between two display messages while pump is in run/on mode 214 * a) Displaying the Current Settings for a few seconds 215 * b) Alerting the user to hit the <SELECT> button to Change Settings 216 * 217 * The User can hit the SELECT button anytime when the display is toggling between the two messages 218 */ 219 220 while (mode == 0) { 221 // Display "Current Settings" 222 lcd.clear(); 223 lcd.setCursor(0, 0); 224 lcd.print("Pump settings:"); 225 lcd.setCursor(0, 1); 226 lcd.print(EEPROM.read(2)); // Display WATER DURATION value 227 lcd.setCursor(2,1); 228 lcd.print("s/"); 229 lcd.setCursor(4,1); 230 lcd.print( EEPROM.read(0)); // Display DAYS INTERVAL value 231 lcd.setCursor(6,1); 232 lcd.print("days & "); 233 lcd.setCursor(13,1); 234// if(Mode == 0) { 235 lcd.print("ON "); 236// } 237// lcd.print("OFF"); 238 delay (50); 239 240 // Wait for <select> button to be pushed 241 for (int i = 0; i <= 15000; i++) { 242 button = evaluateButton(readKey); 243 switch (button) { 244 case 5: // SELECT button depressed; proceed to the program mode 245 button = 0; 246 Serial.print(F("2a) SELECT button hit to 'Change Settings' ")); // Store string in FLASH instead of RAM to save space 247 int menuPage = 0; 248 mainMenuDraw(); 249 delay(100); 250 operateMainMenu(); // This enables user to go into PROGRAM mode in order to change settings or to run pump immediately 251 mainMenuDraw(); 252 delay (100); 253 break; 254 } 255 } 256 257 // Display "Instruction for user to hit SELECT button to enable changing settings". 258 lcd.clear(); 259 lcd.setCursor(0, 0); 260 lcd.print("Hit <select> to"); 261 lcd.setCursor(0, 1); 262 lcd.print("change settings"); // Display value 263 delay (50); 264 265 // Wait for <select> button to be pushed 266 for (int i = 0; i <= 10000; i++) { 267 button = evaluateButton(readKey); 268 switch (button) { 269 case 5: // SELECT button depressed; proceed to the program mode 270 button = 0; 271 Serial.print(F("2b) SELECT button hit to 'Change Settings' ")); 272 int menuPage = 0; 273 mainMenuDraw(); 274 delay(100); 275 operateMainMenu(); // This enables user to go into PROGRAM mode in order to change settings or to run pump immediately 276 mainMenuDraw(); 277 delay (100); 278 break; 279 } 280 } 281} 282} 283 284 285// This function, for the MAIN menu, will generate the 2 menu items that can fit on the screen. They will change as you scroll through your menu. Up and down arrows will indicate your current menu position. 286void mainMenuDraw() { 287 Serial.print("menuPage= "); 288 Serial.println(menuPage); 289 lcd.clear(); 290 lcd.setCursor(0, 0); 291 lcd.print(menuItems[menuPage]); 292 lcd.setCursor(0,1); 293 if (mode == 0) { 294 lcd.print("Use SEL & U/D"); 295 } 296 else { 297 if (menuPage == 1) { 298 lcd.print("Use U/D or SEL"); 299 } else 300 { 301 lcd.print("Use U/D & SEL"); 302 } 303 } 304 305 UpDnArrowDraw(menuPage, maxMenuPages); 306 307} 308 309 310void operateMainMenu() { 311 int activeButton = 0; 312 while (activeButton == 0) { 313 int button; 314 int button_temp; // test 315 readKey = analogRead(0); 316 while (readKey > 790) { // Wait here if no buttons pushed; read value should be max (~1023) 317 delay(50); 318 readKey = analogRead(0); 319 } 320 button = evaluateButton(readKey); 321 switch (button) { 322 case 0: // NO button returns as 0 there is no action taken 323 break; 324 case 1: // RIGHT button returns as 0 there is no action taken 325 break; 326 327 case 2: // UP button 328 button = 0; 329 if(mode==1) { 330 cursorPosition = cursorPosition - 1; 331 cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1)); 332 menuPage = menuPage - 1; 333 menuPage = constrain(menuPage, 0, maxMenuPages); 334 mainMenuDraw(); 335 } 336 break; 337 338 339 case 3: // DOWN Key 340 button = 0; 341 if(mode==1) { 342 cursorPosition = cursorPosition + 1; 343 cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1)); 344 menuPage = menuPage + 1; 345 menuPage = constrain(menuPage, 0, maxMenuPages); 346 mainMenuDraw(); 347 } 348 break; 349 350 case 4: // LEFT button returns as 0 there is no action taken 351 button = 0; 352 activeButton == 1; 353 break; 354 355 case 5: // SELECT button will proceed to the lower level sub-menu 356 button = 0; 357 switch (cursorPosition) { // The case that is selected here is dependent on which menu page you are on and where the cursor is. 358 case 0: 359 menuItem1(); // Submenu for MODE 360 if (mode == 0) { 361 activeButton = 1; 362 } 363 break; 364 case 1: 365 if (mode == 1) { 366 menuItem2(); // submenuItems_INSTANT 367 } 368 break; 369 case 2: 370 if (mode == 1) { 371 menuItem3(); // Submenu for DAYS INTERVAL 372 } 373 break; 374 case 3: 375 if (mode == 1) { 376 menuItem4(); // Submenu for WATER DURATION 377 } 378 break; 379 case 4: 380// menuItem5(); // Spare 381 break; 382 } 383 mainMenuDraw(); 384 break; 385 386 } 387 } 388} 389 390// This function is called whenever a button is pressed and determines which of the 5 buttons was pressed, based on the voltage drop across the buttons all hooked up to A0. 391int evaluateButton(int x) { 392 int result = 0; 393 x = analogRead(0); 394 if (x < 790) { 395 delay(100); 396 x = analogRead(0); // Performs glitch filtering 397 } 398 if (x < 50) { 399 result = 1; // right button 400 } else if (x < 195) { 401 result = 2; // up button 402 } else if (x < 380) { 403 result = 3; // down button 404 } else if (x < 555) { // Added this temporarily to implement 'Select' Fct. Swap this with result 4 later to maintain order. 405 result = 4; // Left button 406 } else if (x < 790) { 407 result = 5; // Select button 408 } 409 return result; 410} 411 412// If there are common usage instructions on more than 1 of your menu items you can call this function from the sub 413// menus to make things a little more simplified. If you don't have common instructions or verbage on multiple menus 414// I would just delete this void. You must also delete the drawInstructions()function calls from your sub menu functions. 415/* 416void drawInstructions() { 417 lcd.setCursor(0, 1); // Set cursor to the bottom line 418 lcd.print("Use "); 419 lcd.print(byte(1)); // Up arrow 420 lcd.print("/"); 421 lcd.print(byte(2)); // Down arrow 422 lcd.print(" buttons"); 423} 424 425*/ 426 427 428 429void menuItem1() { // Function executes when you select the 1st item from main menu 430 int activeButton = 0; 431 submaxMenuPages = (sizeof(submenuItems_MODE)/ sizeof(String)-1); 432 submenuPage = 0; // set to run mode 433 readKey = analogRead(0); // Wait until last push button operation (likely SELECT) is over so that submenu functions can take over 434 while (readKey < 790) { 435 delay(10); 436 readKey = analogRead(0); 437 } 438 lcd.clear(); 439 lcd.setCursor(0, 0); 440 lcd.print("UP/DN to chg"); 441 lcd.setCursor(0, 1); 442 lcd.print(submenuItems_MODE[submenuPage]); 443 UpDnArrowDraw(submenuPage, submaxMenuPages); 444 while (activeButton == 0) { 445 int button; // Debounce 446 readKey = analogRead(0); 447 while (readKey > 790) { // Wait here if no buttons pushed; read value should be max (~1023) 448 delay(10); 449 readKey = analogRead(0); 450 } 451 button = evaluateButton(readKey); 452 switch (button) { 453 case 0: // When button returns as 0 there is no action taken 454 break; 455 456 case 1: // When button returns as 1 there is no action taken 457 break; 458 459 case 2: // Up arrow control 460 button = 0; 461 if (submenuPage == 0) { 462 break; 463 } 464 submenuPage = submenuPage - 1; 465 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 466 UpDnArrowDraw(submenuPage, submaxMenuPages); 467 lcd.clear(); 468 lcd.setCursor(0, 0); 469 lcd.print("UP/DN to chg"); 470 lcd.setCursor(0, 1); 471 lcd.print(submenuItems_MODE[submenuPage]); // Display value 472 Serial.println("submenuPage = "); 473 Serial.println(submenuPage); 474 Serial.println("submaxMenuPages = "); 475 Serial.println(submaxMenuPages); 476 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the UP/DN arrows 477 break; // Exit Case 2 478 479 case 3: // Down arrow control 480 button = 0; 481 if (submenuPage == submaxMenuPages) { 482 break; 483 } 484 submenuPage = submenuPage + 1; 485 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 486 UpDnArrowDraw(submenuPage, submaxMenuPages); 487 lcd.clear(); 488 lcd.setCursor(0, 0); 489 lcd.print("UP/DN to chg"); 490 lcd.setCursor(0, 1); 491 lcd.print(submenuItems_MODE[submenuPage]); 492 Serial.println("submenuPage = "); 493 Serial.println(submenuPage); 494 Serial.println("submaxMenuPages = "); 495 Serial.println(submaxMenuPages); 496 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the UP/DN arrows 497 break; // Exit Case 3 498 499 case 4: // This case will execute if the "back" button (e.g. LEFT) is pressed to go back to the Main menu 500 button = 0; 501 activeButton = 1; 502 break; 503 504 case 5: // This case will execute if the "select" button is pressed. CODE NEEDED HERE 505 button = 0; 506 lcd.setCursor(5, 1); 507 lcd.print(" selected"); //Indicates that current displayed value has been selected 508 mode = submenuPage; // Mode controls 509 Serial.print(F("Selected Mode (0=RUN; 1=Program)= ")); // Store string in FLASH instead of RAM to save space 510 Serial.println(mode); 511 delay(2000); // Wait 2 seconds 512 activeButton = 1; 513 break; 514 } 515 } 516} 517 518void menuItem2() { // Enables immediate Turn-on of water for duration based on value stored in EEPROM. No menu selection here. 519 520 int duration; 521 duration = EEPROM.read(2); // Retrieve duration value 522 duration = duration*1000; // convert to ms 523 524 delay(1000); // wait for 1 second 525 Serial.print(" PUMP ON"); 526 Serial.println(); // End the line 527 digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) (INDICATOR) 528 digitalWrite(12, HIGH); // set transistor control output HIGH to turn on pump 529 lcd.clear(); 530 lcd.setCursor(1, 0); 531 lcd.print("PUMP running"); 532 delay(duration); // wait for 10 seconds 533 Serial.print(" PUMP OFF"); 534 Serial.println(); // End the line 535 digitalWrite(LED_BUILTIN, LOW); // turn the LED off (INDICATOR) 536 digitalWrite(12, LOW); // set transistor control output LOW to turn pump off 537 lcd.clear(); 538 lcd.setCursor(1, 0); 539 lcd.print("PUMP done"); 540 delay(2000); 541 } 542 543void menuItem3() // Function executes to set the # of Days between turn-on of water (DAYS INTERVAL) 544 { 545 int activeButton = 0; 546 submenuPage = EEPROM.read(0)-2; // remove offset due to values starting with 2 Days 547 submaxMenuPages = (sizeof(submenuItems_DAYSINTERVAL)/ sizeof(String)-1); 548 Serial.print (F("sizeof(submenuItems_DAYSINTERVAL) = ")); 549 Serial.println (sizeof(submenuItems_DAYSINTERVAL)); 550 Serial.print (F("sizeof(String) is ")); 551 Serial.println (sizeof(String)); 552 Serial.println("submaxMenuPages = "); 553 Serial.println(submaxMenuPages); 554 readKey = analogRead(0); // Wait until last push button operation (likely SELECT) is over so that submenu functions can take over 555 while (readKey < 790) { 556 delay(10); 557 readKey = analogRead(0); 558 } 559 560 Serial.print("submaxMenuPages= "); 561 Serial.print(submaxMenuPages); 562 Serial.println(" entering MenuItem3"); 563 lcd.clear(); 564 lcd.setCursor(1, 0); 565 lcd.print("UP/DN to change"); 566 lcd.setCursor(0, 1); 567 lcd.print(EEPROM.read(0)); 568 lcd.print(" days(curr)"); 569 UpDnArrowDraw(submenuPage, submaxMenuPages); 570 while (activeButton == 0) { 571 button; // Debounce 572 readKey = analogRead(0); 573 while (readKey > 790) { // Wait here if no buttons pushed; read value should be max (~1023) 574 delay(10); 575 readKey = analogRead(0); 576 } 577 578 button = evaluateButton(readKey); 579 switch (button) { 580 case 0: // When button returns as 0 there is no action taken 581 break; 582 583 case 1: // When button returns as 1 there is no action taken 584 break; 585 586 case 2: // Up arrow control 587 button = 0; 588 if (submenuPage == 0) { 589 UpDnArrowDraw(submenuPage, submaxMenuPages); 590 break; 591 } 592 submenuPage = submenuPage - 1; 593 submenuPage = constrain(submenuPage, 0, submaxMenuPages+1); 594 lcd.clear(); 595 lcd.setCursor(0, 0); 596 lcd.print("Pick 2-14 Days"); 597 lcd.setCursor(0, 1); 598 lcd.print(submenuItems_DAYSINTERVAL[submenuPage]); // TEST FOR NOW TO GET BASIC FUNCTIONALITY WITH ONE SUBMENU - MIGHT NEED ARRAY LATER 599 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the UP/DN arrows 600 601 break; // Exit Case 2 602 603 case 3: // Down arrow control 604 button = 0; 605 if (submenuPage == submaxMenuPages) { 606 UpDnArrowDraw(submenuPage, submaxMenuPages); 607 break; 608 } 609 submenuPage = submenuPage + 1; 610 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 611 lcd.clear(); 612 lcd.setCursor(0, 0); 613 lcd.print("Pick 2-14 Days"); 614 lcd.setCursor(0, 1); 615 lcd.print(submenuItems_DAYSINTERVAL[submenuPage]); // TEST FOR NOW TO GET BASIC FUNCTIONALITY WITH ONE SUBMENU - MIGHT NEED ARRAY LATER 616 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the UP/DN arrows 617 break; // Exit Case 3 618 619 case 4: // This case will execute if the "back" button (e.g. LEFT) is pressed to go back to the Main menu 620 button = 0; 621 activeButton = 1; 622 break; 623 624 case 5: // This case will execute if the "select" button is pressed. 625 button = 0; 626 byte Day; 627 lcd.setCursor(8, 1); 628 lcd.print("selected"); 629 Day_Interval = submenuPage + 2; // Offset because days options starts at 2 instead of 0 630 EEPROM.write(0, Day_Interval); // EEPROM Address 0 holds DAY INTERVAL value 631 EEPROM.write(1, Day_Interval); // EEPROM Address 1 holds Count down of days until next watering 632 Day_countdown = EEPROM.read(1); 633 delay(2000); // Wait 2 seconds 634 activeButton = 1; 635 break; 636 } 637 } 638} 639 640void menuItem4() { // To set duration of pump turn-on (e.g WATER DURATION), in seconds 641 int activeButton = 0; 642 submenuPage = (EEPROM.read(2) - 4)/2; 643 submaxMenuPages = (sizeof(submenuItems_WATERDURATION)/ sizeof(String)-1); 644 readKey = analogRead(0); // Wait until last push button operation (likely SELECT) is over so that submenu functions can take over 645 while (readKey < 790) { 646 delay(10); 647 readKey = analogRead(0); 648 } 649 650 lcd.clear(); 651 lcd.setCursor(1, 0); 652 lcd.print("UP/DN to change"); 653 lcd.setCursor(0, 1); 654 lcd.print(EEPROM.read(2)); 655 lcd.print(" secs(curr)"); 656 UpDnArrowDraw(submenuPage, submaxMenuPages); 657 while (activeButton == 0) { 658 button; // Debounce 659 readKey = analogRead(0); 660 while (readKey > 790) { // Wait here if no buttons pushed; read value should be max (~1023) 661 delay(10); 662 readKey = analogRead(0); 663 } 664 665 button = evaluateButton(readKey); 666 switch (button) { 667 case 0: // When button returns as 0 there is no action taken 668 break; 669 670 case 1: // When button returns as 1 there is no action taken 671 break; 672 673 case 2: // Up arrow control 674 button = 0; 675 if (submenuPage == 0) { 676 UpDnArrowDraw(submenuPage, submaxMenuPages); 677 break; 678 } 679 submenuPage = submenuPage - 1; 680 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 681 Serial.print("Days Value from UP key (submenuPage)= "); 682 Serial.println(submenuPage); 683 lcd.clear(); 684 lcd.setCursor(0, 0); 685 lcd.print("Pick 4-10 secs"); 686 lcd.setCursor(0, 1); 687 lcd.print(submenuItems_WATERDURATION[submenuPage]); // TEST FOR NOW TO GET BASIC FUNCTIONALITY WITH ONE SUBMENU - MIGHT NEED ARRAY LATER 688 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the UP/DN arrows 689 break; // Exit Case 2 690 691 case 3: // Down arrow control 692 button = 0; 693 if (submenuPage == submaxMenuPages) { 694 UpDnArrowDraw(submenuPage, submaxMenuPages); 695 break; 696 } 697 submenuPage = submenuPage + 1; 698 submenuPage = constrain(submenuPage, 0, submaxMenuPages); 699 lcd.clear(); 700 lcd.setCursor(0, 0); 701 lcd.print("Pick 4-10 secs"); 702 lcd.setCursor(0, 1); 703 lcd.print(submenuItems_WATERDURATION[submenuPage]); 704 Serial.print(F("Days Value from DN key (submenuPage)= ")); // Test 705 Serial.println(submenuPage); 706 UpDnArrowDraw(submenuPage, submaxMenuPages); // This now just draws the UP/DN arrows 707 708 break; // Exit Case 3 709 710 case 4: // This case will execute if the "back" button (e.g. LEFT) is pressed to go back to the Main menu 711 button = 0; 712 activeButton = 1; 713 break; 714 715 case 5: // This case will execute if the "select" button is pressed. CODE NEEDED HERE 716 button = 0; 717 byte Water; 718 lcd.setCursor(8, 1); 719 lcd.print("selected"); 720 Water_duration = (submenuPage +2) * 2; // Adjusted for available options 721 EEPROM.write(2, Water_duration); // EEPROM Address 1 holds WATER DURATION value 722 Water = EEPROM.read(2); 723 Serial.print(F("Selected Water Duration(Byte)after WRITE & READ to EEPROM= ")); 724 Serial.println(Water); 725 delay(2000); // Wait 2 seconds 726 activeButton = 1; 727 break; 728 } 729 } 730} 731 732 733 734// This function will generate the arrows that indicate which UP/DN buttons to press. 735 736 int UpDnArrowDraw(int a,int b) { 737 if (a == 0) { // IF for Up/Dn arrows 738 lcd.setCursor(15, 1); 739 lcd.write(byte(2)); // Display DN arrow only ( BYTE 1 is UP arrow; BYTE 2 is DN arrow) 740 lcd.setCursor(15, 0); 741 lcd.print(" "); 742 } 743 else if (a > 0 and a < b ) { 744 lcd.setCursor(15, 1); 745 lcd.write(byte(2)); 746 lcd.setCursor(15, 0); 747 lcd.write(byte(1)); // Display both UP and DN arrow 748 } 749 else if (a == b) { 750 lcd.setCursor(15, 0); 751 lcd.write(byte(1)); // Display UP arrow only 752 lcd.setCursor(15, 1); 753 lcd.print(" "); 754 } 755} 756
Downloadable files
Interface circuit schematic
Autocad Eagle drawing
Interface circuit schematic
Interface circuit schematic
Autocad Eagle drawing
Interface circuit schematic
Documentation
water_pump_controller_lid_-_rev_3_GXz1vy35ra.stl
STL file for printing lid
water_pump_controller_lid_-_rev_3_GXz1vy35ra.stl
Enclosure and Lid pdf dwg
For information purposes
Enclosure and Lid pdf dwg
Enclosure and Lid pdf dwg
For information purposes
Enclosure and Lid pdf dwg
water_pump_controller_enclosure_-_rev_3_UEjPPcO26g.stl
STL file for printing enclosure
water_pump_controller_enclosure_-_rev_3_UEjPPcO26g.stl
water_pump_controller_lid_-_rev_3_GXz1vy35ra.stl
STL file for printing lid
water_pump_controller_lid_-_rev_3_GXz1vy35ra.stl
Comments
Only logged in users can leave comments