Problem with Menuelogic on two-line-display

Hello,

I’m currently writing a small controller, that uses a 16x2 LCD. So far my menue works fine, I can browse through the menue and submenueentries, but only one problem persists, I can’t return back to the root, if I call the method, it returns to the first menue in the submenue, but not back to the root.

I tried to debug and completely rewrote it, but I missed something, maybe it is something pretty trivial.

I attach a dumbed-down-version of my menue, the menueentries are defined in Values.h.

These are the main function, for going through the root-entries:

void moveThroughRoot(uint8_t Button) {
	if (was_in_sub) {
		lcd.clear();
		is_in_root = true;
		was_in_sub = false;
		Button = btnNONE;
		(*root[main_counter])(btnNONE);
	}

	if (is_in_root) {
		if (Button == (uint8_t)btnRIGHT) {
			if ((main_counter + 1) >= root_size){
				main_counter = 0;
				} else {
				main_counter++;
			}
		}
		
		if (Button == (uint8_t)btnLEFT) {
			if ((main_counter - 1) < 0){
				main_counter = root_size-1;
				} else {
				main_counter--;
			}
		}
		(*root[main_counter])(Button);
		} else {
		(*root[main_counter])(Button);
	}
}

Going through the submenues:

void MoveThroughSub(uint8_t button,uint8_t limit,const Subs subitems[]) {
	if (!is_in_root) {
		if (button == (uint8_t) btnRIGHT) {
			if ((sub_counter + 1) >= limit){
				sub_counter = 0;
				} else {
				sub_counter++;
			}
		}
		
		if (button == (uint8_t) btnLEFT) {
			if ((sub_counter - 1) < 0){
				sub_counter = limit-1;
				} else {
				sub_counter--;
			}
		}
		(*subitems[sub_counter])(button);
	}
}

A root and a submenueentry:

void Settings(uint8_t button) {
	if (is_in_root) {
		lcd.setCursor(0, 0);
		lcd.print("Einstellungen   ");
		if (button == (uint8_t) btnSELECT) {
			is_in_root = false;
			was_in_sub = false;
		}
		} else {
		MoveThroughSub(button, settings_size, settings_sub);
	}
}
void Settings_SVA(uint8_t button) {
	lcd.setCursor(0, 0);
	lcd.print("SVA             ");
	lcd.setCursor(0, 1);
	if (button == (uint8_t) btnUP) {
		sva = true;
	}
	if (button == (uint8_t) btnDOWN) {
		sva = false;
	}
	lcd.setCursor(0, 1);
	if (sva){
		lcd.print("Ja              ");
		} else {
		lcd.print("Nein            ");
	}
}

The Back-To-Root-entry:

void BackToRoot(uint8_t button) {
	lcd.setCursor(0, 0);
	lcd.print("Zur\365ck ins      ");
	lcd.setCursor(0, 1);
	lcd.print("Hauptmen\365?      ");

	if (button == (uint8_t) btnSELECT) {
		lcd.setCursor(0, 1);
		lcd.print("SELECT");
		is_in_root = true;
		was_in_sub = true;
		sub_counter = 0;
	}
}

And finally the definition of the menues.

const Subs settings_sub[] = {Settings_SVA, BackToRoot};
const uint8_t settings_size = 2;

const Subs timelapse_sub[] = {Testsub, BackToRoot};
const uint8_t timelapse_size = 2;

const Main root[] = {Timelapse, Settings};
const uint8_t root_size = 2;

TimelapseController.ino (1.75 KB)

Values.h (3.14 KB)

Where is the value of button coming from ? Is it possible that the system does actually return to the root but with button still acting as if it is pressed it moves straight away to the first sub menu ?

I have not downloaded the files. It would have been easier if you had posted them here.

Ok, here are the complete files, the .ino:

//#include <MerlinMount/merlin_mount.h>
#include <LiquidCrystal/LiquidCrystal.h>
#include "Values.h"
//int counter = 0;

uint8_t analog_read_LCD_buttons()
{
	current_key = (uint16_t) analogRead(0);

	if (current_key > 1000) return btnNONE;
	if (current_key < 50)   return btnRIGHT;
	if (current_key < 250)  return btnUP;
	if (current_key < 450)  return btnDOWN;
	if (current_key < 650)  return btnLEFT;
	if (current_key < 850)  return btnSELECT;

	return btnNONE;
}

uint16_t cnt = 0;
uint8_t read_LCD_buttons()
{
	tmp_key = analog_read_LCD_buttons();
	
	if ((tmp_key == btnUP) || (tmp_key == btnDOWN)) {
		cnt++;
		if (!(cnt % 25)){
			previous_key = current_key;
			return tmp_key;
		}
		} else	if (tmp_key != previous_key) {

		previous_key = tmp_key;
		return tmp_key;
	}

	previous_key = current_key;
	return btnNONE;
}

void moveThroughRoot(uint8_t Button) {
	if (was_in_sub) {
		lcd.clear();
		is_in_root = true;
		was_in_sub = false;
		Button = btnNONE;
		(*root[main_counter])(btnNONE);
	}

	if (is_in_root) {
		if (Button == (uint8_t)btnRIGHT) {
			if ((main_counter + 1) >= root_size){
				main_counter = 0;
				} else {
				main_counter++;
			}
		}
		
		if (Button == (uint8_t)btnLEFT) {
			if ((main_counter - 1) < 0){
				main_counter = root_size-1;
				} else {
				main_counter--;
			}
		}
		(*root[main_counter])(Button);
		} else {
		(*root[main_counter])(Button);
	}
}

void setup()
{
	
	//merlin.init();
	
	lcd.begin(16, 2);
	lcd.setCursor(0,0);
	lcd.print("Initialisierung");
	lcd.setCursor(0,1);
	lcd.print("abgeschlossen.");
	while (read_LCD_buttons() == btnNONE) {};
	lcd.clear();
	(*root[0])(btnNONE);
	//lcd.print("Push the buttons");

}

void loop()
{
	lcd_key = read_LCD_buttons();
	moveThroughRoot(lcd_key);

}

And Values.h

#ifndef VALUES_H

#define VALUES_H
#include <Arduino.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

uint8_t lcd_key     = 0;
uint16_t current_key  = 0;
uint8_t tmp_key = 0;
uint16_t previous_key  = 0;
uint16_t counter_pressed = 0;
const uint16_t compare = 15;
bool cont_increase = false;

bool sva = true;

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

//#define PI (3.141592)

int8_t main_counter = 0;
int8_t sub_counter = 0;
bool is_in_root = true;
bool was_in_sub = false;

typedef void (*Main)(uint8_t button);
typedef void (*Subs)(uint8_t button);

void MoveThroughSub(uint8_t button,uint8_t limit,const Subs subitems[]) {
	if (!is_in_root) {
		if (button == (uint8_t) btnRIGHT) {
			if ((sub_counter + 1) >= limit){
				sub_counter = 0;
				} else {
				sub_counter++;
			}
		}
		
		if (button == (uint8_t) btnLEFT) {
			if ((sub_counter - 1) < 0){
				sub_counter = limit-1;
				} else {
				sub_counter--;
			}
		}
		(*subitems[sub_counter])(button);
	}
}



void Settings_SVA(uint8_t button) {
	lcd.setCursor(0, 0);
	lcd.print("SVA             ");
	lcd.setCursor(0, 1);
	if (button == (uint8_t) btnUP) {
		sva = true;
	}
	if (button == (uint8_t) btnDOWN) {
		sva = false;
	}
	lcd.setCursor(0, 1);
	if (sva){
		lcd.print("Ja              ");
		} else {
		lcd.print("Nein            ");
	}
}

void BackToRoot(uint8_t button) {
	lcd.setCursor(0, 0);
	lcd.print("Zur\365ck ins      ");
	lcd.setCursor(0, 1);
	lcd.print("Hauptmen\365?      ");

	if (button == (uint8_t) btnSELECT) {
		lcd.setCursor(0, 1);
		lcd.print("SELECT");
		is_in_root = true;
		was_in_sub = true;
		sub_counter = 0;
	}
}

void Testsub(uint8_t button) {
	lcd.setCursor(0,0);
	lcd.print("TEST            ");
	lcd.setCursor(9,1);
	lcd.print(millis()/1000);
	lcd.setCursor(0,1);
	switch (button)
	{
		case (uint8_t) btnRIGHT:
		{
			lcd.print("RIGHT ");
			break;
		}
		case (uint8_t) btnLEFT:
		{
			lcd.print("LEFT   ");
			break;
		}
		case (uint8_t) btnUP:
		{
			lcd.print("UP    ");
			break;
		}
		case (uint8_t) btnDOWN:
		{
			lcd.print("DOWN  ");
			break;
		}
		case (uint8_t) btnSELECT:
		{
			lcd.print("SELECT");
			break;
		}
		case (uint8_t) btnNONE:
		{
			lcd.print("NONE  ");
			break;
		}
	}
}

const Subs settings_sub[] = {Settings_SVA, BackToRoot};
const uint8_t settings_size = 2;
const Subs timelapse_sub[] = {Testsub, BackToRoot};
const uint8_t timelapse_size = 2;

void Timelapse(uint8_t button) {
	if (is_in_root) {
		lcd.setCursor(0, 0);
		lcd.print("Zeitraffer      ");
		if (button == btnSELECT) {
			is_in_root = false;
			was_in_sub = false;
		}
		} else {
		MoveThroughSub(button,timelapse_size, timelapse_sub);
	}
}

void Settings(uint8_t button) {
	if (is_in_root) {
		lcd.setCursor(0, 0);
		lcd.print("Einstellungen   ");
		if (button == (uint8_t) btnSELECT) {
			is_in_root = false;
			was_in_sub = false;
		}
		} else {
		MoveThroughSub(button, settings_size, settings_sub);
	}
}

const Main root[] = {Timelapse, Settings};
const uint8_t root_size = 2;


#endif /* VALUES_H */

UKHeliBob:
Where is the value of button coming from ? Is it possible that the system does actually return to the root but with button still acting as if it is pressed it moves straight away to the first sub menu ?

Thank you, that was the problem, I accidently used a wrong variable in the read-out-function for the pseudo-debounce, but I didn’t rewrote it when I rewrote my menues:

uint8_t analog_read_LCD_buttons() {
 current_key = (uint16_t) analogRead(0);
 
 if (current_key > 1000) { return btnNONE; }
 
 if (current_key < 50)   { return btnRIGHT; }
 
 if (current_key < 250)  { return btnUP; }
 
 if (current_key < 450)  { return btnDOWN; }
 
 if (current_key < 650)  { return btnLEFT; }
 
 if (current_key < 850)  { return btnSELECT; }
 
 return btnNONE;
}

uint8_t read_LCD_buttons() {
 tmp_key = analog_read_LCD_buttons();
 
 if ((tmp_key == btnUP) || (tmp_key == btnDOWN)) {
 cnt++;
 
 if (!(cnt % 15)) {
 previous_key = tmp_key;
 return tmp_key;
 }
 }
 
 else if (tmp_key != previous_key) {
 
 previous_key = tmp_key;
 return tmp_key;
 }
 
 previous_key = tmp_key;
 return btnNONE;
}
return tmp_key;

Why do you need to return a global variable?

Your code uses far too many global variables when local variables would be more appropriate.