Keypad Menu

Hello, there.

Last week, I've stumbled upon this program a user provided as an answer on this fórum: Arduino Keypad Menu design - Programming Questions - Arduino Forum

It has already helped me a lot on the developing of a menu for a matrix keyboard as input, but I'm stucked on how to deploy vertical submenus inside each index.

menuItem_t menu[] = {
  {10, MENU_NN, "Login"},
  {20, MENU_NN, "AlarmeTexto1"},
  {21, MENU_NN, "AlarmeTexto2"},
  {30, MENU_NN, "Config-1"},
  {31, MENU_NN, "Config-2"},
  {32, MENU_NUM, "Config-3"},
  {33, MENU_NN, "Config-4"},
  {40, MENU_NN, "Status1"},
  {41, MENU_NN, "Status2"},
  {42, MENU_NN, "Status3"},
  {50, MENU_NUM, "ReprovaCav1"},
  {51, MENU_NUM, "ReprovaCav2"},
  {60, MENU_NUM, "Parada"},
  {70, MENU_NN, "Ajuste"},
  {80, MENU_NUM, "Setup"},
};

These are the current menus, as you click the correspondent key multiple time it will go down through the sub-menus.

But in order to make vertical menus inside each of the menus classified as MENU_NN I was think thinking if Should I create another struct for each index with a submenu? Or is it possible to add it to the existing one as a third digit?

On the project I'm working on, the letters M, R, O and P correspond to Up, Down, Left and Right respectively. Since I'm planning to have vertical and horizontal menus, they would be only for moving through the options depending if its a vertical or a horizontal list. The symbol '#' is the enter and would save the option choosen.

Here is the link for the program if you want to take a look at it:

https://pastebin.com/z1hpqXzr

Thank you very much!

Most on here will not click on a link. Please post your entire sketch in a code block.

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include <Keypad.h>
hd44780_I2Cexp lcd; // declare lcd object: auto locate & config display for hd44780 chip
 
const byte ROWS = 4; // Four rows
const byte COLS = 8; //  columns
 
// Define the Keymap
char hexaKeys[ROWS][COLS] = {
  {'A', 'B', '1', '2', '3', 'I', 'J', 'K'},
  {'C', 'D', '4', '5', '6', 'L', 'M', 'N'},
  {'E', 'F', '7', '8', '9', 'O', '#', 'P'},
  {'G', 'H', '.', '0', '*', 'Q', 'R', 'S'},
};
 
byte rowPins[ROWS] = {10, 11, 12, 13}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5, 6, 7, 8, 9}; //connect to the column pinouts of the keypad
 
//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
 
enum {MENU_NN, MENU_NUM};
struct menuItem_t {
  byte id;
  char type;
  char* text;
};
 
menuItem_t menu[] = {
  {10, MENU_NN, "Login"},
  {20, MENU_NN, "AlarmeTexto1"},
  {21, MENU_NN, "AlarmeTexto2"},
  {30, MENU_NN, "Config-1"},
  {31, MENU_NN, "Config-2"},
  {32, MENU_NUM, "Config-3"},
  {33, MENU_NN, "Config-4"},
  {40, MENU_NN, "Status1"},
  {41, MENU_NN, "Status2"},
  {42, MENU_NN, "Status3"},
  {50, MENU_NUM, "ReprovaCav1"},
  {51, MENU_NUM, "ReprovaCav2"},
  {60, MENU_NUM, "Parada"},
  {70, MENU_NN, "Ajuste"},
  {80, MENU_NUM, "Setup"},
};
 
#define NUMMENUITEMS (sizeof(menu)/sizeof(menu[0]))
 
int findMenuIndex(byte id)
{
  for (int i = 0; i < NUMMENUITEMS; i++)
  {
    if (id == menu[i].id) return i;
  }
  return -1; // id not found
}
 
int currentMenuID;
int currentMenuIndex = -1;
boolean currentMenuChanged;
char menuInput[21];
boolean menuInputChanged;
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////Processing of Input Key/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
int handleMenuKey(char c)
{ // return value is -1 if key has been handled by no value has been entered
  // return value is menuIndex of item for which a value has been entered
  int retVal = -1;
  currentMenuChanged = false;
  menuInputChanged = false;
  if (c == 0) return retVal;
  int menuID = currentMenuID;
  if (c == '#' && currentMenuID >= 0 && strlen(menuInput) > 0)
  {
    retVal = currentMenuIndex;
    currentMenuID = 0; // Abort/Exit menu
    currentMenuIndex = -1;
    currentMenuChanged = true;
    return retVal; // we have finished entering a value
  }
  else if (c == '*' && strlen(menuInput) > 0)
  {
    menuInput[strlen(menuInput) - 1] = '\0'; // clear last input character
    menuInputChanged = true;
  }
  else if (c == '*' && currentMenuID >= 0)
  {
    currentMenuID = 0; // Abort/Exit menu
    currentMenuIndex = -1;
    currentMenuChanged = true;
  }
  else if (c >= 'A' && c <= 'H') // key 'A'...'H' pressed
  {
    int i = c - 'A';
    if (i != menuID / 10 - 1) menuID = 10 + i * 10; // activate main menu
    else if (findMenuIndex(menuID + 1) >= 0) menuID++; // increase sub menu
    else menuID = 10 + i * 10; // from last sub menu return to main menu
    if (menuID != currentMenuID) // has menuID changed?
    {
      currentMenuChanged = true;
      currentMenuID = menuID;
      currentMenuIndex = findMenuIndex(menuID);
      memset(menuInput, 0, sizeof(menuInput));
    }
  }
  else if (c >= '0' && c <= '9' && menu[currentMenuIndex].type == MENU_NUM) // key'0'...'9' pressed and numeric input
  {
    if (strlen(menuInput) < sizeof(menuInput) - 1)
    {
      menuInput[strlen(menuInput)] = c;
      menuInputChanged = true;
    }
  }
  return retVal;
}
////////////////////////Output Functions ////////////////////////////////////////////////
 
void outputMenuItem()
{
  // for LCD output: set output position here
    switch (currentMenuID) {
    case 10:
      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Login Operadores");
      lcd.setCursor(0, 2);
      lcd.print(">Cod. Operador1");
      lcd.setCursor(0, 4);
      lcd.print(" Cod. Operador2");
      break;
    case 20:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Alarme Texto 1");
      break;
    case 21:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Alarme Texto 2");
      break;
    case 30:
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Config. Cavidade 1");
      lcd.setCursor(0, 2);
      lcd.print(">Cod. Cavidade1");
      lcd.setCursor(0, 4);
      lcd.print(" Cod. Cavidade1");
      break;
    case 31:
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Config. Cavidade 2");
      lcd.setCursor(0, 2);
      lcd.print(">Cod. Cavidade2");
      lcd.setCursor(0, 4);
      lcd.print(" Cod. Cavidade2");
      break;
    case 32:
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Tempo Verificacao");
      lcd.setCursor(3, 1);
      lcd.print("Maquina Parada");
      lcd.setCursor(0, 4);
      lcd.print(">_____");
      break;
    case 33:
      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Config. Habilita");
      lcd.setCursor(6, 1);
      lcd.print("Cavidade");
      lcd.setCursor(0, 4);
      lcd.print(">Cav1:ON");
      lcd.setCursor(12, 4);
      lcd.print("Cav2:OFF");
      break;
    case 40:
      lcd.clear();
      lcd.setCursor(7, 0);
      lcd.print("Status");
      lcd.setCursor(0, 2);
      lcd.print("Tempo Ciclo:_____");
      lcd.setCursor(0, 4);
      lcd.print("Tempo Espera:_____");
      break;
    case 41:
      lcd.clear();
      lcd.setCursor(3, 0);
      lcd.print("Status-Tempos:");
      lcd.setCursor(0, 2);
      lcd.print("Maq.Parada:_____");
      lcd.setCursor(0, 4);
      lcd.print("Producao:_____");
      break;
    case 42:
      lcd.clear();
      lcd.setCursor(7, 0);
      lcd.print("Status");
      lcd.setCursor(0, 2);
      lcd.print("OEE CAV1%:_____");
      lcd.setCursor(0, 4);
      lcd.print("OEE CAV2%:_____");
      break;
    case 50:
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Reprova Cavidade 1");
      lcd.setCursor(0, 2);
      lcd.print("Motivo: MotivoY    >");
      lcd.setCursor(0, 4);
      lcd.print("Qtd:_____");
      break;
    case 51:
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Reprova Cavidade 2");
      lcd.setCursor(0, 2);
      lcd.print("Motivo: MotivoX    >");
      lcd.setCursor(0, 4);
      lcd.print("Qtd:_____");
      break;
    case 60:
      lcd.clear();
      lcd.setCursor(7, 0);
      lcd.print("Parada");
      lcd.setCursor(0, 2);
      lcd.print("Motivo Parada: XYZ >");
      lcd.setCursor(0, 4);
      lcd.print("Cod.Funcionario:____");
      break;
    case 70:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Ajuste");
      break;
    case 80:
      lcd.clear();
      lcd.setCursor(7, 0);
      lcd.print("Setup:");
      lcd.setCursor(0, 2);
      lcd.print("Digite sua Senha:");
      lcd.setCursor(0, 4);
      lcd.print(">_____");
      break;
 
  }
}
 
//  if (currentMenuIndex>=0)
//    snprintf(buf, sizeof(buf), "%-20s", menu[currentMenuIndex].text);
//  else
//    snprintf(buf, sizeof(buf), "%-20s", "No menu active");
//  //Serial.println(buf);
//  lcd.print(buf);
 
 
void outputMenuInput()
{
  // for LCD output: set output position here
  outputMenuItem();
  switch (currentMenuID) {
      char buf[21];
    case 32:
      lcd.setCursor(1, 3);
      // Format buffer to fixed length
      snprintf(buf, sizeof(buf), "%-20s", menuInput);
      //Serial.println(buf);
      lcd.print(buf);
      break;
    case 50:
      lcd.setCursor(4, 4);
      // Format buffer to fixed length
      snprintf(buf, sizeof(buf), "%-20s", menuInput);
      //Serial.println(buf);
      lcd.print(buf);
      lcd.setCursor(1, 0);
      lcd.print("Reprova");
      break;
    case 51:
      lcd.setCursor(4, 4);
      // Format buffer to fixed length
      snprintf(buf, sizeof(buf), "%-20s", menuInput);
      //Serial.println(buf);
      lcd.print(buf);
      lcd.setCursor(1, 0);
      lcd.print("Reprova");
      break;
    case 80:
      lcd.setCursor(1, 3);
      // Format buffer to fixed length
      snprintf(buf, sizeof(buf), "%-20s", menuInput);
      //Serial.println(buf);
      lcd.print(buf);
      break;
    case 60:
      lcd.setCursor(16, 4);
      // Format buffer to fixed length
      snprintf(buf, sizeof(buf), "%-20s", menuInput);
      //Serial.println(buf);
      lcd.print(buf);
      lcd.setCursor(7, 0);
      lcd.print("Parada");
      break;
  }
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
void setup() {
  Serial.begin(9600);
  lcd.begin(20, 4);
}
 
void loop()
{
  // input
  char c = customKeypad.getKey();
 
  // processing of input key
  int inputReady = handleMenuKey(c);
 
  // if we are finished with entering a value, we could do further processing
  if (inputReady >= 0)
  { // in this case just show some serial debugging message
    Serial.print("Input ready: ");
    Serial.print(menuInput);
    Serial.print(" for menu index ");
    Serial.println(inputReady);
  }
 
  // output functions (i.e. output to serial, output to lcd)
  if (currentMenuChanged) outputMenuItem();
  if (menuInputChanged)  outputMenuInput();
}