Go Down

Topic: Projekt: LCDMenuLib / LCDMenuLib2 ( LCDML ) - Menü mit mehreren Ebenen  (Read 315476 times) previous topic - next topic

wayfarer_basta

Hallo Jomelo,
danke für die schnelle Anwort. Leider habe ich kein Beispiel gefunden wie die callBack Funktion mit Parameterübergabe aufgerufen wird.
Ich habe den Parameteraufruf wie im folgenden Codeschnipsel versucht:
Code: [Select]

// For beginners
// LCDML_add(id, prev_layer, new_num, lang_char_array, callback_function)
LCDML_add         (0  , LCDML_0         , 1  , "Weckzeit 1"       , mFunc_Alarm(1));       // this menu function can be found on "LCDML_display_menuFunction" tab


Viele Grüße

Way

Jomelo

Falls du die LCDMenuLib verwendest:

Beispiele -> LCDMenuLib -> AVR -> LCDML_DSIP -> LCDML_with_parameter

LCDML_DISP_addParam( .... )


Falls du die LCDMenuLib2 verwendes ist in jedem Beispiel die Beschreibung wie du Parameter übergeben kannst vorhanden.

LCDML_addAdvanced (16 , LCDML_0         , 5  , NULL,          "Parameter"      , NULL,                0,            _LCDML_TYPE_default);                    // NULL = no menu function
 LCDML_addAdvanced (17 , LCDML_0_5       , 1  , NULL,          "Parameter 1"      , mFunc_para,       10,            _LCDML_TYPE_default);                  

Schau dir bitte nochmal die Beispiele an.

while(!success){try++;}

Lafi2004

kurze Frage, wie muss ich einen Interrupt einbinden damit er auch funktioniert? Ziel ist es Impulse eines Aneometers zu zählen.


Code: [Select]
// *********************************************************************
// Global variables
// *********************************************************************


// Aneometer (Windmessen)
#define REEDPIN 2 // Pin für Reed-Kontakt, Digital-2 für Interrupt 0
#define REEDINTERRUPT 0 // Hardware-Interrupt für den Reed-Pin


Code: [Select]
// *********************************************************************
// INTERRUPT SERVICE ROUTIN (ISR)
// *********************************************************************
  void reedISR(){
//    if (millis() - StartZeitWindMessen >= IntervalWindMessen) // Entprellzeit
//  {
    reedCountSum++;                 // eine Radumdrehung zählen
    reedMillisSum += millis() - StartZeitWindMessen; // Zeit addieren
    StartZeitWindMessen = millis();     // Zeit merken
//  }
}


Code: [Select]

// *********************************************************************
// SETUP
// *********************************************************************
  void setup()
  {
    // serial init; only be needed if serial control is used
    Serial.begin(9600);                // start serial
    Serial.println(F(_LCDML_VERSION)); // only for examples
 
    // Einstellungen Interrupt
    pinMode(REEDPIN, INPUT_PULLUP); // Hallsensor
    attachInterrupt(REEDINTERRUPT, reedISR, LOW);




Leider funktioniert es gar nicht...

postmaster-ino

Hi

Da Du nicht den ganzen Sketch zeigst - bzw. nicht ALLE relevanten Teile - habe ich die Vermutung, daß reedCountSum nicht volatile ist.

Zu Deiner Zeitenzählerei:
statt:
reedMillisSum += millis() - StartZeitWindMessen; // Zeit addieren
-->
besser Das:
reedMillisSum += IntervalWindMessen;

Funktioniert gar nicht besagt, daß
- der Sketch nicht Das macht, was Du willst
- der Sketch GAR NICHTS macht
- der Sketch gar nicht bis zum Target kommt
- das Kompilieren bereits fehl schlägt
- to be continued ...

MfG
anscheinend ist Es nicht erwünscht, einen Foren-internen Link als 'Homepage' einzubinden, damit JEDER nur einen Klick von combie's Liste zum Thema State-Maschine entfernt ist.
... dann eben nicht ...

Lafi2004

Hallo,

zunächst mal lieben Dank für die Antwort. Ich erst ganz frisch dabei, und hab erhebliche Defizite in der Programmierung. Den ganzen Sketch kann ich erst frühestens heute Abend hier einstellen, wobei ich mich vermutlich dann wegen meiner Programmierung schämen muss...

Da Du nicht den ganzen Sketch zeigst - bzw. nicht ALLE relevanten Teile - habe ich die Vermutung, daß reedCountSum nicht volatile ist.
Das wäre durchaus möglich, werde ich prüfen und gegebenenfalls ändern bzw. nochmal Rückfragen was ich da genau machen muss...


Zu Deiner Zeitenzählerei:
statt:
reedMillisSum += millis() - StartZeitWindMessen; // Zeit addieren
-->
besser Das:
reedMillisSum += IntervalWindMessen;
ist das denn genauso genau? Vermutlich kommt es natürlich nicht auf die µsek. an aber das war in meiner Überlegung vermutlich der Grund...

Funktioniert gar nicht besagt, daß
- der Sketch nicht Das macht, was Du willst
- der Sketch GAR NICHTS macht
- der Sketch gar nicht bis zum Target kommt
- das Kompilieren bereits fehl schlägt
- to be continued ...
Sorry dumm von mir (geht nicht, ist keine Fehlerbeschreibung) ;) . Also....
Der Sketch wird kompiliert und in den UNO übertragen, ohne Fehlermeldung.
Der UNO springt scheinbar nicht in die ISR (hier hatte ich zu Testzwecken einen Serial.print eingefügt)

Nochmals vielen Dank für deine Mühe!

postmaster-ino

Hi

Da die serielle Schnittstelle selber von Interrupts abhängt, ist Es keine wirklich gute Idee, so was in einer ISR zu benutzen.
Ein wackelnder Ausgangspin (... wenn's langsam genug ist: eine blinkende LED) geht überall und lässt sich ebenfalls als Hinweis benutzen.
Wenn ein Oszi zur Hand ist (die ganz billigen USB-Oszis gehen hier auch), kann man an der Frequenz oder der Form - man könnte die Bits einer Zahl 'übertragen' - ebenfalls Erkenntnisse gewinnen, was der Sketch macht.

Zum Erhalt der nächsten Trigger-Zeit:
Wenn Du ab Zeitpunkt A möglichst genau alle B Milisekunden eine Aktion ausführen willst, machst Du Das mit
Code: [Select]
if (millis()-A>=B){ ... tu was ...
Wenn Du nun den Zeitpunkt von A um die Wartezeit B verschiebst (+B), wird der nächste Trigger um genau diese 'Zeit' ausgelöst - auch, wenn Deine Funktion ab und zu Mal eine ms 'verliert' - also sich millis() um 1 erhöht während Deine Funktion noch läuft.
Man kann auch, wenn's nicht so auf die genaue Zwischenzeit ankommt
Code: [Select]
A=millis();schreiben - zu 99% bring Das genau das gleiche Ergebnis - aber eben nicht immer.
Z.B. bei sekündlichen Aktionen, also B=1000:
Du kannst Dir ja millis() in Deiner Funktion ausgeben lassen - mit der '+B'.Variante schwankt der Anzeigewert um 1.
1000,2000,3001,4000,5001,6001,7000,...
Ein entstandener Fehler ist nur in dieser 'Runde' relevant, größer wird Er aber nicht.

Mit 'A=millis();' wandert der Wert immer weiter nach oben
1000,2000,3001,4001,5001,6002,7003,8003,...
Hier bleibt der ein Mal entstandene Fehler erhalten und neue Fehler werden hinzu addiert.

Zu dem volatile:
Alle Variablen, Die sowohl in der ISR wir im normalen Code benutzt werden, müssen volatile deklariert sein und, wenn größer als ein Byte (Breite des Datenbus des verwendeten µC) muß der Zugriff 'atomar' erfolgen.
volatile: Die Variable wird IMMER aus dem Speicher gelesen und in den Speicher geschrieben.
Ohne volatile kann der Kompiler Werte in Registern (eine Art Wert-Koffer des AVR-Chip, hat Er 32 Stück von) halten und auf diese Register kann Er 'direkt' - also wirklich in diesem Takt, zugreifen, Zahlen addieren, den Wert verarbeiten. Ansich eine schöne Sache, aber bei einem ISR-Aufruf wird eh 'mit einem weißen Papier' angefangen, also die Variablen, Die die ISR benutzt, kommen definitiv aus dem Speicher und gehen auch Dahin zurück - es ist ja vorher nicht klar, wo der µC gerade war, als der Interrupt ausgelöst wurde.
Davon bekommt aber der normale Programmablauf Nichts mit und der Arduino geht davon aus, daß die Variable, Die Er jetzt gerade eben um 1 erhöht hat, eben genau diesen Wert hat - in Seinem Register steht der Wert ja auch noch drin - im Speicher hat Er Sich aber bereits geändert. Hier arbeitest Du dann im Sketch mit falschen/alten Werten - es scheint, als ob Deine ISR nicht 'zündet'.
atomar: Wenn die Variable größer ist, als der µC 'am Stück' verarbeiten kann, muß man verhinder, daß der µC 'zwischendrin' irgend was macht.
Z.B. kann man ja per Timer-ISR einen Counter hochzählen lassen.
Nun will man diesen Counter im Programm auch benutzen und liest Diesen aus.
Da der Counter größer ist, als der AVR am Stück lesen kann, wird das erste Byte gelesen und (bildlich gesprochen) dem Arduino übergeben.
- Nun wäre eigentlich das nächste Byte dieses Counter dran, aber der Timer hat gerade zugeschlagen und den Counter um 1 erhöht.
Jetzt lesen wir wirklich weiter und lesen 'das andere Byte' des Counter.
Im Normalfall haben wir jetzt einen Counterwert, Der 'um 1 falsch sein könnte' - gibt Schlimmeres.
Wenn aber der Counter von 0x00FF auf 0x0100 gesprungen ist, haben wir
- als Erstes die FF gelesen
- nun hat der Timer den Wert erhöhr
- als Nächstes lesen wir die 01
Unser Wert ist nun 0x01FF, obwohl der Counter vorher 0x00FF war und danach 0x0100 - wir sind 255 Schritte daneben.

Ok, wie machen?
Dafür bieten diverse Lib's Blöcke an, Die Sich dann auch atomic (oder so) nennen.
Oder, man verbietet Interrupts - beim Arduino per noInterrupts(); - und erlaubt nach dem Lesen Interrupts wieder - beim Arduino per interrupts();.
Sollte man auch wirklich tun, sonst hat man keine Millis() mehr, die serielle Schnittstelle funktioniert nicht mehr, eigentlich klappt dann nur noch ziemlich wenig - Das, was den Arduino zum Arduino macht, benutzt die Interrupts, um Dir (und mir natürlich auch) das Leben zu vereinfachen.

Viel Text und die Erklärung gibt's wohl auch noch runder zu Lesen in den Tiefen des WWW - bleibt jetzt aber genau so stehen!

MfG
anscheinend ist Es nicht erwünscht, einen Foren-internen Link als 'Homepage' einzubinden, damit JEDER nur einen Klick von combie's Liste zum Thema State-Maschine entfernt ist.
... dann eben nicht ...

Jomelo

So,

die Version 2.0.0 ist seit Dienstag keine Beta Version mehr.

Heute habe ich noch die Version 2.1.0 hinzugefügt bei der nun mehrere Sprachen unterstützt werden ohne neue Menüelemente anzulegen. Da dies etwas kniffliger ist würde ich nun keinem Anfänger empfehlen damit einzusteigen.
 
Dazu gibt es ein Beispiel unter 02_functionality -> 022 multiLanguage.

Im Moment werden nur die Standardzeichen unterstützt und alle Sonderzeichen die sich z.B. über /zahl hinzufügen lassen.
while(!success){try++;}

timtailors

Hallo,

wie kann man den beim mDyn_para  eine funktion aufrufen lassen nachdem man den Wert eingestellt hat?
Ich habe einen Encoder. Dücke einmal um die Zahl einstellen zu können, dann drücke nochmals und es soll nun der neue Wert im EEPROM gespeichert werden. An welcher Stelle kann man das einfügen?

Jomelo

Guten Morgen,

schau dir dazu einfach nochmal das Beispiel an. Falls du den Vorgang so nicht verstehst füg dir Debugausgaben ein mit Serial.println("press 1"); und Serial.println("press 2");
Ich habe das unten im Code mal ergänst.

Code: [Select]

 if (line == LCDML.MENU_getCursorPos())
  {
    // make only an action when the cursor stands on this menu item
    //check Button
    if(LCDML.BT_checkAny())
    {
      if(LCDML.BT_checkEnter())
      {
        // this function checks returns the scroll disable status (0 = menu scrolling enabled, 1 = menu scrolling disabled)
        if(LCDML.MENU_getScrollDisableStatus() == 0)
        {
          // disable the menu scroll function to catch the cursor on this point
          // now it is possible to work with BT_checkUp and BT_checkDown in this function
          // this function can only be called in a menu, not in a menu function
          LCDML.MENU_disScroll();

          Serial.println("press 1");
        }
        else
        {
          // enable the normal menu scroll function
          LCDML.MENU_enScroll();

           Serial.println("press 2");

          // ***************
          // Hier deinen Code einfügen
          // ***************


        }

        // do something
        // ...
      }

     
     

while(!success){try++;}

timtailors

Hallo großartig, danke. Dann kann ich damit vermutlich auch eine Funktion basteln,damit der Wert der verändert werden kann unterstrichen wird zB. Sonst weiß man nie ob man schon in dem Modus ist oder nicht

timtailors

Hallo,

ich habe noch eine Frage und zwar lege ich das ganze schlafen und wenn ein Eingang per Interrupt getriggert wird, wird eine bestimmte Aktion aufgeführt. Hier soll allerdings das Display nicht angehen und die ganze LCDlib kann abgeschaltet sein. Wenn ich LCDML.loop(); auskommentiere funktioniert das, wenn ich das allerdings in eine if-Bedingung stecke, dann nicht.

Gibt es eine spezielle Funktion wie man die Bildschirmausgabe deaktivieren kann? Wenn ich einfach den Strom ür das Display (ssd1306) abschalte, dann bleibt das Programm offenbar stecken

ArduChris

Hallo,

du kannst folgende Funktion der u8g2 lib verwenden:

u8g2.setPowerSave(1);//Display aus
u8g2.setPowerSave(0);//Display an

Funktioniert bei mir super.
Viele Grüße

athome

Moin,
die Menü  Funktion ist nicht dafür vorgesehen einen Stepper zu betreiben. In den Menüfunktionen solltest du nur Display Inhalte unterbringen.

Zudem sollte im Setup der Menüfunktion kein Stepper initialisiert werden, da hier dein Wert immer erneut wieder auf 0 gesetzt wird.

Ich löse das bei mir immer so, dass ich aus dem Haupt-Loop-Funktion immer den Stepper triggere:

Code: [Select]

void loop()
{
   LCDML.loop_control();
   // abfrage von entschaltern eventuell auch hier einfügen, falls vorhanden

  if(obj_stepper_x.distanceToGo() == 0) // || endschalter_erreicht
  {
    // update menü und co
    LCDML.loop_menu();
  }
  else
  {
    obj_stepper_x.run();
  }
}


In einer der Menüfunktionen mache ich dann folgendes:
-> Intervallzeit auf 100 ms einstellen.
Code: [Select]

 if(obj_stepper_x.distanceToGo() == 0)
    obj_stepper_x.moveTo(-7100); // Bei mir aus einem Programm kopiert, der Wert ist hier nur exemplarisch
 }



Das ganze lässt sich auch gut mit einer Task Library verwenden. Dafür habe ich nun aber nicht direkt ein Beispiel zur Hand, mein aktuelles Projekt brauch noch 2 Wochen bis ich es veröffentlichen kann. :-)
Ich hänge mich auch gleich mal an diesen Super Thread an möchte es aber eher pragmatisch lösen...

Wenn ich dich richtig verstanden habe muss ich im Hauptmenue
Code: [Select]

LCDML_add         (0  , LCDML_0         , 1  , "Manueller Vorschub"   , mFunc_mVorschub);                                       //Manueller Vorschub aktiv

setzen und auch in diesem Tab meine Servo Library einbinden

die Anzeige Funktion sieht dann in etwa so aus:

Code: [Select]

void mFunc_mVorschub(uint8_t param)
// *********************************************************************
{
  if(LCDML.FUNC_setup())          // ****** SETUP *********
  {
    // setup function
    lcd.setCursor(0, 0);
    lcd.print(F("Manueller Vorschub:"));
    lcd.setCursor(0, 1);
    lcd.print(F("      Aktiv!"));
    lcd.setCursor(0, 2);
    lcd.print(F(""));
    lcd.setCursor(0, 3);
    lcd.print(F("Zurueck mit Enter"));
  }

  if(LCDML.FUNC_loop())           // ****** LOOP *********
  {
    // loop function, can be run in a loop when LCDML_DISP_triggerMenu(xx) is set
    // the quit button works in every DISP function without any checks; it starts the loop_end function
    if(LCDML.BT_checkEnter()) { // check if Enter button is pressed (enter, up, down, left, right)
      // LCDML_goToMenu stops a running menu function and goes to the menu
      LCDML.FUNC_goBackToMenu();
    }
  }
 
….


aber nun verstehe ich nicht mehr ganz ….
Was muss ich im Haupt Tab in der Loop Funktion bauen? Ich würde dann mit dem Encoder hier den Schrittmotor verfahren

Besten Dank für deinen gewaltigen Support


Skipper_Do

Hi...

Erstmal Super Library...

Bin gerade dabei damit was zu basteln..
Stoße aber an eine Grenze...

Was kann ich denn anstellen um mehr als 255 Menüeinträge eingebaut zu bekommen? (Sind stark ineinander verschachtelt, daher so viele...)

LG

Jomelo

Moin,

mehr als 254 Menüeinträge werden von der Lib nicht unterstützt. Falls du mehr benötigst, müsste du dir die ganze Lib umschreiben.

Du bist der erste der versucht auf einem Mikrocontroller so ein riesen Menü aufzubauen.
Darf ich mal fragen welchen Kontroller du einsetzt und was genau du vor hast. Eventuell lässt sich da ja noch einiges optimieren.  
while(!success){try++;}

Go Up