Projektvorstellung: Library für das Erstellen von Menus für LCD Displays

Hallo und danke für das Intresse,
ich habe nun den oberen Beitrag bearbeitet und die Lib als .zip angehängt also alle die Intresse haben einfach downloaden. Zudem hab ich vor, ein paar fragen zu beantworten :wink:

BerndJM:
Hi,

bei deinem Beispielcode taucht bei mir ein "geistiges Fragezeichen" auf:
Was passiert bei readButton = 50 (oder 195 oder 380) ?

Grüßle Bernd

Dieser Abschnitt bezieht sich komplett auf das Benutzen eines KeyPadShields (Arduino_LCD_KeyPad_Shield__SKU__DFR0009_-DFRobot) Letztendlich ist dieser Code nur dafür da, um zu ermitteln welcher Button gedrückt wurde und somit durch das Menü zu navigieren.

michael_x:
Wieviel RAM brauchen deine MenuItem-Elemente? Verbrät ein addItem () - Aufruf noch etwas zusätzlich?

Wie viel RAM es genau verbrät kann ich die leider nicht sagen.
Ja addItem() benötigt auch noch Speicher. Allerdings hab ich vor, wie auch schon andere sagten, per Zeiger auf die Items zu zeigen.
Weiter Infos solltest du dir aber aus der Lib holen können.

michael_x:
Das Ganze aus einfach konfigurierbaren Bausteinen zusammenzustellen, ist ein guter Ansatz!

Danke, das war meine Motivation eine solche Lib zubasteln. Es freut mich das ich das anscheinend geschafft habe.

Am Ende möchte ich nochmals sagen das ich mich sehr über Verbesserungsvorschläge freue. Ich hoffe das mein Code nicht ganz so grausam ist und keine hoch heiligen unaufgeschriebenen Programmierregeln bricht.
Also dann viel Spass mit der Lib :wink:

Der Anhang ist der am ersten Post ?

Ich hoffe das mein Code nicht ganz so grausam ist und keine hoch heiligen unaufgeschriebenen Programmierregeln bricht.

Wenn du ohne String arbeitest, ist es "nicht ganz so grausam" :wink:

Wie gesagt, ein Menu im PROGMEM Flash-Speicher aber "konfiguriert statt programmiert", wäre gut.

Hmm doch die String Klasse. Die würde ich auf jeden Fall entsorgen. Da Du mit den Strings selbst nichts machst, außer sie zu speichern, sollte einfach "String" gegen "char*", oder sogar "const char*" ausgetauscht werden.

Als nächstes würde ich auf jeden Fall von den "künstlichen" Indizes auf Zeiger umstellen, dann sparst Du Dir die Funktion getNeighbour(), da jeder Eintrag von forward, back, up und down gleich der passende Zeiger auf das nächste Item ist. Damit man einen definierten Start hat, würde ich ein "root" Element im Menu-Objekt definieren, welches immer den ersten Eintrag liefert. Zusätzlich sollte jedes MenuItem entsprechende Methoden getForward(), getBack(), getUp und getDown() haben, damit man auf die angehängten Elemente zugreifen und damit durch den Baum / Graph navigieren kann.

Mario.

michael_x:
Der Anhang ist der am ersten Post ?

Wie gesagt, ein Menu im PROGMEM Flash-Speicher aber "konfiguriert statt programmiert", wäre gut.

1.) Ja aus dem ersten Post.
2.) Wie soll ich dieses "konfiguriert statt programmiert" verstehen?

mkl0815:
Hmm doch die String Klasse. Die würde ich auf jeden Fall entsorgen. Da Du mit den Strings selbst nichts machst, außer sie zu speichern, sollte einfach "String" gegen "char*", oder sogar "const char*" ausgetauscht werden.

Okay, dazu Fragen. Warum sind Strings schlecht? Sind das nicht letztendlich auch nur Char arrays? Was haben Chars als Vorteil gegenüber Strings?
Und wenn ich jetzt von String auf Char wechsel ist das doch für den User "relativ" aufwendig da er einen Namen z.B. so anlegen muss:

char name[5] = {'H', 'a', 'l', 'l', 'o'};

mkl0815:
Als nächstes würde ich auf jeden Fall von den "künstlichen" Indizes auf Zeiger umstellen, dann sparst Du Dir die Funktion getNeighbour(), da jeder Eintrag von forward, back, up und down gleich der passende Zeiger auf das nächste Item ist. Damit man einen definierten Start hat, würde ich ein "root" Element im Menu-Objekt definieren, welches immer den ersten Eintrag liefert. Zusätzlich sollte jedes MenuItem entsprechende Methoden getForward(), getBack(), getUp und getDown() haben, damit man auf die angehängten Elemente zugreifen und damit durch den Baum / Graph navigieren kann.

Das wäre der nächste Schritt gewesen, allein Schon damit ein Item nicht zwei mal existiert.

Unterm Strich: Würdet ihr diese Lib benutzen wenn ihr gerade ein Projekt amlaufen habt, bei dem ihr deratige Menüs braucht?

cr0n0s1:
Okay, dazu Fragen. Warum sind Strings schlecht? Sind das nicht letztendlich auch nur Char arrays? Was haben Chars als Vorteil gegenüber Strings?
Und wenn ich jetzt von String auf Char wechsel ist das doch für den User "relativ" aufwendig da er einen Namen z.B. so anlegen muss:

char name[5] = {'H', 'a', 'l', 'l', 'o'};

Strings sind "schlecht", die Klasse bei jeder String-Operation den knappen Speicher fragmentiert und am Ende jeder String mehr Platz weg nimmt, als der gleiche char*.

Einen C-String kann man auch folgendermaßen deklarieren:

char* name1 = "Hallo";
char name2[] = "Hallo";

Beide Variablen enthalten auch schon das abschliessende '\0' Zeichen. Was Dein "char name[5]" nicht macht. Dein "Hallo" hat als C-String kein Ende.
Und ein char* name = "Hallo" ist genau so aufwändig wie ein String-Objekt zu erzeugen.
Mario.

mkl0815:

char* name1 = "Hallo";

char name2[] = "Hallo";



Beide Variablen enthalten auch schon das abschliessende '\0' Zeichen. Was Dein "char name[5]" nicht macht. Dein "Hallo" hat als C-String kein Ende. 
Und ein char* name = "Hallo" ist genau so aufwändig wie ein String-Objekt zu erzeugen.
Mario.

Ahh super, das war mir vorher nie so bewusst, dann seh ich auch kein problem dadrin das ganze mit Chars zu machen :wink:

Ahh super, das war mir vorher nie so bewusst, dann seh ich auch kein problem dadrin das ganze mit Chars zu machen

Sorry, hätt' ich vielleicht vorher besser erklären sollen. Vermutlich kannst du dir gar nicht vorstellen, was passiert, wenn du schreibst:

String text = "Hallo";
text = text + "!";

text liefert zwar jetzt "Hallo!", aber der alte String("Hallo") liegt immer noch als Müll rum, weil er zu klein für den neuen text ist.

Ein char array ausreichender Größe ist im Endeffekt einfacher, wenn du erstmal dran gewöhnt bist, wie es zu benutzen ist.

Und wenn du dir dann angewöhnst, feste Texte gar nicht in den RAM zu legen :wink:

Wie soll ich dieses "konfiguriert statt programmiert" verstehen?

So wie du es machst, mit einer Liste von MenuElementen, die quasi in einer Tabelle definiert haben, wie sie zusammenhängen und welche Texte sie anzeigen, statt alles per Programm zu codieren.

Nur hab ich in meinem 328 nur noch 800 byte RAM frei, und ein SD Card Sektor ist halt mal 512 byte groß, da bin ich etwas knickig. Ausserdem geht's auch um's Prinzip, aus sportlichem Ehrgeiz...

Eure Erläuterungen zu Strings sind sehr interessant und gut zu Wissen.

Wie würde denn die richtige Implementierung der PROGMEM in die Lib aussehen?

Lib_X.h

const char **_flashText;

Lib_X.cpp

char ProgMemBuffer[20];
strcpy_P(ProgMemBuffer, (char*)pgm_read_word(_flashText));

*.ino

prog_char strServo1[]               PROGMEM = {"Servos AN"};

PROGMEM const char *MenServo_Strings[] = 
{   
  strServo1
};

KlasseX._flashText = &MenServo_Strings[0];

Funktionieren tuts, ist das auch die optimalste Art ohne Verbesserungsmöglichkeiten??

Könnte man evtl. die neue Funktion F("") irgendwie mit benutzen?
Damit wäre die Einbindung leichter.

Z.B.:

KlasseX.InitText(F("Servos AN"));

Könnte man evtl. die neue Funktion F("") irgendwie mit benutzen?
Damit wäre die Einbindung leichter.

Na klar, F("text") ist zwar keine Funktion sondern ein Makro in WString.h; der einzige Sinn ist, aus "text" den Pseudo-VariablenTyp __FlashStringHelper* zu machen, und als PSTR("text") im Flash abzulegen.

Musst du nur eine Methode InitText(const __FlashStringHelper* ptext) hinzufügen.
Zum Verwenden auf const prog_char* casten, oder einfach mit print() ausgeben.

So habe jetzt meine

Init(const __FlashStringHelper* Text)
{
	_Text3 = Text;
}

jetzt möchte ich den übergebenen Wert in der Klasse abspeichern, komme aber nicht weiter.
Ist das richtig?
const __FlashStringHelper* _Text3;

Ich möchte den Wert später an anderer Stelle wieder verarbeiten:
sprintf(Text, "%s", _Text3);

Brauche nochmals Hilfe, komme nicht weiter...

jetzt möchte ich den übergebenen Wert in der Klasse abspeichern, komme aber nicht weiter.
Ist das richtig?
const __FlashStringHelper* _Text3;

Ja, nur der Name _Text3 ist nicht sehr schön :wink:

Ich möchte den Wert später an anderer Stelle wieder verarbeiten:
sprintf(Text, "%s", _Text3);

sprintf kennt keinen __FlashStringHelper ( und auch kein PROGMEM )
print(_Text3); geht direkt, sonst schau nach den Funktionen, mit denen char aus dem PROGMEM geladen oder kopiert werden

Ja, Text3 auch nur deshalb, weil meine Lib momentan mehrere "Textformate" unterstützt. Wird dann später bereinigt :wink:

Die Fuunktion für PROGMEM ist klar: strcpy_P(Text, (char*)pgm_read_word(_Text2));

Dann werde ich mal suchen, was den __FlashStringHelper in ein char Array klopfen kann.

den __FlashStringHelper

... gibt es so gar nicht "wirklich".
Von einem Pointer auf einen __Flashstringhelper weiss man, dass print() es drucken kann, und dass man diesen Pointer als einen prog_char* ( oder einen PGM_P ) verwenden kann.

also z.B. strcpy_P(rambuffer, (prog_char*)Text3 );

--> arduino-1.0.1\hardware\tools\avr\avr\include\avr\pgmspace.h

Einsame Klasse - so gehts!

mkl0815:
Damit man einen definierten Start hat, würde ich ein "root" Element im Menu-Objekt definieren, welches immer den ersten Eintrag liefert. Zusätzlich sollte jedes MenuItem entsprechende Methoden getForward(), getBack(), getUp und getDown() haben, damit man auf die angehängten Elemente zugreifen und damit durch den Baum / Graph navigieren kann.

So ein ROOT Element halte ich auch für wichtig. Da beim Menü sich um eine Baum-Datenstruktur handelt. Und die Baumstruktur braucht immer eine Wurzel.

Siehe hier:

Allerdings handelt es sich bei der Datensturktur eher um einen gerichteten Graphen, nicht um einen Baum. In einem Baum gibt es keine Schleifen, die aber in der Menustruktur durchaus möglich sind. Z.B. damit ich beim Weiterblättern nach dem letzten Eintrag einer Ebene wieder an deren Anfang heraus komme.

alhin:
So ein ROOT Element halte ich auch für wichtig. Da beim Menü sich um eine Baum-Datenstruktur handelt. Und die Baumstruktur braucht immer eine Wurzel.

Also ich habe einen Einstiegspunkt mit begin(int startElement).

mkl0815:
Allerdings handelt es sich bei der Datensturktur eher um einen gerichteten Graphen, nicht um einen Baum. In einem Baum gibt es keine Schleifen, die aber in der Menustruktur durchaus möglich sind. Z.B. damit ich beim Weiterblättern nach dem letzten Eintrag einer Ebene wieder an deren Anfang heraus komme.

Und wie in diesem Post beschrieben ist dies eher kein Baum sondern ein Graph und das macht es auch möglich endlos durch eine Ebene zu laufen (Also wenn man am letzten Element angekommen ist gehts oben wieder weiter). Dies war auch so von mir gewollt. Wobei mich interessieren würde, wo für ein root Element gut sein soll?

Cronos, du könntest jetzt auch die Texte über den Flash realisieren.
Vorlagen sind ja vorhanden :grin: