Keep it Simple Stupid – Designing a Simple Menu with Version Control

Yes, I spilled my coffee on my notebook :/  It has mostly dried, so I think it will be okay, but my notebook is a very important tool, since it not only helps me brainstorm, but it helps me document things.

When I was trying to design this new menu system, I thought of many kinds of complicated things. After struggling for over an hour to get a concept I had to work, I took a break.  Instead of trying to answer the question “How do I make a menu system that does all of these things”, I thought of this question: “How do I make a very simple menu system?”

It won’t be as simple as the code bits from the last post suggested, but I do believe it will be easier to work with.  All of the menu things are in the main .ino file, and the only thing that is in the “Library” are the functions to handle the buttons (from an ADC channel, to button pressed).

I’ll go ahead and post what I have now here, although later I will make a repository for it.  And it’s not even done, I still have some work to do on it, but I think it is a step in the right direction, towards simplicity. I’ll be using this menu system to organize functions that I’ll use for the wireless testing.  I’ll also do SD card management on it as well (maybe reading from the SD card and showing it on the LCD :P)

An important note: Throughout my development on this, I use version control (Mercurial) so that once I got to a good space, I could quickly make a commit, and have a place where I can go back to, which I had to on several occasions. Commiting is so easy, that you’ll want to do it often (as you should).

Main arduino code. You’ll need to create a library named SimpleMenu in your libraries directory. Then add SimpleMenu.cpp and SimpleMenu.h. (I put empty comment lines in the below code so that the <code> html tag would codify it all, without me having to put <code> for each block)
// include the library code:
#include #include
#include .
/*
Example of the AdvMenu library. The main switches for enabling/disabling things are at the
top of AdvMenu.h. Disabling the music reduces the sketch size by 11274 bytes. But music is
awesome!
*/
//#define SETUP_EEPROM 1 //enable to intialize eeprom
#define EEP_MAINMENU 100 //store at offset 100
#define DISPHEIGHT 4
#define DISPWIDTH 20
//
//Keys for LCD to generate these characters
#define UPARROW ((uint8_t)3)
#define DOWNARROW ((uint8_t)4)
#define SELECT ((uint8_t)5)
#define HEART ((uint8_t)0)
#define SMILE ((uint8_t)2)
#define KEY ((uint8_t)1)
//
//
//
//class Menu;
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 7, 6, 5, 4, 3);
//
//
//
void customclear()
{
// ensures all custom character slots are clear before new custom
// characters can be defined.
byte blank[8] =
{
B00000, B00000, B00000, B00000, B00000, B00000, B00000, B00000
};
for(int i = 0; i < 8; i++)
{
lcd.createChar(i, blank);
}
}
// make some custom characters:
// Names for these are #defined in AdvMenu.h
byte heart[8] = {
0b00000,
0b01010,
0b11111,
0b11111,
0b11111,
0b01110,
0b00100,
0b00000
};
byte Password[8]={
B00110,
B00100,
B00110,
B00100,
B01110,
B10001,
B10001,
B01110
};
byte smiley[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b10001,
0b01110,
0b00000
};
byte UpArrow[8]={
B00100,
B01110,
B11111,
B00100,
B00100,
B00100,
B00100,
B00000
};
byte DownArrow[8]={
B00000,
B00100,
B00100,
B00100,
B00100,
B11111,
B01110,
B00100
};
byte Select[8]={
B01000,
B01100,
B00110,
B11111,
B11111,
B00110,
B01100,
B01000
};
//
//
#define MENULEN 5
#define MAXMENU 20
#define MENUWIDTH 20
char* menutable[]=
{
"Settings",
"Change Menu",
"Show SD Card",
"Write SD Card",
"Wireless Menu"
};
char* subtable[]=
{
"Back to Main",
"string 2 3",
"string 3 4",
"string 4",
"String 5"
};
//
uint8_t mainarray[MAXMENU];
uint8_t subarray[MAXMENU];
//
//
void setup() {
unsigned int error;
unsigned char i;
// sets the LCD's rows and columns:
// create a new character
lcd.begin(20, 4);
Serial.begin(57600);
//
lcd.createChar(0, heart);
lcd.createChar(1, Password);
lcd.createChar(2, smiley);
lcd.createChar(3, UpArrow);
lcd.createChar(4, DownArrow);
lcd.createChar(5, Select);
//
#ifdef SETUP_EEPROM
for (i = 0; i < MAXMENU; i++)
{
mainarray[i] = i;
EEPROM.write(EEP_MAINMENU+i,i);
}
#endif
//get main menu
for (i=0; i {
mainarray[i] = EEPROM.read(EEP_MAINMENU+i);
subarray[i] = i; //not stored in eeprom yet
}
}
//
//
//
//
void loop(){
uint8_t menusel = 0;
unsigned char button;
unsigned char i=0;
unsigned char offset = 0;
unsigned char suboff = 0;
unsigned char curse=0;
unsigned char subcurse=0;
unsigned char menulen = MENULEN; //default
unsigned char sublen = 3;
unsigned int j;
lcd.clear();
while(1){
//display menu
lcd.clear();
for(j = 0; j < DISPHEIGHT; j++)
{
lcd.setCursor(1,j);
if(menusel == 0 && mainarray[j+offset] < menulen){
lcd.print(menutable[mainarray[j+offset]]);
}
if(menusel == 1 && subarray[j+suboff] < sublen)
{
lcd.print(subtable[subarray[j+suboff]]);
}
}
if(menusel == 0)
{//overlay selection
lcd.setCursor(0,curse);
lcd.write(SELECT);
}
if(menusel == 1)
{
//overlay selection
lcd.setCursor(0,subcurse);
lcd.write(SELECT);
}
//check buttons
button = buttonWait(A5,NO);
if(menusel == 0)
{
//if B pressed
if(button == BTNB)
{
//go into function selected. If it is a menu, change to it
if(mainarray[curse + offset] == 0)
menusel = 1;
button = 0; //reset button, dumbass
}
//if Up pressed
if(button == BTNU)
{
if(curse != 0)
{
curse--;
}
else
{
if(offset != 0)
{
offset--;
}
else
{
//shift everything by one in mainarray
for(j=0;j<MENULEN;j++)
{
if(mainarray[j] == 0)
{
mainarray[j] = MENULEN-1;
}
else
{
mainarray[j] = mainarray[j]-1;
}
}
}
}
}
//if Down pressed
if(button == BTND)
{
if(offset + DISPHEIGHT < MENULEN)
{
offset++;
}
else
{
if((curse != DISPHEIGHT-1) && (curse < menulen))
curse++;
}
}
}
if(menusel == 1)
{
//a submenu? Can use different table, or an offset for orig table.
//if B pressed
if(button == BTNB)
{
//go into function selected. If it is a menu, change to it
if(subarray[subcurse+suboff] == 0)
{
menusel = 0;
subcurse = 0;
suboff = 0;
}
else if(subarray[subcurse+suboff] == 1)
{
lcd.print("test");
delay(5000);
}
button = 0; //reset button, dumbass
}
//if Up pressed
if(button == BTNU)
{
if(subcurse != 0)
{
subcurse--;
}
else
{
if(suboff != 0)
{
suboff--;
}
else
{
//shift everything by one in mainarray
for(j=0;j<sublen;j++)
if(subarray[j] == 0)
subarray[j] = sublen-1;
else
subarray[j] = subarray[j]-1;
}
}
}
//if Down pressed
if(button == BTND)
{
if(suboff + DISPHEIGHT < sublen)
{
suboff++;
}
else
{
if((subcurse != DISPHEIGHT-1) && (subcurse < sublen-1))
subcurse++;
}
}
}
//start functions here
};
}

SimpleMenu.c

#include "SimpleMenu.h"
SimpleMenu::SimpleMenu (void)
{
init();
}
void SimpleMenu::init(void)
{
}
uint8_t buttonCheck( uint16_t analogpin)
{
uint16_t i=0, upper, lower;
uint8_t button = 0;
uint16_t analogval;
analogval = analogRead(analogpin);
//find what button it corresponds to
for( uint16_t i = 0; i < 21; i++) { // checks the _analogval against the high and low vales in the array upper = pgm_read_word(&(Button[i][1])); lower = pgm_read_word(&(Button[i][2])); if(analogval >= upper && analogval {
// stores the button number to a variable
button = pgm_read_word(&(Button[i][0]));
return button;
}
}
return button;
}
//
uint8_t buttonWait(uint16_t analogpin, uint8_t toggle)
{
//if toggle set, wait until button comes back to 0 position
uint16_t init_butt=0,state = 0;
uint32_t time;
uint8_t buffer[10];
uint8_t value;
//consider sleep code here
while(1){
while(state == 0)
{
for( uint16_t j=0; j {
buffer[j] = buttonCheck(analogpin);
delay(1);
}
value = buffer[0];
for( uint16_t j=1; j DEBOUNCE)
{
return parse_buttons(init_butt);
}
}
while (state == 2)
{
//wait for button fall
for( uint16_t j=0; j {
buffer[j] = buttonCheck(analogpin);
}
value = buffer[0];
for( uint16_t j=1; j<10;j++)
{
if(value != buffer[j])
{
//mismatch, discard
value =254;
break;
}
}
if( (value == 0))
{
return parse_buttons(init_butt);
}
}
}
return 0;
}
uint8_t parse_buttons(uint16_t button)
{
//given the channel and button, return corresponding actual button
//hardware specific
switch(button)
{
case 1:
return BTNU;
case 2:
return BTND;
case 3:
return BTNL;
case 4:
return BTNR;
case 5:
return BTNA;
case 6:
return BTNB;
default:
break;
};
return 255;
}

SimpleMenu.h

#ifndef __SIMPLE_MENU_H
#define __SIMPLE_MENU_H
//
#include #include <avr/pgmspace.h>
#include
#include <avr/io.h>
#include
#include
#include #include "Arduino.h"
//
#define BTNU (( uint8_t)13)
#define BTND (( uint8_t)14)
#define BTNL (( uint8_t)15)
#define BTNR (( uint8_t)16)
#define BTNA (( uint8_t)17)
#define BTNB (( uint8_t)18)
#define YES (( uint8_t)1)
#define NO (( uint8_t)0)
#define DEBOUNCE 200 //ms/sample
//******************************************************************************
//
// Button Definitions
//
//******************************************************************************
//2-dimensional array for asigning the buttons and there high and low values
const uint16_t PROGMEM Button[21][3] = {{1, 834, 845}, // button 1
{2, 712, 721}, // button 2
{3, 603, 613}, // button 3
{4, 315, 326}, // button 4
{5, 173, 185}, // button 5
{6, 85, 97}, // button 6
{7, 888, 898}, // button 1 + button 2
{8, 872, 882}, // button 1 + button 3
{9, 849, 858}, // button 1 + button 4
{10, 844, 848}, // button 1 + button 5
{11, 838, 843}, // button 1 + button 6
{12, 805, 815}, // button 2 + button 3
{13, 748, 758}, // button 2 + button 4
{14, 729, 740}, // button 2 + button 5
{15, 719, 728}, // button 2 + button 6
{16, 668, 678}, // button 3 + button 4
{17, 636, 646}, // button 3 + button 5
{18, 619, 629}, // button 3 + button 6
{19, 405, 415}, // button 4 + button 5
{20, 359, 369}, // button 4 + button 6
{21, 237, 247}}; // button 5 + button 6
//
extern uint8_t buttonWait(uint16_t analogpin, uint8_t toggle);
extern uint8_t parse_buttons(uint16_t button);
extern uint8_t buttonCheck(uint16_t analogpin);
/*
Menu Class
*/
class SimpleMenu
{
public:
SimpleMenu (void);
void init(void);
private:
};
#endif

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s