switch case annidati nella void loop (nokia lcd + arduino)

Salve ragazzi, sono nuovo e voglio porvi un problema.
Sto usando un lcd nokia+joistick della nuelectronics su arduino 2009.

Vi posto il codice che da come esempio la nuelectronics:

void setup()
{

  lcd.LCD_3310_init();
  lcd.LCD_3310_clear();
  
   //menu initialization
   init_MENU();
   current_menu_item = 0;	
}



void loop()
{
    byte i;
    for(i=0; i<NUM_KEYS; i++){
       if(button_flag[i] !=0){
            
            button_flag[i]=0;  // reset button flag
			switch(i){

		case UP_KEY:
		// current item to normal display
		lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
	        current_menu_item -=1;
		if(current_menu_item <0)  current_menu_item = NUM_MENU_ITEM -1;
	       // next item to highlight display
	       lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
	       break;
	      case DOWN_KEY:
	     // current item to normal display
	     lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
	     current_menu_item +=1;
	     if(current_menu_item >(NUM_MENU_ITEM-1))  current_menu_item = 0;
	     // next item to highlight display
	     lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
	     break;
	     case LEFT_KEY:
             init_MENU();
	     current_menu_item = 0;
	     break;   
	     case RIGHT_KEY:
             lcd.LCD_3310_clear();
	     (*menu_funcs[current_menu_item])();
              lcd.LCD_3310_clear();
	      init_MENU();
	     current_menu_item = 0;           
	     break;	
			}
				
		}
    }
}

Vi spiego :

Viene creato un menù con la init_MENU() . appena creato il menù parte lo switch nella loop per scorrerlo.
Si entra nelle scelte del menù con il tasto destro del joistick e parte un funzione (*menu_funcs[current_menu_item])();
come potete vedere se si scorre il menù aumenta current_menu_item, quindi in base alla voce del menù selezionato si entra in una relativa finestra.
Quello che ho fatto è che appena viene premuto il tasto sx lancio un init_menu_1(), solo che a questo punto dovrebbe partire uno switch nel case LEFT_KEY solo che tale switch non lavora, e quindi il sotto menù non scorre.

la domanda è questa:
Come faccio a creare un switch che dipende dallo switch sopracitato?
Ovvero sembra che nel case LEFT_KEY non gira il loop e quindi non posso fare nulla di dinamico.

dovrei fare una cosa di questo genere:

void setup()
{

  lcd.LCD_3310_init();
  lcd.LCD_3310_clear();
  
   //menu initialization
   init_MENU();
   current_menu_item = 0;	
}



void loop()
{
    byte i;
    for(i=0; i<NUM_KEYS; i++){
       if(button_flag[i] !=0){
            
            button_flag[i]=0;  // reset button flag
			switch(i){

		case UP_KEY:
		// current item to normal display
		lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
	        current_menu_item -=1;
		if(current_menu_item <0)  current_menu_item = NUM_MENU_ITEM -1;
	       // next item to highlight display
	       lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
	       break;
	      case DOWN_KEY:
	     // current item to normal display
	     lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
	     current_menu_item +=1;
	     if(current_menu_item >(NUM_MENU_ITEM-1))  current_menu_item = 0;
	     // next item to highlight display
	     lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
	     break;
	     case LEFT_KEY:
             init_MENU_1();
	     current_menu_item = 0;
	    /*----------------------------------------------------------------*/
              
            byte j;
    for(j=0; j<NUM_KEYS; j++){
       if(button_flag[i] !=0){
            
            button_flag[j]=0;  // reset button flag
			switch(j){

		case UP_KEY:
		// current item to normal display
		lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
	        current_menu_item -=1;
		if(current_menu_item <0)  current_menu_item = NUM_MENU_ITEM -1;
	       // next item to highlight display
	       lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
	       break;

           /*-----------------------------------------------------------------*/
             break;   


	     case RIGHT_KEY:
             lcd.LCD_3310_clear();
	     (*menu_funcs[current_menu_item])();
              lcd.LCD_3310_clear();
	      init_MENU();
	     current_menu_item = 0;           
	     break;	
			}
				
		}
    }
}

Speriamo che capite il mio problema altrimetni sono costretto a postarvi tutto il codice

Potresti annidare una nuova subroutine per gestire il sottomenu. Però ricordati che gli Atmega non sono multitasking per cui per eseguire più compiti insieme (nel tuo caso: leggere il joystick per capire cosa l'utente sta facendo e gestire un menu) devi strutturare il tuo codice per fare ciò. Il compito è difficile ma non impossibile.

Io vorrei creare una funzione nel loop ad esempio void_loop_1() contenete lo switch case, e lanciarla appena ho lanciato init_MENU_1() ma non riesco a definire funzioni nella loop, mi da sempre errore.

Cosa intendi per subroutine? scusa ma non sono un asso! tnx

Che intendi che non riesci a definire funzioni nella loop?
Le funzioni si chiamano, non si definiscono dentro altre funzioni.

Cioè:

void loop() {
funzione1();
}

void funzione1(){
//niente
}

PS:
una subroutine è una funzione che non ritorna nessun valore.
Una funzione dichiarata void nome_funzione() è una subroutine, perché restituisce "void", cioè nulla.
Se invece hai int nome_funzione() allora la funzione restituisce un valore int.

Una volta si chiamavano subroutine le porzioni di codice che non restituivano dati. Ora son tutte indicate come funzioni.... eh, s'invecchia... :sweat_smile:

ciao, si io la subroutine la chiamo funzione void.

Il problema è che se in uno dei case richiamo una funziono void con altro switch definito fuori dalla loop non mi funziona!!

Cmq ora al posto di fare sottomenù faccio solo un menù principale più lungo , solo che ho un altro problemino. ti posto il codice di esempio della nuelctronics e ti spiego:

#include "nokia_3310_lcd.h"
#include "avr_bmp.h"

//keypad debounce parameter
#define DEBOUNCE_MAX 15
#define DEBOUNCE_ON  10
#define DEBOUNCE_OFF 3 

#define NUM_KEYS 5

#define NUM_MENU_ITEM	4  // devo arrivare fino a 9

// joystick number
#define UP_KEY 3
#define LEFT_KEY 0
#define CENTER_KEY 1
#define DOWN_KEY 2
#define RIGHT_KEY 4

// menu starting points

#define MENU_X	10		// 0-83
#define MENU_Y	1		// 0-5



// adc preset value, represent top value,incl. noise & margin,that the adc reads, when a key is pressed
// set noise & margin = 30 (0.15V@5V)
int  adc_key_val[5] ={30, 150, 360, 535, 760 };

// debounce counters
byte button_count[NUM_KEYS];
// button status - pressed/released
byte button_status[NUM_KEYS];
// button on flags for user program 
byte button_flag[NUM_KEYS];

// menu definition
char menu_items[NUM_MENU_ITEM][12]={
	"TEMPERATURE",
	"CHAR MAP",
	"BITMAP",
	"ABOUT"	
};

void (*menu_funcs[NUM_MENU_ITEM])(void) = {
	temperature,
	charmap,
	bitmap,
	about,

// qui mi servono altre voci

};

char current_menu_item;


Nokia_3310_lcd lcd=Nokia_3310_lcd();

void setup()
{
    
   // setup interrupt-driven keypad arrays  
   // reset button arrays
   for(byte i=0; i<NUM_KEYS; i++){
     button_count[i]=0;
     button_status[i]=0;
     button_flag[i]=0;
   }
  
  // Setup timer2 -- Prescaler/256
  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);
  TCCR2B = (1<<CS22)|(1<<CS21);      
  
  ASSR |=(0<<AS2);

   // Use normal mode  
   TCCR2A =0;    
     //Timer2 Overflow Interrupt Enable  
     TIMSK2 |= (0<<OCIE2A);
     TCNT2=0x6;  // counting starts from 6;  
   TIMSK2 = (1<<TOIE2);    
   
                  
 
  SREG|=1<<SREG_I;
  
  lcd.LCD_3310_init();
  lcd.LCD_3310_clear();
  
   //menu initialization
   init_MENU();
   current_menu_item = 0;	
}


/* loop */

void loop()
{
    byte i;
    for(i=0; i<NUM_KEYS; i++){
       if(button_flag[i] !=0){
            
            button_flag[i]=0;  // reset button flag
			switch(i){

				case UP_KEY:
					// current item to normal display
			lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
					current_menu_item -=1;
					if(current_menu_item <0)  current_menu_item = NUM_MENU_ITEM -1;
					// next item to highlight display
			lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
					break;
				case DOWN_KEY:
				// current item to normal display
			lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
					current_menu_item +=1;
					if(current_menu_item >(NUM_MENU_ITEM-1))  current_menu_item = 0;
					// next item to highlight display
			lcd.LCD_3310_write_string(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
					break;
				case LEFT_KEY:
          init_MENU();
					current_menu_item = 0;
					break;   
				case RIGHT_KEY:
        	lcd.LCD_3310_clear();
				 	(*menu_funcs[current_menu_item])();
          lcd.LCD_3310_clear();
				 	init_MENU();
					current_menu_item = 0;           
					break;	
			}
				
		}
    }
}

/* menu functions */

void init_MENU(void){

  byte i;
  
  lcd.LCD_3310_clear();

    lcd.LCD_3310_write_string(MENU_X, MENU_Y, menu_items[0], MENU_HIGHLIGHT );
	
  for (i=1; i<NUM_MENU_ITEM; i++){
    lcd.LCD_3310_write_string(MENU_X, MENU_Y+i, menu_items[i], MENU_NORMAL);
  }
	
	
}

// waiting for center key press
void waitfor_OKkey(){
  byte i;
  byte key = 0xFF;
	while (key!= CENTER_KEY){
    for(i=0; i<NUM_KEYS; i++){
       if(button_flag[i] !=0){
           button_flag[i]=0;  // reset button flag
           if(i== CENTER_KEY) key=CENTER_KEY;
        }
     }
   }
		
}

void temperature()
{
        lcd.LCD_3310_write_string_big(10, 1, "+12.30", MENU_NORMAL);
        lcd.LCD_3310_write_string(78, 2, "C", MENU_NORMAL);
        lcd.LCD_3310_write_string(38, 5, "OK", MENU_HIGHLIGHT );
	waitfor_OKkey();
}

void charmap(){
  char i,j;
   for(i=0; i<5; i++){
    for(j=0; j<14; j++){
      lcd.LCD_3310_set_XY(j*6,i);
      lcd.LCD_3310_write_char(i*14+j+32, MENU_NORMAL);
      }
    }
  
  
  lcd.LCD_3310_write_string(38, 5, "OK", MENU_HIGHLIGHT );
	waitfor_OKkey();   
}

void bitmap(){
  lcd.LCD_3310_draw_bmp_pixel(20,1, AVR_bmp, 48,24);
  lcd.LCD_3310_write_string(38, 5, "OK", MENU_HIGHLIGHT );
	waitfor_OKkey();
}


void about(){
  
  lcd.LCD_3310_write_string( 0, 1, "Nokia 3310 LCD", MENU_NORMAL);
  lcd.LCD_3310_write_string( 0, 3, "nuelectronics", MENU_NORMAL);
	lcd.LCD_3310_write_string(38, 5, "OK", MENU_HIGHLIGHT );
	waitfor_OKkey();
	
		
}



// The followinging are interrupt-driven keypad reading functions
//  which includes DEBOUNCE ON/OFF mechanism, and continuous pressing detection


// Convert ADC value to key number
char get_key(unsigned int input)
{
	char k;
    
	for (k = 0; k < NUM_KEYS; k++)
	{
		if (input < adc_key_val[k])
		{
           
    return k;
        }
	}
    
    if (k >= NUM_KEYS)
        k = -1;     // No valid key pressed
    
    return k;
}

void update_adc_key(){
  int adc_key_in;
  char key_in;
  byte i;
  
  adc_key_in = analogRead(0);
  key_in = get_key(adc_key_in);
  for(i=0; i<NUM_KEYS; i++)
  {
    if(key_in==i)  //one key is pressed 
    { 
      if(button_count[i]<DEBOUNCE_MAX)
      {
        button_count[i]++;
        if(button_count[i]>DEBOUNCE_ON)
        {
          if(button_status[i] == 0)
          {
            button_flag[i] = 1;
            button_status[i] = 1; //button debounced to 'pressed' status
          }
		  
        }
      }
	
    }
    else // no button pressed
    {
      if (button_count[i] >0)
      {  
		button_flag[i] = 0;	
		button_count[i]--;
        if(button_count[i]<DEBOUNCE_OFF){
          button_status[i]=0;   //button debounced to 'released' status
        }
      }
    }
    
  }
}

// Timer2 interrupt routine -
// 1/(160000000/256/(256-6)) = 4ms interval

ISR(TIMER2_OVF_vect) {  
  TCNT2  = 6;
  update_adc_key();
}

se ci fai caso, nel case DOWN_KEY , sul display viene riscritto di volta in volta la riga che sto scorrendo.
Il punto è che vine riscritta con la funzione write una riga nella posizione x,y+contatore
ma siccome ci sono solo sei righe nel display, una volta che modifico il contatore fino a NUM_MENU_ITEM=9 ovviamente a partire da y+5 non viene scritto nient'altro.
A me nel case downkey serve cmq il contatore current_menu_item che arriva fino a 9, ma da un certo punto in poi devo riniziare a scrivere da (x,y+0)

mi impicco con i vari for while e if e non riesco a far funzionare il tutto.
Se ti servono le librerie che magari vuoi provare sono su
http://www.nuelectronics.com/estore/index.php?main_page=product_info&cPath=1&products_id=12

Devi fare un check e vedere quali voci sono da visualizzare in un dato momento.

PS:
quel display lo conosco bene. L'ho già usato. Il mio Gamepack raccoglie 3 giochini scritti per esso. Dagli un'occhiata. Magari vedi come fare per risolvere certe situazioni.

ciao perdonami, ma non ho capito.
Non basta solo fare nel cas downkey tipo un if current_menu_item<5 fai questo
else fai altro

grazie per le risposte.

Dicevi che non tutte le voci ti entrano a video. Fai un check per vedere quali sono da visualizzare e quali no.

Allora io appena inizia il loop lancio

lcd.LCD_3310_write_string(0, 0, "Titolo:", MENU_NORMAL);

poi mi rimangono 5 righe che le scrivo con init_MENU();

char menu_items[NUM_MENU_ITEM][12]={
	"VOCE1",
	"VOCE2",
	"VOCE3",
	"VOCE4",
        "VOCE5",
	"VOCE6",
	"VOCE7",
	"VOCE8",
        "VOCE9",
	
};

in questo modo vedo il titolo, e 5 voci, poi siccome NUM_MENU_ITEM vale 9 devo usare lo joistik 9 volte in basso per reiniziare.
in questo modo però mai e poi mai riesco a vedere le covi 6789.
siccome è più complicato perchè dovrebbe partire un altro init_MENU() con le voci mancanti a me va bene anche se mi appare una voce alla volta.
infatti io posso risolvere senza far apparire tutto il menù con le 9 voci, ma posso semplicemente sovrascrivere sempre la stessa riga, con il contatore che si aggiorna

Ho risolto per quanto riguarda il menù , solo ora ho un paio di domandine da novizio:
siccome la funzione di libreria del display
void LCD_write_string(unsigned char X,unsigned char Y,char *s, char mode)

come potete vedere scrive solo char* (io gli mando tutto virgolettato):
Come faccio a convertire int in char* oppure char in char* ???

grazie ancora a tutti

Per int->char* puoi usare itoa (http://www.cplusplus.com/reference/clibrary/cstdlib/itoa/)
Per char->char* beh, char è un singolo carattere, puoi benissimo assegnarlo al 1° elemento del tuo char*.

grazie mille leo,
ancora una cosa non riesco a far un contatore come si deve, perchè mi si blocca lo switch del joistick

devo fare un contatore che parte da 1 e che cresce di 1 ogni 10 secondi

counter=1;
while(counter <=6120)
{
Serial.println(counter);
delay(10000);
counter++;
}

questo andrebbe benissimo se non fosse che se lo metto nel setup non mi fa andare avanti

se faccio nella loop solo :

Serial.println(counter);
delay(10000);
counter++;

mi si blocca per 10 sec e non mi fa fare il resto delle cose nella loop e quindi lo switch non scorre e di conseguenza mi si blocca il joistick

grazie

Come detto in tanti post, devi usare un controllo fatto sulla funzione millis().
Cerca nel forum e trovi esempi e spiegazioni.
Delay non va bene perché ferma l'esecuzione del programma.