Servo Positionieren über LCD Menü

Hallo,
würde gerne einen Servo über ein Menü im Display (LCD16x4) an eine definierte Stelle fahren lassen.
Das verfahren mit Tastern auf verschiedene Positionen bekomme ich hin, allerdings nicht das Positionieren mit Anwahl über eine Menüführung im LCD Display.

Über eure Hilfe würde ich mich sehr freuen!!!

Gruß Jörg

Naja, wenn du bereit bist, dich ein wenig in m2tklib einzuarbeiten, wäre das menü kein problem.

Oliver

Ok,
aber ich hoffe auf deine Hilfe!

Was meinst Du mit was ich anfangen soll?

Ich habe keine Erfahrung mit Menüführungen auf dem LCD Display.
Einen Text ausgeben bekomme ich gerade so hin.

Gruß Jörg

Hallo Jörg

Nun am Anfang stehen zwei Dinge, die du bestimmt hinbekommst.
1.
M2tklib herunterladen, installieren und ein paar Beispiele ausprobieren: Google Code Archive - Long-term storage for Google Code Project Hosting.
Wenn das alles funktioniert, können wir loslegen.

Nimm Stift und Papier: Male Dir deine Menüs auf, so wie du sie gerne haben möchtest. Achte dabei gleich auf die 16x4 Zeichen.
Aktive Elemente (Schaltflächen, Zahlen/Texteingabe) benötigen zwei Zeichen zusätzlich links und rechts in M2tklib (aber das siehst du ja in den Beispielen). Schreibe Dir auf, wie du von Menü zu Menü springen kannst. Es gibt Schaltflächen, mit denen du ein anderes Menü anspringen kannst.
Ziel sollte also ein Blatt Papier mit Menüs sein, die untereinander angesprungen werden können.
Fotografiere das ab oder scanne das ein und poste es hier. Dann können wir die Details diskutieren.

Grüße,
Oliver

Hallo Oliver,

hab soweit alles zum laufen bekommen und ein paar Beispiele ausprobiert.
Hat alles geklappt (musste nur anstatt #include "m2ghl.h", include "utilitiy/m2ghlc.h"
einsetzten.

Hier meine angedachte Menüführung:

Menüführung Servopositionierung.pdf (180 KB)

Hallo Jörg

Wunderbares Menüdesign.
Das mit dem utility folder ist doof. Danke für den Hinweis. Ich dachte ich hätte das mittlerweile behoben.

Aber lass uns loslegen. Am besten nimmst du eines der funktionierenden Beispiele. Dort findest Du irgendwo diese Zeile:

M2tk m2(&el_top, m2_es_arduino, m2_eh_4bs, m2_gh_lc);

Diese Zeile initalisiert die library und stellt auch gleich das erste Menü dar (Erster Parameter: el_top).
Der zweite Parameter "m2_es_arduino", gibt an woher m2tklib seine daten bekommt (also die angeschlossenen Taster).
Der dritte Parameter "m2_eh_4bs" gibt an, wie viele Taster bedient werden sollen und nach welchem Konzept M2tklib diese Tasten verarbeiten soll. Ist nicht weiter wichtig, ich denke m2_eh_4bs ist genau der richtige für deine drei Tasten.
Der vierte Parameter "m2_gh_lc" ist der graphic handler für die LiquidCrystal library, das wird sich auch nicht ändern.

Auf den setup() und loop() gehe ich erst mal nicht ein, das sollte ungefähr so aussehen wie hier (wobei die 2,3 &4 die Arduino pins sind, an denen du die Taster angeschlossen hast... muss ggf angepasst werden):

void setup() {
  m2_SetLiquidCrystal(&lcd, 20, 4);
  m2.setPin(M2_KEY_SELECT, 2);
  m2.setPin(M2_KEY_NEXT, 3);
  m2.setPin(M2_KEY_PREV, 4);

}

void loop() {
  m2.checkKey();
  if ( m2.handleKey() )
    m2.draw();
  m2.checkKey();
}

Jetzt zum eigentlichen Menü. In m2tklib kann es beliebig viele Menüs geben, aber nur eines ist immer aktiv (logisch..).
Man kann jederzeit ein anderes Menü darstellen. Dazu benutzt man entweder die m2.setRoot() funktion oder die aktive Schaltfläche M2_ROOT(). Dazu später mehr. Ausserdem wird durch den M2tk initalisierung (siehe oben) auch gleich das erste aktive Menü festgelegt.

Wie aber sieht ein Menü aus? Es wird aus sogenannten Elementen zusammengesetzt. Diese Elemente beginnen alle mit "M2_". Die Beschreibung aller Elemente gibt es hier:
https://code.google.com/p/m2tklib/wiki/elref
Es gibt aktive Elemente (Buttons, Zahleneingabe, Texteingabe), passive Elemente (Labels, Bitmaps) und sogenannte Kontainerelemente, die wiederum andere (atomare) Elemente beinhalten können.

Schauen wir uns mal das Untermenü von Dir an. Es könnte wie folgt aussehen

M2_BUTTON(el_svo_um_pos1, NULL, "Servostellung 1", &fn_svo_pos1);
M2_BUTTON(el_svo_um_pos2, NULL, "Servostellung 2", &fn_svo_pos2);
M2_BUTTON(el_svo_um_pos3, NULL, "Servostellung 3", &fn_svo_pos3);
M2_BUTTON(el_svo_um_pos4, NULL, "Servostellung 4", &fn_svo_pos4);
M2_LIST(list_svo_um) = { &el_svo_um_pos1, &el_svo_um_pos2, &el_svo_um_pos3, &el_svo_um_pos4};
M2_VLIST(top_el_svo_um, NULL, list_svo_um);

M2_BUTTON ist ein atomares aktives Element, das eine Unterfunktion aufruft, wenn es angeklickt wird.
Erstes Argument ist der Name des Elements
Zweites Argument ist der sogenannte Format String, der hier nicht verwendet wird, deshalb steht hier NULL.
Drittes Argument ist der String, der angezeigt werden soll
Viertes Argument ist der Name der Funktion die aufgerufen werden soll (siehe unten).

Jetzt müssen diese vier Buttons zu einem Menü zusammengeschnürt werden. Das erledigt ein Kontainer-Element (M2_VLIST). Kontainer-Elemente benötigen meistens noch ein Hilfskonstrukt, nämlich eine Liste mit Elementen. Das macht M2_LIST(). M2_LIST und M2_VLIST gehören eigentlich immer zusammen.
Der Kontainer heisst übrigens VLIST, weil die Elemente in der Liste vertikal (untereinander) angeordnet werden.

Jetzt fehlen noch die vier Funktionen für die M2_BUTTON Elemente. Die Namen sind ja schon angegeben (letztes Argument!), aber die Funktion fehlt noch. Die sehen beispielsweise so aus:

char *svo_pos_str = "<keine>";
int svo_pos = 0;

void fn_svo_pos1(m2_el_fnarg_p fnarg) {
  svo_pos_str = "Servostellung 1";
  svo_pos = 1;
  m2_SetRoot(&top_el_svo_um);
}

Es handelt sich um eine Funktion ohne Rückgabewert und mit einem seltsamen Argument (fnarg), das wir aber einfach als gegeben hinnehmen und nicht weiter betrachten.
Ich habe noch zwei globale Variablen eingeführt, in denen die Auswahl gespeichert wird. Am Ende der Funktion wird noch ein neues Menü aktiviert (bzw hier in das selbe Menü zurückgesprungen). Allerdings haben wir hier ein C/C++ Problem: Möglicherweise steht die Funktion im Quelltext VOR der eigentlichen Definition des Menüs, d.h. der Menü-Name "top_el_svo_um" ist dem Compiler zu diesem Zeitpunkt noch gar nicht bekannt. Die Lösung besteht darin eine Vorwärtsdeklaration zu machen (hier M2_EXTERN_VLIST).

Der vollständige Code sieht dann etwa so aus:

char *svo_pos_str = "<keine>";
int svo_pos = 0;


/* svo_hm: Servo Untermenue */

M2_EXTERN_VLIST(top_el_svo_um);

void fn_svo_pos1(m2_el_fnarg_p fnarg) {
  svo_pos_str = "Servostellung 1";
  svo_pos = 1;
  m2_SetRoot(&top_el_svo_um);
}
void fn_svo_pos2(m2_el_fnarg_p fnarg) {
  svo_pos_str = "Servostellung 2";
  svo_pos = 2;
  m2_SetRoot(&top_el_svo_um);
}
void fn_svo_pos3(m2_el_fnarg_p fnarg) {
  svo_pos_str = "Servostellung 3";
  svo_pos = 3;
  m2_SetRoot(&top_el_svo_um);
}
void fn_svo_pos4(m2_el_fnarg_p fnarg) {
  svo_pos_str = "Servostellung 4";
  svo_pos = 4;
  m2_SetRoot(&top_el_svo_um);
}

M2_BUTTON(el_svo_um_pos1, NULL, "Servostellung 1", &fn_svo_pos1);
M2_BUTTON(el_svo_um_pos2, NULL, "Servostellung 2", &fn_svo_pos2);
M2_BUTTON(el_svo_um_pos3, NULL, "Servostellung 3", &fn_svo_pos3);
M2_BUTTON(el_svo_um_pos4, NULL, "Servostellung 4", &fn_svo_pos4);
M2_LIST(list_svo_um) = { &el_svo_um_pos1, &el_svo_um_pos2, &el_svo_um_pos3, &el_svo_um_pos4};
M2_VLIST(top_el_svo_um, NULL, list_svo_um);

Damit würde ich jetzt erst mal Schluss machen und Dir ein paar Hausaufgaben mitgeben:
Nehme eines der Beispiele aus m2tklib (z.B. Combo) und ersetze das Menü dort durch das hier diskutierte Menü. Denke daran, dass Du auch das erste Argument des Construktors (M2tk m2) ändern musst.
Spiele ein bischen herum. Du solltest den Cursor über die vier Menüs lenken können. Wenn Du einen Menü-Eintrag aktivierst, wird das gleiche Menü wieder aufgerufen (der Cursor springt wieder an die oberste Stelle).

Wenn das klappt, machen wir mit dem Hauptmenü weiter.

Sage mir Bescheid, wenn ich zu langsam bin, zu viel erzähle oder wenn Du sonst noch Fragen hast.

Bis dann,
Oliver

Für den Fall, dass Du gleich weitermachen willst, das Hauptmenü könnte so aussehen:

/* svo_hm: Servo Hauptmenue */

M2_LABEL(el_svo_hm_title, "f8W64", "Servoeinstellung");
M2_ROOT(el_svo_hm_jmp_um, "f8W64", "Auswahl", &top_el_svo_um);

M2_LABEL(el_svo_hm_space, NULL, "");
M2_LABELPTR(el_svo_hm_pos, "f8W64", &svo_pos_str);

M2_LIST(list_svo_hm) = { &el_svo_hm_title, &el_svo_hm_jmp_um, &el_svo_hm_space, &el_svo_hm_pos};
M2_VLIST(el_svo_hm_vlist, NULL, list_svo_hm);
M2_ALIGN(top_el_svo_hm, "-1|1W64H64", &el_svo_hm_vlist);

Oliver

SUPER !!!
Vielen, vielen Dank!!!

Bin schon am ausprobieren, hab´s aber noch nicht hin bekommen.

Melde mich sobald ich soweit bin.

Nochmals vielen, vielen Dank für deine Mühen!!!!

Gruß Jörg

Viel Erfolg. Melde Dich, wenn Du fragen hast.

Oliver

Hallo Oliver,
nochmal bis hierhin vielen, vielen Dank für deine Hilfe!!!

Hab jetzt mal alles ,so wie es Du beschrieben hast, gemacht (Code im Anhang).
Ein paar Sachen hab ich nicht verstanden, z.B.:

Im Hauptmenü hab ich die Zeile
M2_LABEL(el_svo_hm_titel, "f8W64", "Servostellung"); geschrieben.
Scheint eine Positionsangabe des Textes zu sein, oder?
Nur wie hab ich diese zu verstehen (f8W64)?

Im Hauptmenü Zeile:
M2_LABEL(el_svo_hm_jmp_um, "f8W64", "Auswahl", &top_el_svo_um);
wurde die Eckige Klammer nicht gesetzt. Erst als ich die "f8W64" gegen NULL getauscht habe,
ist sie erschienen, warum?

Gruß Jörg

Hauptmenu_Servo_04_03_2015.ino (1.94 KB)

Hallo Oliver,

hier noch mein Code mit Servoansteuerung.

Allerdings fehlt hier noch das Menü mit "Servo Positionieren [OK].

Da benötige ich deine Hilfe.

Hauptmenu_Servo_04_03_2015.ino (2.16 KB)

Hi

Das Menü könnte etwa so aussehen:

void fn_svo_ok(m2_el_fnarg_p fnarg) {
  /* accept selection */
}

M2_LABELPTR(el_svo_ok_label1, "f8W64", &svo_pos_str);
M2_LABEL(el_svo_ok_label2, "f8W64", "positionieren?");
M2_BUTTON(el_svo_ok, "f8W64", "ok", fn_ok);

M2_LIST(list_svo_ok) = { &el_svo_ok_label1, &el_svo_ok_label2, &el_svo_ok };
M2_VLIST(el_svo_confirm, NULL, list_svo_ok);

"f8W64" ist der optionale format string.
Ich hab das versucht hier zu erklären: Google Code Archive - Long-term storage for Google Code Project Hosting.
f8: Centrierter text
W64: Mache das Element so breit wie das Display. W32 wäre dann halbe Displaybreite. W16 entsprechend ein viertel.

Ich hab die menüs mit meinem Graphic-Display-Simulator getestet. Das verhält sich hier etwas anders, deshalb kann es schon sein, dass bei Dir "NULL" oder vielleicht auch "f8W50" (oder so) besser funktioniert.

Die vier positionsfunktionen, müssen natürlich abgeändert werden, damit sie das neue Menü aufrufen:

void fn_svo_pos1(m2_el_fnarg_p fnarg) {
  svo_pos_str = "Servostellung 0";
  svo_pos = 1;
  m2_SetRoot(&el_svo_confirm);
}

Entsprechend auch die anderen.
Ausserdem würde ich dann das setzen der servos in die ok funktion verlagern:

void fn_svo_ok(m2_el_fnarg_p fnarg) {
  if(svo_pos == 4){
  Ini.write(175);

}
 
  if(svo_pos == 3){
  Ini.write(140);

}
 if(svo_pos == 2){
   Ini.write(80);
   
}

if(svo_pos == 1){
  Ini.write(0);
  
}
}

M2_LABELPTR(el_svo_ok_label1, "f8W64", &svo_pos_str);
M2_LABEL(el_svo_ok_label2, "f8W64", "positionieren?");
M2_BUTTON(el_svo_ok, "f8W64", "ok", fn_ok);

M2_LIST(list_svo_ok) = { &el_svo_ok_label1, &el_svo_ok_label2, &el_svo_ok };
M2_VLIST(el_svo_confirm, NULL, list_svo_ok);

Grüße,
Oliver

Hallo Oliver,

die Menüführung funktioniert allerdings komm ich mit der Text Zentrierung nicht ganz klar.

Der Text (z.B.) [Auswahl] lässt sich mit "f8W64" nicht im 20x4LCD zentrieren.

Gruß Jörg

Hallo Jörg

Tatsächlich hatte ich bei meinen Experimenten für dein Menü ebenfalls Probleme mit der Zentrierung.
Eventuell funktioniert "f8w20" oder "f8w18" (kleines w!)
Alternativ könntest du natürlich leerzeichen voranstellen. Es gibt auch die möglichkeit statt M2_VLIST und M2_HLIST den container M2_XYLIST zu verwenden. Letzteres schaltet die optionen x und y frei, die dann ein freies positionieren ermöglichen. Statt "f8W64" schreibt man dann "y2x5" den linken anfang vom string.

Oliver

Hallo Oliver,
das Projekt mit der Menüführung steht und funktioniert sehr gut.
Ich werde den Aufbau und den fertigen Code nächste Woche hier Posten.

Gruß Jörg

Na, das klingt doch ganz gut. Vielleicht kannst du noch den ein oder anderen screenshot beilegen.

Grüße,
Oliver

So,
hier erstmal ein paar Bilder von meiner Menüführung:

Der Code sieht wie folgt aus:

#include <LiquidCrystal.h>
#include "M2tk.h"
#include "utility/m2ghlc.h"
#include <Servo.h> 

Servo Ini;

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Zuweisung der Arduino PINs//
int8_t uiKeySelectPin = 10;
int8_t uiKeyNextPin = 7;
int8_t uiKeyPrevPin = 9;



const char *svo_pos_str = "-- keine Auswahl --";
int svo_pos = 0;



M2_EXTERN_VLIST(top_el_svo_um);


// Hauptmenue //
M2_LABEL(el_svo_hm_space, NULL, "   --- Menue ---");
M2_LABEL(el_svo_hm_title, NULL, "  Servoverstellung");
M2_ROOT(el_svo_hm_jmp_um, "f8W40", "  Auswahl mit OK  ", &top_el_svo_um);
M2_LABELPTR(el_svo_hm_pos, "f8W64", &svo_pos_str);
M2_LIST(list_svo_hm) = { &el_svo_hm_space, &el_svo_hm_title,  &el_svo_hm_jmp_um,  &el_svo_hm_pos};
M2_VLIST(el_svo_hm_vlist, NULL, list_svo_hm);
M2_ALIGN(top_el_svo_hm, "-1|1W64H64", &el_svo_hm_vlist);





//Untermenue //
M2_BUTTON(el_svo_um_pos1, NULL, "Stellung -1", &fn_svo_pos1);
M2_BUTTON(el_svo_um_pos2, NULL, "Stellung -2", &fn_svo_pos2);
M2_BUTTON(el_svo_um_pos3, NULL, "Stellung -3", &fn_svo_pos3);
M2_BUTTON(el_svo_um_pos4, NULL, "Stellung -4", &fn_svo_pos4);
M2_LIST(list_svo_um) = { &el_svo_um_pos1, &el_svo_um_pos2, &el_svo_um_pos3, &el_svo_um_pos4};
M2_VLIST(top_el_svo_um, NULL, list_svo_um);





M2tk m2(&top_el_svo_hm, m2_es_arduino, m2_eh_4bs, m2_gh_lc);

//Zuweisung - Ansteuerung der Pins//
void setup() {
  m2_SetLiquidCrystal(&lcd, 20, 4);
  m2.setPin(M2_KEY_SELECT, uiKeySelectPin);
  m2.setPin(M2_KEY_NEXT, uiKeyNextPin);
  m2.setPin(M2_KEY_PREV, uiKeyPrevPin);
  Ini.attach(6);
  
}



//Eingabe der Servopositionen//
void fn_svo_ok(m2_el_fnarg_p fnarg) {
  if(svo_pos == 4){
  Ini.write(25);  //minimal 25
  m2_SetRoot(&top_el_svo_hm);
}
 
  if(svo_pos == 3){
  Ini.write(90);
  m2_SetRoot(&top_el_svo_hm);
}
   if(svo_pos == 2){
   Ini.write(91);
   m2_SetRoot(&top_el_svo_hm);
}

   if(svo_pos == 1){
   Ini.write(159);  //maximal 159
   m2_SetRoot(&top_el_svo_hm);
  
}
}

// Getroffene Auswahl Akzeptieren//
M2_LABELPTR(el_svo_ok_label1, "f8W64", &svo_pos_str);
M2_LABEL(el_svo_ok_label2, "f8W32", "  Positionieren?");
M2_BUTTON(el_svo_ok, "f2W16", "       ok        ", &fn_svo_ok);
M2_BUTTON(el_svo_ok_um, "f2W16", "     zurueck     ", &fn_svo_zurueck);

M2_LIST(list_svo_ok) = { &el_svo_ok_label1, &el_svo_ok_label2, &el_svo_ok,  &el_svo_ok_um };
M2_VLIST(el_svo_confirm, NULL, list_svo_ok);

void fn_svo_zurueck(m2_el_fnarg_p fnarg) {
  m2_SetRoot(&top_el_svo_um);
}
// Getroffene Auswahl im Hauptmenue Anzeigen//
void fn_svo_pos1(m2_el_fnarg_p fnarg) {
  svo_pos_str = "     Stellung -1";
  svo_pos = 1;
  m2_SetRoot(&el_svo_confirm);
}
void fn_svo_pos2(m2_el_fnarg_p fnarg) {
  svo_pos_str = "     Stellung -2";
  svo_pos = 2;
  m2_SetRoot(&el_svo_confirm);
}
void fn_svo_pos3(m2_el_fnarg_p fnarg) {
  svo_pos_str = "     Stellung -3";
  svo_pos = 3;
  m2_SetRoot(&el_svo_confirm);
}
void fn_svo_pos4(m2_el_fnarg_p fnarg) {
  svo_pos_str = "     Stellung -4";
  svo_pos = 4;
  m2_SetRoot(&el_svo_confirm);
}


void loop() {
  m2.checkKey();
  if ( m2.handleKey() )
    m2.draw();
  m2.checkKey();
 
  
}

Der Aufbau wurde wie folgt realisiert:

Na, das sieht doch richtig gut aus. Tolles Gehäuse und prima Taster.

Oliver

Hallo Oliver,

ich wollte mich auf diesem Weg nochmal herzlich für deine Hilfe bedanken!!!
Ich hoffe ich kann auf dich zurück kommen wenn ich mal wieder ein Problem hab.

Vielen, vielen Dank!!!

Gruß Jörg