Sprung in Funktionen und zurück klappt nicht

Ich sitze heir seit paar tagen dran und ich komme nicht weiter.
Aus der loop kann ich in das menü springen, aber ich komme aus den
einzelnen funktionen nicht mehr zurück zur loop oder vom submenu zur main.

Ich habe nichts im netz gefunden, deshalb hab ich mal nach meinem wissen
losgelegt, aber nun bin ich am ende.

Kann mir jemand paar tipps geben was ich hier falsch mache?

#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,10,9,8,7); //Create LCD object (RS,E,DB4,DB5,DB6,DB7)

#include <rotary.h> //github.com/brianlow/Rotary
Rotary r=Rotary(3,2);

const int RotSW=4; //Pushbutton
int butState=0;
int cnt=0;
int subMenu;
int rotTurn=0;

void setup()
 {pinMode(RotSW,INPUT_PULLUP); lcd.begin(20,4); lcd.clear();
  PCICR|=(1<<PCIE2); PCMSK2|=(1<<PCINT18)|(1<<PCINT19); sei();
 }

void loop()
 {lcd.clear(); lcd.setCursor(0,0); lcd.print("Your in the Loop");

  butState=getButton(); //Read state of pushbutton
  while(butState==3){Main();} //Button long press -> Menü aufrufen

  delay(100);
 }


void Main()
 {lcd.setCursor(0,0); lcd.print("Set Exit            ");
  if(cnt==1){rotTurn++; cnt=0; if(rotTurn>1)rotTurn=0;} //Encoder turn right
  if(cnt==2){rotTurn--; cnt=0; if(rotTurn<0)rotTurn=1;} //Encoder turn left
  butState=getButton(); //Read state of pushbutton
  switch(rotTurn)
   {case 0: //In SubMenu() springen
       lcd.setCursor(0,1); lcd.print("---                 ");
       if(butState==1&&subMenu!=1){subMenu=1; SubMenu();}
       break;
    case 1: //Menü() verlassen und in die Loop() zurückspringen
       lcd.setCursor(0,1); lcd.print("    ----            ");
       if(butState==1){butState=0; loop();} //Springt nicht zurück in die Loop()
       break;
    default: break;
   }
  butState=3;
 }


void SubMenu()
 {lcd.setCursor(0,0); lcd.print("Set                 "); lcd.setCursor(0,1); lcd.print("                    ");
  if(cnt==1){rotTurn++; cnt=0;} //Encoder turn right
  if(cnt==2){rotTurn--; cnt=0;} //Encoder turn left
  if(rotTurn<0){rotTurn=255;}
  if(rotTurn>255){rotTurn=0;}
  lcd.setCursor(0,1); lcd.print("RotTurn (0-255): "); lcd.print(rotTurn);

  butState=getButton(); //Read state of pushbutton
  if(butState==1){butState=0; Main();} //Sprint nicht zurück in die Main()

  while(subMenu==1) {delay(100); SubMenu();} //Verläßt das SubMenu() nach paar sekunden!
 }



ISR(PCINT2_vect) //Interrupt service routine for encoder
 {unsigned char result=r.process();
  if(result==DIR_CW)      {cnt=1;}
  else if(result==DIR_CCW){cnt=2;}
 }

byte getButton() //Read Pushbutton with debounce
 {butState=0; unsigned long dnTime, upTime;
  if(!digitalRead(RotSW))
   {delay(20);
    if(!digitalRead(RotSW)) {dnTime=millis(); while(!digitalRead(RotSW));}
    upTime=(millis()-dnTime);
    if(upTime>=1000)     return butState=3; //Long press
    else if(upTime>=400) return butState=2; //Medium press
    else                 return butState=1; //Short click
   }
  return butState=0;
 }

Du darfst die Funktion nicht main() nennen, das ist ein reservierter Namen.
Des weiteren weigere ich mich Deinen Sketch zu lesen. Formatiere ihn leserlich und schreibe nicht mehrere Funktionen in eine Zeile.

Grüße Uwe

Ich glaube Main() ist nicht reserviert, sondern main().

Gruß Tommy

Hmm...

Funktionen ruft man auf, man springt da nicht rein.

Und wenn überhaupt, dann kann das nur longjump().
Aber wer longjump() nutzt, der frisst auch kleine Kinder.
Ganz selten, Sinn das macht.
:o :o :o

OnlySketching:
Kann mir jemand paar tipps geben was ich hier falsch mache?

int cnt=0;

cnt muss volatile deklariert werden.

Da es ein int (2 Bytes) ist, müsste man auch eigentlich alle Zugriffe aus dem Hauptprogramm atomar gestalten,
in deinem Fall wäre es einfacher cnt zu einem (unsigned) char zu machen.

Das sei() in setup ist ohne vorhergehendes cli() sinnfrei, Interrupts sind standardmäßig eingeschaltet.

Pinchange Interrupts auf den externen Interruptpins halte ich für Verschwendung und komplizierter.
Wenn man eine Library benutzt die etwas anderes nicht unterstützt, von mir aus.

Deine while-delay Technik finde ich zum Speien.
Mehrere Statements in einer Zeile benutze ich normalerweise nicht.

Ok ich habe den Code jetzt zerlegt, so das man ihn besser lesen kann

Die Buttonabfrage und die Encoderabfrage funktionieren gut,
es geht nur im diese Menüstruktur, ob man das so macht oder nicht.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,10,9,8,7); //Create LCD object (RS,E,DB4,DB5,DB6,DB7)

#include <rotary.h> //github.com/brianlow/Rotary
Rotary r=Rotary(3,2);

const int RotSW=4; //Pushbutton
int butState=0;
unsigned char cnt=0;
int subMenu;
int rotTurn=0;

void setup()
 {pinMode(RotSW,INPUT_PULLUP);
  lcd.begin(20,4);
  lcd.clear();
  PCICR|=(1<<PCIE2);
  PCMSK2|=(1<<PCINT18)|(1<<PCINT19);
 }

void loop()
 {lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Your in the Loop");

  butState=getButton(); //Read state of pushbutton
  while(butState==3)
   {
    Main_Menu();
   } //Button long press -> Menü aufrufen

  delay(100);
 }


void Main_Menu()
 {lcd.setCursor(0,0);
  lcd.print("Set Exit            ");
  if(cnt==1)
   {
    rotTurn++;
    cnt=0;
    if(rotTurn>1)
     {
      rotTurn=0;
     }
   } //Encoder turn right
  if(cnt==2)
   {rotTurn--;
    cnt=0;
    if(rotTurn<0)
     {
      rotTurn=1;
     }
   } //Encoder turn left
  butState=getButton(); //Read state of pushbutton
  switch(rotTurn)
   {case 0: //In Sub_Menu() springen
       lcd.setCursor(0,1);
       lcd.print("---                 ");
       if(butState==1  &&  subMenu!=1)
        {
         subMenu=1;
         Sub_Menu();
        }
       break;
    case 1: //Menü() verlassen und in die Loop() zurückspringen
       lcd.setCursor(0,1);
       lcd.print("    ----            ");
       if(butState==1)
        {
         butState=0;
         loop();
        } //Springt nicht zurück in die Loop()
       break;
    default:
       break;
   }
  butState=3;
 }


void Sub_Menu()
 {lcd.setCursor(0,0);
  lcd.print("Set                 ");
  lcd.setCursor(0,1);
  lcd.print("                    ");
  if(cnt==1)
   {
    rotTurn++;
    cnt=0;
   } //Encoder turn right
  if(cnt==2)
   {
    rotTurn--;
    cnt=0;
   } //Encoder turn left
  if(rotTurn<0)
   {
    rotTurn=255;
   }
  if(rotTurn>255)
   {
    rotTurn=0;
   }
  lcd.setCursor(0,1);
  lcd.print("RotTurn (0-255): ");
  lcd.print(rotTurn);

  butState=getButton(); //Read state of pushbutton
  if(butState==1)
   {
    butState=0;
    Main_Menu();
   } //Sprint nicht zurück in die Main_Menu()

  while(subMenu==1)
   {
    delay(100);
    Sub_Menu();
   } //Verläßt das Sub_Menu() nach paar sekunden!
 }



ISR(PCINT2_vect) //Interrupt service routine for encoder
 {unsigned char result=r.process();
  if(result==DIR_CW)
   {
    cnt=1;
   }
  else if(result==DIR_CCW)
   {
    cnt=2;
   }
 }

byte getButton() //Read Pushbutton with debounce
 {butState=0;
  unsigned long dnTime, upTime;
  if(!digitalRead(RotSW))
   {delay(20);
    if(!digitalRead(RotSW))
     {
      dnTime=millis();
      while(!digitalRead(RotSW));
     }
    upTime=(millis()-dnTime);
    if(upTime>=1000)     return butState=3; //Long press
    else if(upTime>=400) return butState=2; //Medium press
    else                 return butState=1; //Short click
   }
  return butState=0;
 }

OnlySketching:
Die Buttonabfrage und die Encoderabfrage funktionieren gut,
es geht nur im diese Menüstruktur, ob man das so macht oder nicht.

Meiner Ansicht nach: nein.

Also ich habe den sketch hier laufen auf dem display...buttons und encoder tun soweit.
Wiesso meinst du das es nicht geht?

Funktionen sind kein goto! Am Anfang der Funktion wird die Rücksprung-Adresse auf dem Stack gespeichert. Und am Ende kehrt sie wieder dahin wo sie aufgerufen wurde. Das bedeutet auch dass du normal nicht eine Funktion sich selbst aufrufen lassen kannst. Dadurch wird der Speicher ganz schnell voll.

Und auch nicht in loop() main() aufrufen und dann in loop() wieder main(). Das ist das gleiche. Die Funktion weiß von selbst wo es weitergeht

OnlySketching:
Wiesso meinst du das es nicht geht?

Habe ich nicht gesagt, lies es nochmal durch, ich habe diese Frage beantwortet:

OnlySketching:
es geht nur im diese Menüstruktur, ob man das so macht oder nicht.

und zwar mit nein (aus meiner Sicht, es gibt natürlich keine Vorschriften, wie man ein Programm schreibt).

In der loop soll später (falls das mit dem menü klappt) eine led gedimmt werden, mit dem wert aus der 0-255 aus der void Sub_Menu().

Wenn der Nano strom bekommt, soll kein Menü zu sehen sein, das soll nur für die einstellungen sein.
Deshalb muß man 1,5 sek den button gedrückt halten, erst dann gehts zum menü.
Das geht ja auch soweit....und ich kann den wert 0-255 ändern, alles andere, wie menü verlassen geht nicht.

Whandall:
Habe ich nicht gesagt, lies es nochmal durch, ich habe diese Frage beantwortet:

und zwar mit nein (aus meiner Sicht, es gibt natürlich keine Vorschriften, wie man ein Programm schreibt).

Ahh ok du meintest, das es so nicht macht....ja das glaub ich dir, ich hab sowas noch nie gemacht, deshalb sieht das so aus...vielleicht werde ich in 3 monate über manen code lachen...aber zZ is es das beste was mir einfällt.

Deshalb möchte ich ja auch das farum nutzen, um das besser oder richtig zu machen.

Hi

Du wirst in drei Monaten nicht über Deinen alten Code lachen - Du wirst Dich ernsthaft fragen, 'was der Künstler Da eigentlich machen wollte'.
Das aber nicht nur, weil Du besser wirst - sondern auch, weil Du kaum Kommentare geschrieben hast.
Damit meine ich aber nicht
unsigned int zahl; Zahl, ohne Vorzeichen
... weil Das erklärt sich von selber - aber was 'zahl' hier macht, wäre eine Nennung wert.

Bei Deinem Sketch fehlt mir momentan der Elan, Den 'durchzukauen' - in Kurzform, was funktioniert nicht so, wie Du willst?
Bzw. Was erwartest Du und was bekommst Du statt Dessen?

MfG

postmaster-ino:
Bei Deinem Sketch fehlt mir momentan der Elan, Den 'durchzukauen' - in Kurzform, was funktioniert nicht so, wie Du willst?

Der grundlegendste Fehler ist dass er nicht versteht was Funktionen eigentlich machen

Wenn man das drumherum mal weglässt:

void loop()
{
   Main_Menu();
}

void Main_Menu()
{
   Sub_Menu();

   ...

   loop();
}

void Sub_Menu()
{
   Main_Menu();
}

Das Problem ist viel elementarer als "Mein Programm macht nicht was ich will"

Um das klar zu machen: hier geht es nicht um schlechten Stil, sondern dass der Controller so nicht arbeiten kann. Selbst wenn das im Moment geht (wahrscheinlich weil die Funktionsaufrufe nach langsamen Benutzereingaben erfolgen), wird er das auf Dauer sicherlich nicht tun

Eine beabsichtigte Rekursion, auf AVRs, ist schon bedenklich.
Aber eine unbeabsichtigte indirekte Rekursion kann/wird einem das Genick brechen.

Ich sehe das auch so:
Das eigentliche Problem liegt viel tiefer, als die oberflächlichen Symptome, auf den ersten Blick, vermuten lassen.

Ok was schlagt ihr mir jetzt vor? oder wars das mit dem menü?
Dann vergessen wir den code den ich gepostet habe komplett und fangen bei 0 an.

Ich schreib mal was ich von dem Menü erwarte:

  • Nach dem der nano saft bekommt, soll eine LED an einem PWM pin mit halber helligkeit leuchten zB 128.
  • Wenn man den Button des Encoder länger gedrückt hällt, soll in das Menü aufgerufen werden.
  • dort können zB 3 untermenüs sein (für 3 LEDs an 3 PWM pins) die man mit dem encoder durchscrollen kann.
  • mit jedem weiteren kurz-klick soll eins der 3 untermenüs aufgerufen werden, in dem man jetzt einen wert 0-255 einstellen kann.
loop
   - menu (long press)
      - submenu1 (klick) -> 0-255 (werte mit encoder ändern)
        oder zurück zum menu 

      - submenu2 (klick) -> 0-255 (werte mit encoder ändern)
        oder zurück zum menu 

      - submenu3 (klick) -> 0-255 (werte mit encoder ändern)
        oder zurück zum menu 

      - exit  (klick) -> menü beenden, zurück zu loop

Warum nimmst du nicht einfach das hier:
https://forum.arduino.cc/index.php?topic=73816.0
Ist zwar auch mit Programmierung verbunden, aber du musst das "Rad" nicht neu erfinden.

Schau dir Zustandsautomaten an. Da hast du eine Variable die dir angibt in welchem Menu du dich befindest. Diese kann man je nach aktuellem Menü und Eingabe ändern.

In Funktionen bleibt man so nie fest hängen. Je nach Zustand wird eine andere Funktion ausgeführt. Und wenn nichts zu tun ist beendet diese sich sofort wieder.
Dadurch kommt loop() immer wieder gleich dran. Es ist nämlich auch ein Fehler zu denken du springst von Menü zu Menü und am Ende vielleicht einmal nach loop(). Die Bezeichnung loop() darfst du ruhig wörtlich nehmen. Das ist eine Schleife die ständig arbeitet.

Der Vorgang ist eher so:

void loop()
{
   if zustand == MENU_1    //praktisch wird hier oft switch/case verwendet
      menu_1();
   else  if zustand == MENU_2
     menu_2();


   //hier kann man "gleichzeitig" noch andere Dinge tun!
}

void menu_1()
{
    if eingabe == ...
       zustand = MENU_2;

   ...
}

Menüs kann man dann nur zum Zeitpunkt des Zustandswechels einmal neu zeichnen

@HotSystems
Warum nimmst du nicht einfach das hier:
forum.arduino.cc/index.php?topic=73816.0
Ist zwar auch mit Programmierung verbunden, aber du musst das "Rad" nicht neu erfinden.

Habe ich gerade getestet, verbraucht bereits 11KB mit dem beginner-menü.
Ich möchte erst mal nur 3 RGB werte ändern, dafür knall ich doch keine
11kb Code auf den armen arduino...und dann brauche ich eine ganze Armada an
buttons mit zwischenwiderstände. Sind Encoder verpöhnt, damit kann man
eigentlich alles steuern.

@Serenifly
Die Bezeichnung loop() darfst du ruhig wörtlich nehmen. Das ist eine Schleife die ständig arbeitet.

Ja das ist etwas gewöhnungsbedürftig. Ich verwächsel das immer mit index.php
und dann kann man über die links die webseiten anspringen....und dort verweilen
bis man wieder lust auf die startseite hat.

Ok dann lass ich das mit der menüprogrammierung. Die MenüLibrary ist mir zu stark nach
dem sprichwort "mit Kanonen auf Spatzen schießen".

Danke an alle, eventuell finde ich einen code den ich umbauen kann.

OnlySketching:
Habe ich gerade getestet, verbraucht bereits 11KB mit dem beginner-menü.
Ich möchte erst mal nur 3 RGB werte ändern, dafür knall ich doch keine
11kb Code auf den armen arduino...und dann brauche ich eine ganze Armada an
buttons mit zwischenwiderstände. Sind Encoder verpöhnt, damit kann man
eigentlich alles steuern.

Dann hast du es nicht richtig gelesen. Das Menü geht auch mit Encoder.
Und wieviel Tasten du dranbaust, bleibt dir überlassen.
Ich sehe bei den 11 kb kein Problem, ausser dein Speicher wird knapp.