Skip to content

Circular menu in arduino

Step 2

All the complicated part is already done !

Download the CircularMenuAdvanced example

Open menu.h and change the voice

#define MENUSIZE 26 //26 ELEMENTS

to your needs. In our example we have 26 elements.

Now all you have to do is to write your *.pde program.

In our case we did this :

//Made By TrustFm.net (for www.hw2sw.com)

#include "Menu.h"

Menu MyMenu;

//The DOWN button
const byte BtnDownPin = 7;
byte BtnDownState = 0;
byte BtnDownLastState = 0; 

//The RIGHT button
const byte BtnRightPin = 6;
byte BtnRightState = 0;
byte BtnRightLastState = 0; 

//The ENTER button
const byte BtnEnterPin = 5;
byte BtnEnterState = 0;
byte BtnEnterLastState = 0; 

void ClearScreen(){
  int i;
  for (i = 0; i < 15; i = i + 1) {
    Serial.println();
  }
}

void ShowHints() {
  //Right hint
  if ( (*(MyMenu.GetSelectedMenuCell())).GetTitle() != (*((*(MyMenu.GetSelectedMenuCell())).GetNextRight())).GetTitle() ) {
    Serial.print("              > ");
    Serial.println( (*((*(MyMenu.GetSelectedMenuCell())).GetNextRight())).GetTitle() );
  } 

  //Down hint
  if ( (*(MyMenu.GetSelectedMenuCell())).GetTitle() != (*((*(MyMenu.GetSelectedMenuCell())).GetNextDown())).GetTitle() ) {
    Serial.print(" V ");
    Serial.println( (*((*(MyMenu.GetSelectedMenuCell())).GetNextDown())).GetTitle() );
  }
}

void setup()
{
  Serial.begin(9600);

  //Menu definition (Title,Father,Down,Next)
  (*(MyMenu.GetMenuCell(0))).Set("Menu 1", MyMenu.GetMenuCell(0), MyMenu.GetMenuCell(1),MyMenu.GetMenuCell(0));
  (*(MyMenu.GetMenuCell(1))).Set("Menu 2", MyMenu.GetMenuCell(1), MyMenu.GetMenuCell(6),MyMenu.GetMenuCell(2));
  (*(MyMenu.GetMenuCell(2))).Set("Menu 2>Smenu 1", MyMenu.GetMenuCell(1), MyMenu.GetMenuCell(3),MyMenu.GetMenuCell(2));
  (*(MyMenu.GetMenuCell(3))).Set("Menu 2>Smenu 2", MyMenu.GetMenuCell(1), MyMenu.GetMenuCell(4),MyMenu.GetMenuCell(3));
  (*(MyMenu.GetMenuCell(4))).Set("Menu 2>Smenu 3", MyMenu.GetMenuCell(1), MyMenu.GetMenuCell(5),MyMenu.GetMenuCell(4));
  (*(MyMenu.GetMenuCell(5))).Set("Menu 2>up one dir", MyMenu.GetMenuCell(1), MyMenu.GetMenuCell(2),MyMenu.GetMenuCell(5));
  (*(MyMenu.GetMenuCell(6))).Set("Menu 3", MyMenu.GetMenuCell(6), MyMenu.GetMenuCell(14),MyMenu.GetMenuCell(7));
  (*(MyMenu.GetMenuCell(7))).Set("Menu 3>Smenu 1", MyMenu.GetMenuCell(6), MyMenu.GetMenuCell(8),MyMenu.GetMenuCell(7));
  (*(MyMenu.GetMenuCell(8))).Set("Menu 3>Smenu 2", MyMenu.GetMenuCell(6), MyMenu.GetMenuCell(12),MyMenu.GetMenuCell(9));
  (*(MyMenu.GetMenuCell(9))).Set("Menu 3>Smenu 2>SSmenu 1", MyMenu.GetMenuCell(8), MyMenu.GetMenuCell(10),MyMenu.GetMenuCell(9));
  (*(MyMenu.GetMenuCell(10))).Set("Menu 3>Smenu 2>SSmenu 2", MyMenu.GetMenuCell(8), MyMenu.GetMenuCell(11),MyMenu.GetMenuCell(10));
  (*(MyMenu.GetMenuCell(11))).Set("Menu 3>Smenu 2>up one dir", MyMenu.GetMenuCell(8), MyMenu.GetMenuCell(9),MyMenu.GetMenuCell(11));
  (*(MyMenu.GetMenuCell(12))).Set("Menu 3>Smenu 3", MyMenu.GetMenuCell(6), MyMenu.GetMenuCell(13),MyMenu.GetMenuCell(12));
  (*(MyMenu.GetMenuCell(13))).Set("Menu 3>up one dir", MyMenu.GetMenuCell(6), MyMenu.GetMenuCell(7),MyMenu.GetMenuCell(13));
  (*(MyMenu.GetMenuCell(14))).Set("Menu 4", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(0),MyMenu.GetMenuCell(15));
  (*(MyMenu.GetMenuCell(15))).Set("Menu 4>Smenu 1", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(21),MyMenu.GetMenuCell(16));
  (*(MyMenu.GetMenuCell(16))).Set("Menu 4>Smenu 1>SSmenu 1", MyMenu.GetMenuCell(15), MyMenu.GetMenuCell(20),MyMenu.GetMenuCell(17));
  (*(MyMenu.GetMenuCell(17))).Set("Menu 4>Smenu 1>SSmenu 1>SSSmenu 1" , MyMenu.GetMenuCell(16), MyMenu.GetMenuCell(18),MyMenu.GetMenuCell(17));
  (*(MyMenu.GetMenuCell(18))).Set("Menu 4>Smenu 1>SSmenu 1>SSSmenu 2", MyMenu.GetMenuCell(16), MyMenu.GetMenuCell(19),MyMenu.GetMenuCell(18));
  (*(MyMenu.GetMenuCell(19))).Set("Menu 4>Smenu 1>SSmenu 1>up one dir", MyMenu.GetMenuCell(16), MyMenu.GetMenuCell(17),MyMenu.GetMenuCell(19));
  (*(MyMenu.GetMenuCell(20))).Set("Menu 4>Smenu 1>up one dir", MyMenu.GetMenuCell(15), MyMenu.GetMenuCell(16),MyMenu.GetMenuCell(20));
  (*(MyMenu.GetMenuCell(21))).Set("Menu 4>Smenu 2", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(22),MyMenu.GetMenuCell(21));
  (*(MyMenu.GetMenuCell(22))).Set("Menu 4>Smenu 3", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(23),MyMenu.GetMenuCell(22));
  (*(MyMenu.GetMenuCell(23))).Set("Menu 4>Smenu 4", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(24),MyMenu.GetMenuCell(23));
  (*(MyMenu.GetMenuCell(24))).Set("Menu 4>Smenu 5", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(25),MyMenu.GetMenuCell(24));
  (*(MyMenu.GetMenuCell(25))).Set("Menu 4>up one dir", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(15),MyMenu.GetMenuCell(25));

  pinMode(BtnDownPin, INPUT);
  pinMode(BtnRightPin, INPUT);
  pinMode(BtnEnterPin, INPUT);

  ClearScreen();
  Serial.print(">");
  Serial.println(  (*(MyMenu.GetSelectedMenuCell())).GetTitle()  );
  (*(MyMenu.GetSelectedMenuCell())).SetIsSelected(true);
  ShowHints(); 

}

void loop() // The main loop (runs over and over again)
{
  BtnDownState = digitalRead(BtnDownPin);
  BtnRightState = digitalRead(BtnRightPin);
  BtnEnterState = digitalRead(BtnEnterPin);

  //The DOWN button
  if (BtnDownState != BtnDownLastState) {
    if (BtnDownState == HIGH) {
      ClearScreen();
      Serial.print(">");
      Serial.println(  (*(MyMenu.GetNextDownMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
      ShowHints();
    }
  }
  BtnDownLastState = BtnDownState;

  //The RIGHT button
  if (BtnRightState != BtnRightLastState) {
    if (BtnRightState == HIGH) {
      ClearScreen();
      Serial.print(">");
      Serial.println(  (*(MyMenu.GetNextRightMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
      ShowHints();
    }
  }
  BtnRightLastState = BtnRightState;

  //The ENTER button
  if (BtnEnterState != BtnEnterLastState) {
    if (BtnEnterState == HIGH) {
      if (  (*(MyMenu.GetMenuCell(0))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(1))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 2 info page");
      } else if (  (*(MyMenu.GetMenuCell(2))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 2 Submenu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(3))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 2 Submenu 2 info page");
      } else if (  (*(MyMenu.GetMenuCell(4))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 2 Submenu 3 info page");
      } else if (  (*(MyMenu.GetMenuCell(5))).GetIsSelected()==true  ) { //UP ONE DIR
        ClearScreen();
        Serial.print(">");
        Serial.println(  (*(MyMenu.GetFatherMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
        ShowHints();
      } else if (  (*(MyMenu.GetMenuCell(6))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 3 info page");
      } else if (  (*(MyMenu.GetMenuCell(7))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 3 submenu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(8))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 3 submenu 2 info page");
      } else if (  (*(MyMenu.GetMenuCell(9))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 3 sub sub menu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(10))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 3 sub sub menu 2 info page");
      } else if (  (*(MyMenu.GetMenuCell(11))).GetIsSelected()==true  ) { //UP ONE DIR
        ClearScreen();
        Serial.print(">");
        Serial.println(  (*(MyMenu.GetFatherMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
        ShowHints();
      } else if (  (*(MyMenu.GetMenuCell(12))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 3 submenu 3 info page");
      } else if (  (*(MyMenu.GetMenuCell(13))).GetIsSelected()==true  ) { //UP ONE DIR
        ClearScreen();
        Serial.print(">");
        Serial.println(  (*(MyMenu.GetFatherMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
        ShowHints();
      } else if (  (*(MyMenu.GetMenuCell(14))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 info page");
      } else if (  (*(MyMenu.GetMenuCell(15))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 submenu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(16))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 SUB SUB menu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(17))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 SUB SUB SUB menu 1 info page");
      } else if (  (*(MyMenu.GetMenuCell(18))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 SUB SUB SUB menu 2 info page");
      } else if (  (*(MyMenu.GetMenuCell(19))).GetIsSelected()==true  ) { //UP ONE DIR
        ClearScreen();
        Serial.print(">");
        Serial.println(  (*(MyMenu.GetFatherMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
        ShowHints();
      } else if (  (*(MyMenu.GetMenuCell(20))).GetIsSelected()==true  ) { //UP ONE DIR
        ClearScreen();
        Serial.print(">");
        Serial.println(  (*(MyMenu.GetFatherMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
        ShowHints();
      } else if (  (*(MyMenu.GetMenuCell(21))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 submenu 2 info page");
      } else if (  (*(MyMenu.GetMenuCell(22))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 submenu 3 info page");
      } else if (  (*(MyMenu.GetMenuCell(23))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 submenu 4 info page");
      } else if (  (*(MyMenu.GetMenuCell(24))).GetIsSelected()==true  ) {
        ClearScreen();
        Serial.println("This is the Menu 4 submenu 5 info page");
      } else if (  (*(MyMenu.GetMenuCell(25))).GetIsSelected()==true  ) { //UP ONE DIR
        ClearScreen();
        Serial.print(">");
        Serial.println(  (*(MyMenu.GetFatherMenuCell(MyMenu.GetSelectedMenuCell()))).GetTitle()  );
        ShowHints();
      }
    }
  }
  BtnEnterLastState = BtnEnterState;

}

In order to do your menu you should only change the menu definition part !
For example
(*(MyMenu.GetMenuCell(0))).Set("Menu 1", MyMenu.GetMenuCell(0), MyMenu.GetMenuCell(1),MyMenu.GetMenuCell(0));

means that the 0 cell element has a title "Title 1" with a father id 0, down id 1 and right id 0.

Another example :

(*(MyMenu.GetMenuCell(15))).Set("Menu 4>Smenu 1", MyMenu.GetMenuCell(14), MyMenu.GetMenuCell(21),MyMenu.GetMenuCell(16));

means that the 15 cell element has a name "Menu 4>Smenu 1" his father has id 14, down id 21 and right id 16

As you remember the id numbers are already known (look at your menu.txt) done in the step before.
Now change only the ENTER part in base at your needs . (What do you want to do in each case).

Step 3

Construct your circuit like the photo below :

The down button is on port 7 the right button on port 6 and the enter button at port 5.

So reassuming

  1. Download the CircularMenuAdvanced example
  2. Create your menu.txt (find father, down and right elements). This step is optional but helps a lot.
    Rules :
    Add to all sub-levels, except the first one, the voice "up one directory" for navigability reasons.
    No father means that the father pointer points itself.
    Bottom element means that the down pointer points at the top element of the same sub level.
    Right edge element means that the right pointer points itself.
  3. Change from menu.h the total elements of your menu
  4. Edit the "definition part" and the "enter part" of the given .pde file
  5. Add the three buttons (down, right and enter) at the pins 7,6,5 look here to find out how to hook the push buttons at arduino
  6. You are ready to go

A video example is located below :

 

Last thing :
Of course you can select different approaches for the navigability.
For example you can decide that the right edge element points at the root element so you have vertical and horizontal circularity.
It is up to you to decide the way to link your cell elements but keep in mind the navigability of your menu.
By doing vertical and horizontal circularity then the voices "up one dir" are not needed but a mistake by the user can produce a much longer circle path.

Hope you liked this article.

Update: the above code works perfectly with Arduino IDE 0.22; due to very little incompatibilities, such as headers renomination, the Arduino IDE 1.0 requires modification described in this article.