16x2 LCD Menu for Arduino

Introduction

I just want to share this beautiful LCD Menu implementation that you may use to your next Arduino project. The original code is for PIC microcontrollers, and just ported it for Arduino. My apologies to the original author of the source code, all credits goes to you, Sir.

Video Demonstration

Source Code

  1#include "LiquidCrystal.h"
  2
  3LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
  4
  5// Definitions:
  6#define DISPLAY_ROWS      2
  7#define PER_SPACE         1
  8
  9#define KEYPAD_ROWS       4
 10#define KEYPAD_COLS       4
 11
 12// Pin assignments:
 13#define LCD_BACKLIGHT_PIN 10
 14#define LED_PIN           11
 15
 16unsigned char selected = 1;
 17unsigned char prev_key;
 18byte lcd_brightness_val = 127;
 19byte led_brightness_val = 127; 
 20int led_fadeAmount = 5;
 21bool isLcdBacklit = true;
 22bool isKeyDisabled = false;
 23bool isSetLCDBrightnessMode = false;
 24bool isSetLEDBrightnessMode = false;
 25bool isLEDEnableMode = false;
 26bool led_state = false;
 27bool isBlinkLED = false;
 28bool isFadeLED = false;
 29
 30char buttonPressed = '0';
 31char lastButtonState = '0';
 32unsigned long lastDebounceTime = 0;
 33unsigned long debounceDelay = 100; 
 34
 35// ####################################
 36unsigned long checkButtonTaskTimer = 0;
 37const unsigned long checkButtonTaskInterval = 300;
 38unsigned long updateLCDTaskTimer = 0;
 39const unsigned long updateLCDTaskInterval = 400;
 40
 41unsigned long blinkLEDTaskTimer = 0;
 42const unsigned long blinkLEDTaskInterval = 500;
 43
 44typedef const struct MenuStructure
 45{
 46  const char *text;
 47  unsigned char num_menupoints;
 48  unsigned char up;
 49  unsigned char down;
 50  unsigned char enter;
 51  void (*fp) (void);
 52} MenuEntry;
 53
 54const char menu_000[] = " LCD Menu:    ";    // 0
 55const char menu_001[] = "  LED Control ";    // 1
 56const char menu_002[] = "  LCD Control ";    // 2
 57const char menu_003[] = "  Messages    ";    // 3
 58const char menu_004[] = "  Option4     ";    // 4
 59const char menu_005[] = "  Option5     ";    // 5
 60const char menu_006[] = "  Option6     ";    // 6
 61const char menu_007[] = "  Option7     ";    // 7
 62const char menu_008[] = "  Option8     ";    // 8
 63const char menu_009[] = "  start       ";    // 9
 64
 65const char menu_100[] = " LED Control: ";   // 10
 66const char menu_101[] = "  LED Enable  ";   // 11
 67const char menu_102[] = "  Blinking    ";   // 12
 68const char menu_103[] = "  Brightness  ";   // 13
 69const char menu_104[] = "  Fading      ";   // 14
 70const char menu_105[] = "  Return      ";   // 15
 71
 72const char menu_200[] = " LCD Control: ";   // 16
 73const char menu_201[] = "  Backlight   ";   // 17
 74const char menu_202[] = "  Brightness  ";   // 18
 75const char menu_203[] = "  Return      ";   // 19
 76
 77const char menu_300[] = " Messages:    ";   // 20
 78const char menu_301[] = "  Write       ";   // 21
 79const char menu_302[] = "  Inbox       ";   // 22
 80const char menu_303[] = "  Outbox      ";   // 23
 81const char menu_304[] = "  Return      ";   // 24
 82
 83void start(void);
 84void LED_light();
 85void LED_blink();
 86void LED_brightness();
 87void LED_fade();
 88void LCD_backlight();
 89void LCD_brightness();
 90void show_menu(void);
 91void checkButton();
 92
 93MenuEntry menu[] =
 94{
 95  // text, num_menupoints, up, down, enter, *fp
 96  {menu_000, 10, 0, 0, 0, 0},       // 0
 97  {menu_001, 10, 1, 2, 11, 0},      // 1
 98  {menu_002, 10, 1, 3, 17, 0},      // 2
 99  {menu_003, 10, 2, 4, 21, 0},      // 3
100  {menu_004, 10, 3, 5, 4, 0},       // 4
101  {menu_005, 10, 4, 6, 5, 0},       // 5
102  {menu_006, 10, 5, 7, 6, 0},       // 6
103  {menu_007, 10, 6, 8, 7, 0},       // 7
104  {menu_008, 10, 7, 9, 8, 0},       // 8
105  {menu_009, 10, 8, 9, 1, start},   // 9
106
107  {menu_100, 6, 0, 0, 0, 0},                  // 10
108  {menu_101, 6, 11, 12, 11, LED_light},       // 11
109  {menu_102, 6, 11, 13, 12, LED_blink},       // 12
110  {menu_103, 6, 12, 14, 13, LED_brightness},  // 13
111  {menu_104, 6, 13, 15, 14, LED_fade},        // 14
112  {menu_105, 6, 14, 15, 1, 0},                // 15
113
114  {menu_200, 4, 0, 0, 0, 0},                  // 16
115  {menu_201, 4, 17, 18, 17, LCD_backlight},   // 17
116  {menu_202, 4, 17, 19, 18, LCD_brightness},  // 18
117  {menu_203, 4, 18, 19, 2, 0},                // 19
118
119  {menu_300, 5, 0, 0, 0, 0},                  // 20
120  {menu_301, 5, 21, 22, 21, 0},               // 21
121  {menu_302, 5, 21, 23, 22, 0},               // 22
122  {menu_303, 5, 22, 24, 23, 0},               // 23
123  {menu_304, 5, 23, 24, 3, 0}                 // 24
124  
125};
126
127void setup() {
128  // put your set code here, to run once:
129  lcd.clear();
130  lcd.begin(16, 2);
131  pinMode(LCD_BACKLIGHT_PIN, OUTPUT);
132  pinMode(LED_PIN, OUTPUT);
133  analogWrite(LCD_BACKLIGHT_PIN, lcd_brightness_val);
134  digitalWrite(LED_PIN, led_state);
135  pinMode(13, OUTPUT);
136  Serial.begin(9600);
137  show_menu();
138  Serial.println("Setup DONE");
139}
140
141void loop() {
142  if(millis() >= checkButtonTaskTimer + checkButtonTaskInterval){
143    checkButtonTaskTimer += checkButtonTaskInterval;
144    // do the task
145    checkButton();
146  }
147
148  if(millis() >= updateLCDTaskTimer + updateLCDTaskInterval){
149    updateLCDTaskTimer += updateLCDTaskInterval;
150    // do the task
151    //show_menu();
152  }
153
154  if ( (isBlinkLED) ) {
155    if(millis() >= blinkLEDTaskTimer + blinkLEDTaskInterval){
156      blinkLEDTaskTimer += blinkLEDTaskInterval;
157      // do the task
158      if (led_state) {
159        led_state = false;
160      } else {
161        led_state = true;
162      }
163      digitalWrite(LED_PIN, led_state);
164    }
165  }
166
167  if (isFadeLED) {
168    // change the brightness for next time through the loop:
169    led_brightness_val = led_brightness_val + led_fadeAmount;
170    // reverse the direction of the fading at the ends of the fade:
171    if (led_brightness_val <= 0 || led_brightness_val >= 255) {
172      led_fadeAmount = -led_fadeAmount;
173    }
174    analogWrite(LED_PIN, led_brightness_val);
175  }
176}
177
178void start() {
179    lcd.setCursor(0, 0);
180    lcd.print("                ");
181    lcd.setCursor(0, 0);
182    lcd.print("start works!");
183    digitalWrite(LED_PIN, HIGH);
184    delay(1000);
185    digitalWrite(LED_PIN, LOW);
186}
187
188void LED_light() {
189  isLEDEnableMode = true;
190  
191  if (led_state) {
192    led_state = false;
193  } else {
194    led_state = true;
195  }
196  digitalWrite(LED_PIN, led_state);
197}
198
199void LED_blink() {
200  if (isBlinkLED) {
201    isBlinkLED = false;
202  } else {
203    isBlinkLED = true;
204  }
205}
206
207void LED_brightness() {
208  show_led_brightness();
209  isKeyDisabled = true;
210  isSetLEDBrightnessMode = true;
211}
212
213void LED_fade() {
214  if (isFadeLED) {
215    isFadeLED = false;
216  } else {
217    isFadeLED = true;
218  }
219}
220
221void LCD_backlight() {
222  if (isLcdBacklit) {
223    analogWrite(LCD_BACKLIGHT_PIN,0);
224    isLcdBacklit = false;
225  } else {
226    lcd_brightness_val = 255;
227    analogWrite(LCD_BACKLIGHT_PIN,lcd_brightness_val);
228    isLcdBacklit = true;
229  }  
230}
231
232void LCD_brightness() {
233
234  show_lcd_brightness();
235  isLcdBacklit = true;
236  isKeyDisabled = true;
237  isSetLCDBrightnessMode = true;
238}
239
240void show_menu(void)
241{
242  unsigned char line_cnt = 0;
243  unsigned char from = 0;
244  unsigned char till = 0;
245  unsigned char temp = 0;
246  while (till <= selected)
247  {
248    till += menu[till].num_menupoints;   
249  }   
250  from = till - menu[selected].num_menupoints;  
251  till--;                     
252  temp = from;                
253  // browsing somewhere in the middle
254  if ((selected >= (from+PER_SPACE)) && (selected <= till ))
255  {
256    from = selected-PER_SPACE;
257    till = from + (DISPLAY_ROWS-1);
258    for (from; from<=till; from++)
259    {
260      lcd.setCursor(0, line_cnt);
261      lcd.print(menu[from].text);
262      line_cnt = line_cnt + 1;
263    }
264  }
265  
266  // browsing somewhere in the top or the bottom
267  else
268  {
269    // top of the menu
270    if (selected < (from+PER_SPACE))  // 2 lines
271    {
272      //till = from + 3;
273      till = from + (DISPLAY_ROWS-1); // 2 lines
274      for (from; from<=till; from++)
275      {
276        lcd.setCursor(0, line_cnt);
277        lcd.print(menu[from].text);
278        line_cnt = line_cnt + 1;
279      }
280    }
281
282    // bottom of the menu
283    if (selected == till)
284    {
285      from = till - (DISPLAY_ROWS-1); // 2 lines
286      
287      for (from; from<=till; from++)
288      {
289        lcd.setCursor(0, line_cnt);
290        lcd.print(menu[from].text);
291        line_cnt = line_cnt + 1;     
292      }
293    }
294  }
295  lcd.setCursor(0, 2);
296  lcd.print(">");
297}
298
299void checkButton() {
300  int x = analogRead (0);
301
302  if (x < 50) {
303    buttonPressed = 'R';
304  } else if (x < 150) {
305    buttonPressed = 'U';
306  } else if (x < 300){
307    buttonPressed = 'D';
308  } else if (x < 450){
309    buttonPressed = 'L';
310  } else if (x < 700){
311    buttonPressed = 'S';
312  } else {
313    buttonPressed = '0';
314  }
315  processButton(buttonPressed);
316}
317
318void processButton(char buttonPressed) {
319  switch ( buttonPressed ) {
320    case 'R': 
321      if (isKeyDisabled) {
322      } else {
323        if (menu[selected].fp != 0)
324        {
325          menu[selected].fp();
326        }
327        prev_key = selected;
328        selected = menu[selected].enter;
329        show_menu();
330      }
331      break;
332    case 'U': // U
333      if (isKeyDisabled) {
334        if (isSetLCDBrightnessMode) {
335          lcd_brightness_val = lcd_brightness_val + 10;
336          analogWrite(LCD_BACKLIGHT_PIN,lcd_brightness_val);
337          show_lcd_brightness();
338        } else if (isSetLEDBrightnessMode) {
339          led_brightness_val = led_brightness_val + 10;
340          analogWrite(LED_PIN,led_brightness_val);
341          show_led_brightness();
342        }
343      } else {
344        prev_key = selected;
345        selected = menu[selected].up; 
346        show_menu();
347      }
348      break;
349    case 'D': // D
350      if (isKeyDisabled) {
351        if (isSetLCDBrightnessMode) {
352          lcd_brightness_val = lcd_brightness_val - 10;
353          analogWrite(LCD_BACKLIGHT_PIN,lcd_brightness_val);
354          show_lcd_brightness();
355        } else if (isSetLEDBrightnessMode) {
356          led_brightness_val = led_brightness_val - 10;
357          analogWrite(LED_PIN,led_brightness_val);
358          show_led_brightness();
359        }
360      } else {
361        prev_key = selected;
362        selected = menu[selected].down; 
363        show_menu();
364      }
365      break;
366    case 'L': // L
367      if (isKeyDisabled) {
368        isKeyDisabled = false;
369        isSetLCDBrightnessMode = false;
370        hide_lcd_brightness();
371        isSetLEDBrightnessMode = false;
372        hide_led_brightness();
373      }
374      show_menu();
375      break;
376    case 'S': // S
377      show_menu();
378      break;
379    default:
380      break;
381  }  
382}
383
384void show_lcd_brightness() {
385  lcd.setCursor(12,1);
386  float temp = (float) lcd_brightness_val / 255 * 100;
387  if (temp < 10) {
388    lcd.print(":  ");
389    lcd.print(temp);
390  } else if (temp < 100) {
391    lcd.print(": ");
392    lcd.print(temp);
393  } else {
394    lcd.print(":");
395    lcd.print(temp);
396  }
397}
398
399void hide_lcd_brightness() {
400  lcd.setCursor(12,1);
401  lcd.print("    ");
402}
403
404void show_led_brightness() {
405  lcd.setCursor(12,1);
406  float temp = (float) led_brightness_val / 255 * 100;
407  if (temp < 10) {
408    lcd.print(":  ");
409    lcd.print(temp);
410  } else if (temp < 100) {
411    lcd.print(": ");
412    lcd.print(temp);
413  } else {
414    lcd.print(":");
415    lcd.print(temp);
416  }
417}
418
419void hide_led_brightness() {
420  lcd.setCursor(12,1);
421  lcd.print("    ");
422}

Call To Action

If you have any question, please leave it in the comment box. Thank you and have a good day.



Posts in this series



No comments yet!

GitHub-flavored Markdown & a sane subset of HTML is supported.