123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- /*
- Arduino 5 Button LCD Display Menu
- By Joe (grugly at sdf dot org)
- https://notabug.org/grugly/Arduino_5_Button_16x2_LCD_Menu
- */
- // this needs to be here for some reason
- #ifdef __cplusplus
- extern "C" {
- #endif
- // Include LCD and sensor libraries
- #include "LiquidCrystal.h"
- #include "DHT.h"
- // Pin assignment
- #define LCD_PIN_1 8
- #define LCD_PIN_2 9
- #define LCD_PIN_3 4
- #define LCD_PIN_4 5
- #define LCD_PIN_5 6
- #define LCD_PIN_6 7
- #define BACKLIGHT_PIN 10
- #define BUTTON_PIN A0
- #define FAN_PIN 11
- #define LAMP_PIN 12
- #define SENSOR_PIN 13
- // Button reference voltage
- #define BUTTON_NULL 1023
- #define BUTTON_SELECT 639
- #define BUTTON_LEFT 408
- #define BUTTON_DOWN 255
- #define BUTTON_UP 98
- #define BUTTON_RIGHT 0
- // Init LCD and sensor
- LiquidCrystal lcd(LCD_PIN_1, LCD_PIN_2, LCD_PIN_3,
- LCD_PIN_4, LCD_PIN_5, LCD_PIN_6);
- DHT dht;
- // Defaults
- int backlight = 0; // Holds backlight status
- int brightness = 128; // Brightnes of backlight
- int sleep_time = 10000; // Time before screen turns off
- int refresh_delay = 5000; // Info display refresh delay
- int cycle_delay = 2000; // Work cycle refresh delay
- int temp_target = 35; // Optimum temp for our enclosure
- int temp_variance = 5; // How far off the temp range before we correct
- // Variables to hold current data
- int temp = 0; // Temperature
- int temp_high = 0; // Highest temp
- int temp_low = 99; // Lowest temp
- int humid = 0; // Humidity
- int humid_high = 0; // Highest humidity
- int humid_low = 99; // Lowest humidity
- String outstr = ""; // String for LCD output lines
- int fan_state = 0; // 1 when fan is running
- int lamp_state = 0; // 1 when lamp is on
- unsigned long fan_time, fan_duration, fan_average,
- lamp_time, lamp_duration, lamp_average;
- int fan_times, lamp_times = 0;
- // Timers
- unsigned long run_duration, wake_time, wake_duration,
- last_refresh, last_cycle = 0;
- // Displayed text for each menu item (16 char max)
- String menu_list[] = {
- "Target Temp",
- "Temp Variance",
- "Brightness",
- "Standby Delay",
- "Uptime",
- "Fan Time",
- "Fan Average",
- "Lamp Time",
- "Lamp Average"
- };
- // Count and store number of menu items
- int items = round((sizeof(menu_list) / sizeof(String) - 1));
- int item = 0; // Menu position
- int menu_displayed = 0; // Menu displayed if 1
- // Custom chars for LCD for welcome screen
- byte chicken0[8] = { // High bounce
- B00000,
- B00000,
- B01100,
- B11100,
- B01111,
- B01110,
- B00100,
- B00000
- };
- byte chicken1[8] = { // Low bounce
- B00000,
- B00000,
- B00000,
- B01100,
- B11100,
- B01111,
- B01110,
- B00000
- };
- byte chicken2[8] = { // Squatting to lay
- B00000,
- B00000,
- B00000,
- B00000,
- B01100,
- B11100,
- B01111,
- B01110
- };
- byte chicken3[8] = { // Laying the egg
- B00000,
- B00000,
- B01000,
- B00001,
- B01100,
- B11100,
- B01111,
- B01110
- };
- byte egg[8] = { // The egg
- B00000,
- B00000,
- B00000,
- B00000,
- B00100,
- B01110,
- B01110,
- B00100
- };
- // Welcome message with a chicken laying an egg
- void welcome() {
- lcd.clear();
- lcd.setCursor(0, 0);
- lcd.print("Chicken Brooder");
- int i;
- int anichar = 0; // Make chicken bounce
- for (i = 16; i >= 0; i--) {
- lcd.setCursor((i + 1), 1);
- lcd.print(" ");
- lcd.setCursor(i, 1);
- lcd.write(anichar);
- if (i == 8) { // Lay egg at char 8
- lcd.setCursor(i, 1);
- lcd.write(2);
- delay(500);
- lcd.setCursor(i, 1);
- lcd.write(3);
- delay(500);
- lcd.setCursor((i - 1), 1);
- lcd.write(anichar);
- lcd.write(4);
- i--;
- }
- // Bounce chicken
- if (anichar == 0) { anichar++; }
- else { anichar--; }
- delay(500);
- }
- }
- void readTemp() {
- // Read temperature
- temp = round(dht.getTemperature());
- if (temp > temp_high) { temp_high = temp; }
- if (temp < temp_low) { temp_low = temp; }
- return;
- }
- void readHumid() {
- // Read humidity
- humid = round(dht.getHumidity());
- if (humid > humid_high) { humid_high = humid; }
- if (humid < humid_low) { humid_low = humid; }
- return;
- }
- void printTemp() {
- // Display temperature
- lcd.clear();
- lcd.setCursor(0, 0);
- outstr = "Temperature: " + String(temp) + 'C';
- lcd.print(outstr);
- lcd.setCursor(0, 1);
- outstr = "L: " + String(temp_low) + "C H: " + String(temp_high) + "C";
- lcd.print(outstr);
- return;
- }
- void printHumid() {
- // Display humidity
- lcd.clear();
- lcd.setCursor(0, 0);
- outstr = "Humidity: " + String(humid) + '%';
- lcd.print(outstr);
- lcd.setCursor(0, 1);
- outstr = "L: " + String(humid_low) + "% H: " + String(humid_high) + "%";
- lcd.print(outstr);
- return;
- }
- // Display statistics
- int disp_sw = 0;
- void displayInfo() {
- if ((run_duration - last_refresh) >= refresh_delay) {
- if (disp_sw == 0) {
- printTemp();
- disp_sw++;
- }
- else {
- printHumid();
- disp_sw--;
- }
- // Reset timer
- last_refresh = millis();
- }
- }
- // Read sensors, flick switches, etc..
- void doWork() {
- if ((run_duration - last_cycle) >= cycle_delay) {
- // Get the data
- readTemp();
- readHumid();
- // Fan control
- if (temp >= (temp_target + temp_variance) && fan_state == 0) {
- digitalWrite(FAN_PIN, HIGH);
- fan_state = 1;
- fan_time = millis();
- fan_times++;
- }
- if (temp <= temp_target && fan_state == 1) {
- digitalWrite(FAN_PIN, LOW);
- fan_state = 0;
- fan_duration += (run_duration - fan_time);
- fan_average = (fan_duration / fan_times);
- }
- // Lamp control
- if (temp <= (temp_target - temp_variance) && lamp_state == 0) {
- digitalWrite(LAMP_PIN, HIGH);
- lamp_state = 1;
- lamp_time = millis();
- lamp_times++;
- }
- if (temp >= temp_target && lamp_state == 1) {
- digitalWrite(LAMP_PIN, LOW);
- lamp_state = 0;
- lamp_duration += (run_duration - lamp_time);
- lamp_average = (lamp_duration / lamp_times);
- }
- }
- }
- // Read a button press and return number of button pressed
- int readButton() {
- int button_voltage = analogRead(BUTTON_PIN);
- int button_pressed = 0;
- if (button_voltage <= (BUTTON_NULL + 10)
- && button_voltage >= (BUTTON_NULL - 10)) {
- button_pressed = 0;
- }
- if (button_voltage <= (BUTTON_SELECT + 10)
- && button_voltage >= (BUTTON_SELECT - 10)) {
- button_pressed = 1;
- }
- if (button_voltage <= (BUTTON_LEFT + 10)
- && button_voltage >= (BUTTON_LEFT - 10)) {
- button_pressed = 2;
- }
- if (button_voltage <= (BUTTON_DOWN + 10)
- && button_voltage >= (BUTTON_DOWN - 10)) {
- button_pressed = 3;
- }
- if (button_voltage <= (BUTTON_UP + 10)
- && button_voltage >= (BUTTON_UP - 10)) {
- button_pressed = 4;
- }
- if (button_voltage <= (BUTTON_RIGHT + 10)
- && button_voltage >= (BUTTON_RIGHT - 10)) {
- button_pressed = 5;
- }
- // Wait until button is released before we return
- while (button_voltage <= (BUTTON_NULL - 10)) {
- button_voltage = analogRead(BUTTON_PIN);
- delay(100);
- }
- if (button_pressed > 0) {
- wake_time = millis(); // Reset the backlight timer
- }
- return button_pressed;
- }
- // Display the menu item on the LCD
- void displayItem(int item) {
- // Clear the screen and print the item title
- lcd.clear();
- lcd.print(String(menu_list[item]));
- // Display the current value in a readable form
- // Compiler says these need defining outside the case statement!
- int percent, sec;
- unsigned long d, h, m, s;
- switch (item) {
- case 0: // Target temperature
- lcd.setCursor(0, 1);
- lcd.print(String(temp_target) + "C");
- break;
- case 1: // Temperature variance
- lcd.setCursor(0, 1);
- lcd.print(String(temp_variance) + "C");
- break;
- case 2: // Brightness is displayed as a percentage
- // We don't want to turn the screen completely off
- // and a value of 256 will overflow it so we trim
- // the values. I like the result.
- percent = round(((brightness - 16) / 224.0) * 100);
- lcd.setCursor(0, 1);
- lcd.print(String(percent) + "%");
- break;
- case 3: // Stand-by delay is shown in seconds
- if (sleep_time < 21000) {
- sec = sleep_time / 1000;
- lcd.setCursor(0, 1);
- lcd.print(String(sec) + "sec");
- }
- else {
- lcd.setCursor(0, 1);
- lcd.print("OFF");
- }
- break;
- case 4: // Display uptime
- s = (run_duration / 1000) % 60;
- m = (run_duration / 60000) % 60;
- h = (run_duration / 3600000) % 24;
- d = (run_duration / 86400000) % 12;
- lcd.setCursor(0, 1);
- lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
- break;
- case 5: // Display fan time
- s = (fan_duration / 1000) % 60;
- m = (fan_duration / 60000) % 60;
- h = (fan_duration / 3600000) % 24;
- d = (fan_duration / 86400000) % 12;
- lcd.setCursor(0, 1);
- lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
- break;
- case 6: // Display fan average time
- s = (fan_average / 1000) % 60;
- m = (fan_average / 60000) % 60;
- h = (fan_average / 3600000) % 24;
- d = (fan_average / 86400000) % 12;
- lcd.setCursor(0, 1);
- lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
- break;
- case 7: // Display lamp time
- s = (lamp_duration / 1000) % 60;
- m = (lamp_duration / 60000) % 60;
- h = (lamp_duration / 3600000) % 24;
- d = (lamp_duration / 86400000) % 12;
- lcd.setCursor(0, 1);
- lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
- break;
- case 8: // Display lamp average time
- s = (lamp_average / 1000) % 60;
- m = (lamp_average / 60000) % 60;
- h = (lamp_average / 3600000) % 24;
- d = (lamp_average / 86400000) % 12;
- lcd.setCursor(0, 1);
- lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
- break;
- }
- }
- // Change values of menu items
- // Action is 0 for decreasing (left button) and
- // 1 for increasing (right button)
- void updateItem(int item, int action) {
- switch (item) {
- case 0: // Target Temperature
- // Minimum temperature is 20C
- if (action == 0 && temp_target > 20) {
- temp_target--;
- }
- // Max temperature is 40C
- else if (action == 1 && temp_target < 40) {
- temp_target++;
- }
- break;
- case 1: // Temperature Variance
- // Minimum temperature variance is 1C
- if (action == 0 && temp_variance > 1) {
- temp_variance--;
- }
- // Max temperature variance is 10C
- else if (action == 1 && temp_variance < 10) {
- temp_variance++;
- }
- break;
- case 2: // Brightness
- // don't dim the screen to off
- if (action == 0 && brightness != 16) {
- brightness = brightness - 16;
- analogWrite(BACKLIGHT_PIN, brightness);
- }
- // 256 overflows and the screen is quite bright anyway
- else if (action == 1 && brightness != 240) {
- brightness = brightness + 16;
- analogWrite(BACKLIGHT_PIN, brightness);
- }
- break;
- case 3: // Standby Delay
- // Minimum delay is 2 seconds
- if (action == 0 && sleep_time > 2000) {
- sleep_time -= 1000;
- }
- // Max is 20 seconds (21 is off)
- else if (action == 1 && sleep_time < 21000) {
- sleep_time += 1000;
- }
- break;
- // To display info, we don't need to modify the value
- // so for item[2] (Uptime) we don't take any action.
- }
- displayItem(item);
- }
- void setup(void) {
- // Setup LCD
- lcd.begin(16, 2);
- lcd.noCursor();
- lcd.createChar(0, chicken0); // Create jumping chicken char
- lcd.createChar(1, chicken1); // Create landing chicken char
- lcd.createChar(2, chicken2); // Create squatting chicken char
- lcd.createChar(3, chicken3); // Create laying chicken char
- lcd.createChar(4, egg); // Create egg char
- // Setup sensor
- dht.setup(SENSOR_PIN);
- // Setup button and backlight pins
- pinMode(BUTTON_PIN, INPUT);
- pinMode(BACKLIGHT_PIN, OUTPUT);
- pinMode(LAMP_PIN, OUTPUT);
- pinMode(FAN_PIN, OUTPUT);
- // Turn backlight on
- analogWrite(BACKLIGHT_PIN, brightness);
- backlight = 1;
- welcome();
- wake_time = millis();
- readTemp();
- return;
- }
- void loop(void) {
- // Note how long we have been running for
- run_duration = millis();
- // and how long since the last button push
- wake_duration = run_duration - wake_time;
- // Hide the menu/go to sleep
- if (backlight == 1 && wake_duration >= sleep_time) {
- if (menu_displayed == 1) {
- menu_displayed = 0;
- item = 0;
- lcd.clear();
- wake_time = millis();
- } else if (sleep_time != 21000) {
- digitalWrite(BACKLIGHT_PIN, LOW);
- lcd.clear();
- backlight = 0;
- wake_time = 0;
- }
- }
- // Read a button press
- int button = readButton();
- // Wake up when the select button is pushed
- if (button == 1 && backlight == 0) {
- last_refresh = 0;
- analogWrite(BACKLIGHT_PIN, brightness);
- backlight = 1;
- }
- // Show the menu while awake and the select button is pushed
- else if (button == 1 && menu_displayed == 0) {
- // Set menu flag
- menu_displayed = 1;
- // Display the first item
- displayItem(item);
- }
- else if (button >= 1 && menu_displayed == 1) {
- switch (button) {
- case 1: // Select button pushed
- // Exit menu
- menu_displayed = 0;
- item = 0;
- lcd.clear();
- displayInfo();
- return;
- break;
- case 2: // Left button pushed
- // Decrease value
- updateItem(item, 0);
- break;
- case 3: // Down button pushed
- // Display next menu item
- if (++item > items) { item = 0; }
- displayItem(item);
- break;
- case 4: // Up button pushed
- // Display previous menu item
- if (--item < 0) { item = items; }
- displayItem(item);
- break;
- case 5: // Right button pushed
- // Increase value
- updateItem(item, 1);
- break;
- }
- }
- // Show info while awake
- else if (backlight == 1 && menu_displayed == 0) {
- displayInfo();
- }
- // Always do something usefull..
- doWork();
- return;
- }
- #ifdef __cplusplus
- }
- #endif
|