OT: Im standard HD44780 gibts die Zeichen mit Unterlängen in einer zweiten Variante. Da brauchst nur +0x80 dazuzählen und kommst auf die andere Variante.
P.S.: eigentlich müsste es möglich sein, deine Sonderzeichen in einen Converter zu packen, dann kannst du die Zeichen ganz normal im Fließtext schreiben... bei Interesse PM/separter Thread.
Eigentlich gibt es nichts dagegen zu sagen.
Ist voll kommen ok, das so zu tun.
Aus meiner Sicht ist es allerdings eine zählende Schleife (wg. i--), und dafür wurde eigentlich for erfunden.
Eine alternative Schreibweise wäre somit:
for ( ; nach >= 1; i--) Serial.print(" ");
Abweichend von der üblichen for Schleife ist nur, dass der Initialisierungsteil leer ist, denn das wird woanders getan.
Wie schon gesagt, ist überhaupt nicht wichtig.
Denn die beiden Varianten sind sonst in allen Belangen identisch.
Unterscheiden sich nur in der/meiner "Vorstellung" bzw. "Idee"
Dies ist nicht mein Thema, sondern das von @Benziner. @my_xy_projekt wollte nun meine Ideen aus #356 "Passend zum Code aus #355" haben, wie er in #357 schrieb. Diesem Wunsch bin ich nachgekommen und habe mich daher möglichst nahe an seinem Vorbild orientiert. Darum fühle ich mich für while nicht verantwortlich.
Allerdings kann man wegen der wohl unvermeidlich festen Länge const unsigned menueStrLen {12}; die Leerzeichen auch gleich zusammen mit dem Text speichern, weshalb die Berechnung der fehlenden Zeichen entfallen sollte.
Dann ist auch while verschwunden und alle lebten glücklich bis ans Ende ihrer Tage.
Leute, kommt doch mal wieder runter!
Es gibt immer wieder auch Nebenschauplätze, ein "ich kann es besser" ist aber vollkommen unangebracht.
Und ja, ich ärgere mich auch manchmal.
War gestern Abend auch so. - nicht hier. Einfach aufhören. und gut ist.
So und nu zum Tagesgeschehen.
Spätestens seit #353 ist doch klar, das sich vom eigentlichen Tun abhebend noch Unmengen an Aufgaben entwickeln.
In #355 - mit dem dortigen Verweis auf einen selbst aufgemachten Post - habe ich deutlich gemacht, das ich am Anfang stehe um mögliche später entstehende Speicher-Probleme anzugehen.
Nicht mehr und nicht weniger.
Ob da ein while oder for oder gar nichts zum Schluß raus kommt ist wurscht.
Der Code war gedacht, das den JEDER den es interessiert nachvollziehen kann.
Das war ein ganz simpler quick & dirty!
Aufgabe: Die Ausgabe - ggfls. zentriert - voll zu bekommen. Und als erstes und letztes Zeichen immer ein '<' und '>' vs. '*' - also die Wiederverwendung von Zeichenkletten!
Auch hier: Nicht mehr und nicht weniger.
Und wie @agmue schon schrub: Ich wollte eine abweichende Kurzfassung.
Lässt sich nachlesen:
Ich in #357
Das ist dann genau das, was @agmue in #363 als zweiten Code eingestellt hat.
So sah meiner auch aus
@combie Deiner aus #364 ist wirklich kurz.
Ich zolle Dir wie immer Respekt für solche Schnipsel.
Der muss verstanden werden.
Ob das Buch das so hergibt kann ich derzeit nicht beurteilen.
Ich bin nicht allein was die Verständnisfrage zu for und deren Initialisierung angeht.
Hab das als Lesezeichen gesetzt. Der entscheidende Moment ist, ob, wenn es tatsächlich gebraucht wird und nicht gefunden wird.
@noiasca, das mit dem wirklich geilen Code zur Länge der Zeichenketten mit Umlauten hat mich echt fasziniert. Und tut's noch immer. Der ist auch verstanden, mit der Auflösung aus Frage zu CharArray strlen() und Umlaut - #10 by noiasca macht das tasächlich Spass.
Und nu denn hier:
Genau das wollte ich vermeiden.
const char *lcdHauptMenu[] // siehe #355
belegt nur den tatsächlich genutzten Speicher.(-)
Im Hauptmenu ist das noch relativ unproblematisch. Später habe ich aber viel weniger Zeichen auf der Zeile (z.B. 'RGB-')und lege dafür den ganzen Rest mit Leerzeichen an?
Dann kann ich auch jeden Menupunkt wieder einzeln beschreiben.
(-) - Wenn ich da falsch liege, ziehe ich das zurück.
Die Abwägung nach Aufwand und Nutzen findet erst statt, wenn das Funktionale durch ist.
Ich - und so ich nicht ganz falsch liege - auch @benziner wird die Verwendung der Untermenus und des RotaryEncoder die nächsten Tage massiv beschäftigen.
Ganz langsam angehen.
Man liest sich.
Bleibt gesund und passt auf Euch auf!
@benziner morgen Pixel-tab, sobald ich dem Nachmittags habhaft werde. (Ich den Code tatsächlich nicht am Mann ;( )
So und fritz
Ja, ich auch. Andererseits ist bei PROGMEM die Speicherverschwendung nicht ganz so kritisch zu werten. Man muß halt die Balance finden zwischen Datenkomprimierung und Programmieraufwand.
Man sollte in Betracht ziehen, dass die Pointertabelle auch Speicher benötigt.
Pro Pointer, macht das 2 char. Zusammen 22 in dem Beispiel.
Es ist dort also eher eine Geschmacksfrage, als eine des Platzbedarfs.
Naja...
... zählende Schleifen ....
Die ganzen Fachbücher, sind sich da aber schon einig. Incl. C.
Der for Schleifenkopf muss 3 Anweisungen enthalten.
Auch eine leere Anweisung ist gültig.
Du darfst ja auch
c = a + b; ; ; ;
Schreiben.
Das sind immerhin 4 Anweisungen, davon 3 leer.
Also durchaus auch sowas:
for(;;) {/* eine endlosschleife*/}
// identisch mit
for(;true;) {/* eine endlosschleife*/}
// oder
while(1) {/* eine endlosschleife*/}
Eigentlich sollten sich in jedem etwas modernem C++ Buch, die Iteratoren und ihre Verwendung mit Containern finden lassen, wozu gerade eben auch der von dir genannte "Range based for loop" gehört.
Nach dem Lesen sind using, Referenzen, und auch dass C++ automatisch Iteratoren für Standard Arrays zur Verfügung stellt, kein echtes Geheimnis mehr.
Manches ist mit "denken" nicht erreichbar.
Denn mit nur "denken" buddelt man immer nur im selben Sumpf, des schon bekannten.
Keiner sagt, dass es einfach ist.
Aber der Weg führt über das lesen.
Denn darüber erreicht man das ein oder andere "Neue", was man dann in sein "denken" einbauen kann.
Dadurch kommt man zu einem anderen "denken".
Frei nach:
Der Kopf ist rund, damit das Denken seine Richtung ändern kann.
So mein lieber. Dann jetzt mal was fürs Gemüt:
Was haben wir:
Das LED-Menu macht jetzt genau für drei WS drei Farben.
Das, und nur das LED-Menu, bitte einmal durchspielen.
Wenn das auf dem SerMon und dem LCD funktioniert -> Und nur dann(!) darfst Du im void loop() in Zeile 57 den Kommentar entfernen und mir sagen, ob es das ist, was Du willst.
Bitte wie immer drauf achten: Pins müssen ggfls. angepasst werden - ja das änder ich noch...
An alle die sich so arrangiert an meinem Projekt beteiligen, möchte ich meinen Dank aussprechen.
Das kann ich heute gut gebrauchen.....hab den ganzen Tag versucht das Menue auf die Matrix-Anzeige zu übertragen. Nicht ganz einfach ohne Lcd
Hab ich gemacht, und auch nur das.Hat im SerMon und LCD funktioniert weiter mit Schritt 3
Im Prinzip ja, jetzt beim spielen mit dem Farben ist mir aufgefallen das es schön wäre wenn man im z.B. RGB-0 menue zwischen den 3 Werte wählen könnte und dann mit 4´ten Menuepunkt das RGB-0 Menue verlässt oder zum nächsten RGB-1 Menue kommt. Aber das ist jammern auf hohen Niveau. Aber Du hast Recht mit den Wert durchlaufen lassen, zumindest im RGB Menue macht das Sinn
Aber weißt Du wer wieder da ist....? Der Zufallsgenerator
Aber unglaublich was für ein Umfang das ganze im Laufe der Zeit angenommen hat. Ich bin Dir unendlich Dankbar und ich weiß nicht wie ich mich dafür revanchieren kann.
Sag doch Bitte mal was....
Der Sketch verwendet 13678 Bytes (5%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 1236 Bytes (15%) des dynamischen Speichers, 6956 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.
Ich habe heute einfach nur gespielt mit dem Hauptmenu:
Der Sketch verwendet 13648 Bytes (5%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 820 Bytes (10%) des dynamischen Speichers, 7372 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.
Das ist ja ein echtes Dingens...
Na mal schaun, was ich noch bastel.
Und ich beschäftige mich mit den Nebenthemen, in diesem Beitrag mit der Harmonie zwischen IDE, seriellem Monitor und LCD.
UTF-8
Die IDE verwendet in der aktuellen Version UTF-8 als Zeichensatz. in der UTF-8-Codetabelle mit Unicode-Zeichen findet man beispielsweise "ü" als 0xc3bc kodiert, benötigt also zwei Byte.
Der serielle Monitor gibt UTF-8 richtig aus.
Auf LCDs mit HD44870 wird kein UTF-8 unterstützt, weshalb man für eine gute Darstellung auf dem LCD die Buchstaben so "umbiegt", daß der serielle Monitor Müll anzeigt.
In der Bibliothek von @noiasca gibt es die Möglichkeit, eine Umkodierung von UTF-8 auf ein auf dem LCD darstellbares Zeichen vorzunehmen. Damit kann die selbe Zeichenkette sowohl zum seriellen Monitor wie auch zum LCD geschickt werden.
Zeichenkettenlänge
Um beispielsweise zentrierten Text auf dem LCD darstellen zu können, benötigt man die Anzahl der sichtbaren Zeichen. Im Thema Frage zu CharArray strlen() und Umlaut wurde dies erfolgreich gelöst.
Darstellung kleiner Buchstaben mit Nutzung der Unterlänge
Die kleinen Buchstaben g, j, p, q und y werden von HD44870 ohne Unterlänge dargestellt, was die Lesbarkeit erschwert. Mit Hilfe der benutzerdefinierten Zeichen kann man zumindest eine leichte Verbesserung erreichen.
Weil beispielsweise "Hauptmenü" ein Byte mehr im Speicher benötigt, als Buchstaben sichtbar sind, macht die Berechnung der Zentrierung plötzlich richtig Sinn!
Das Programm stammt überwiegend von @noiasca, die Bibliothek ja sowieso, also Spot an und Applaus!
Wer es mal probieren möchte:
#include <Streaming.h> // http://arduiniana.org/libraries/streaming/
#include <Wire.h>
#include <NoiascaLiquidCrystal.h> // download library from https://werner.rothschopf.net/202009_arduino_liquid_crystal_intro.htm
#include <utility/NoiascaCustomCharacters.h> // file with several predefined custom characters
#include <NoiascaHW/lcd_PCF8574.h> // I2C PCF8574 expander
using ConstStrPtr = const char *;
const unsigned menueStrLen {12};
const unsigned menuePunkte {11};
using FlashStrPtr = const __FlashStringHelper *;
using MenuePunkt = const char[menueStrLen + 1];
uint8_t convert_local (uint32_t &special, uint8_t &value)
{
uint8_t isWriteable = NOPRINT;
if ((value & 0b11000000) == 0b11000000) // start byte
{
special = value;
}
else if ((value & 0b11000000) == 0b10000000) // sequence byte (Folgebyte)
{
special = special << 8; // MISSING should get improvement against 3rd following byte
special += value;
if (special > 0xC000 && special < 0xE000) // if 2 byte character
{
isWriteable = PRINT;
}
else if (special > 0xE00000 && special < 0xF0000000) // 3 byte character: 0b11100000 00000000 00000000 im dritten byte von hinten
{
isWriteable = PRINT;
}
else if (special > 0xF0000000) // 4 byte character: 0b11110000 ganz vorne
{
isWriteable = PRINT;
}
}
else // not a sequence byte - reset special
{
special = 0;
}
if (special == 0) // Single Character
{
switch ( value) // overwrite missmatching characters between UTF-8 and charset of ROM Code A00
{
case 0x5c : // REVERSE SOLIDUS (better known as backslash)
value = '|'; // no better alternativ in char set A00
break;
case 0x7e : // tilde ~
value = '-';
break;
}
// check against defined character array
for (byte i = 0; i < 8; i++)
{
if (value == utf8_to_customChar[i])
{
value = i;
break; // found, no need to continue the search
}
}
isWriteable = PRINT;
}
else if (isWriteable) // was UTF-8 2 bytes or more
{
// Step 1: general ROM Mapping
// Lookup within UTF8->ROM Mapping overrides the character to be send to the LCD
uint16_t index = 0;
for (size_t i = 0; i < sizeof(ROM_A00) / sizeof(ROM_A00[0]); i++)
{
index = pgm_read_dword_near(&ROM_A00[i].utf8);
if (index == (special & 0xFFFF))
{
memcpy_P(&value, &ROM_A00[i].c, 1);
break;
}
}
// Step 2: Special character overrule
for (byte i = 0; i < 8; i++)
{
if (special == utf8_to_customChar[i])
{
value = i;
break; // found, no need to continue the search
}
}
// Step 3: Send
// any other character just send to display
special = 0;
// Step 4:
}
return isWriteable; // assume success
}
const byte addr = 0x27;
const byte cols = 16; // columns/characters per row
const byte rows = 2; // how many rows
LiquidCrystal_PCF8574 lcd(addr, cols, rows, convert_local); // create lcd object I2C
// definiert kleine Buchstaben mit Nutzung der Unterlänge g, j, p, q und y
const byte latin_small_g_descender[] PROGMEM = {0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110};
const byte latin_small_j_descender[] PROGMEM = {0b00001, 0b00000, 0b00001, 0b00001, 0b00001, 0b00001, 0b01001, 0b00110};
const byte latin_small_p_descender[] PROGMEM = {0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000};
const byte latin_small_q_descender[] PROGMEM = {0b00000, 0b00000, 0b00110, 0b01001, 0b01001, 0b00111, 0b00001, 0b00001};
const byte latin_small_y_descender[] PROGMEM = {0b00000, 0b00000, 0b01001, 0b01001, 0b01001, 0b00111, 0b00001, 0b01110};
void customCharacterMapping_local()
{
// This UTF8 code will become --> that custom character
utf8_to_customChar[0] = 'g'; lcd.createChar_P(0, latin_small_g_descender);
utf8_to_customChar[1] = 'j'; lcd.createChar_P(1, latin_small_j_descender);
utf8_to_customChar[2] = 'p'; lcd.createChar_P(2, latin_small_p_descender);
utf8_to_customChar[3] = 'q'; lcd.createChar_P(3, latin_small_q_descender);
utf8_to_customChar[4] = 'y'; lcd.createChar_P(4, latin_small_y_descender);
utf8_to_customChar[5] = 0xC384; lcd.createChar_P(5, latin_capital_a_diareses); // Ä comes with the library
utf8_to_customChar[6] = 0xc396; lcd.createChar_P(6, latin_capital_o_diareses); // Ö comes with the library
utf8_to_customChar[7] = 0xc39c; lcd.createChar_P(7, latin_capital_u_diareses); // Ü comes with the library
}
MenuePunkt menue[menuePunkte] PROGMEM
{
{"Hauptmenü"},
{"Turm-Anzeige"},
{"Turm-Licht"},
{"Helligkeit"},
{"RGB-1 Werte"},
{"Fahrspuren"},
{"Startanzeige"},
{"IDNummer"},
{"Funkkanal"},
{"WerksWerte"},
{"zurück"}
};
FlashStrPtr lcdHauptMenu(byte index)
{
return FlashStrPtr(&menue[index]);
}
uint8_t txtlaenge(byte index)
{
char buf[menueStrLen];
strcpy_P(buf, ConstStrPtr(&menue[index]));
int len = utf8_strlenV4(buf);
return len;
}
size_t utf8_strlenV4(const char * str) // https://forum.arduino.cc/t/frage-zu-chararray-strlen-und-umlaut/897224/12
{
int len = 0;
while (*str) len += (*str++ & 0xc0) != 0x80;
return len;
}
void setup()
{
Serial.begin(9600);
Serial.println(F("Start..."));
lcd.begin();
lcd.backlight();
customCharacterMapping_local();
lcd.setCursor(0, 0);
const uint8_t gesamt = 14;
uint8_t txtl = 0;
uint8_t vor = 0;
uint8_t nach = 0;
lcd.setCursor(0, 1);
lcd << lcdHauptMenu(0);
lcd.setCursor(0, 2);
lcd << lcdHauptMenu(1);
lcd.setCursor(0, 3);
lcd << "ÄÖÜäöüßgpq";
for (uint8_t i = 0; i <= 10; i++)
{
lcd.setCursor(0, 0);
txtl = txtlaenge(i);
vor = 0; nach = 0;
if ((gesamt - txtl) > 1)
{
vor = (gesamt - txtl) / 2;
nach = gesamt - (vor + txtl);
}
Serial << "←";
lcd << "←";
while (vor >= 1)
{
Serial << ' ';
lcd << ' ';
vor--;
}
Serial << lcdHauptMenu(i);
lcd << lcdHauptMenu(i);
while (nach >= 1)
{
Serial << ' ';
lcd << ' ';
nach--;
}
Serial << "→" << '\n';
lcd << "→";
delay(1000);
}
}
void loop() {}
Wegen der Vergleichbarkeit habe ich es bewußt im Originalzustand gelassen. Versteckt man also das Vorgeplänkel in einer eigenen Datei, ändert sich nichts beim Menü.
Fazit
Meine Empfehlung an Dich ist, die Verwendung der Bibliothek von @noiasca wegen der Gleichbehandlung der UTF-8-Zeichen in Erwägung zu ziehen. Ob der höhere Speicherverbrauch den Nutzen aufwiegt, muß im Gesamtzusammenhang beurteilt werden, da halte ich mich zurück.
Die Darstellung der kleinen Buchstaben durch benutzerdefinierte Zeichen als Teil der Bibliothek wurde mir für eine spätere Version versprochen. Dadurch würde convert_local() entfallen.
Ich habe durch die Beschäftigung mit diesem Thema schön was gelernt und wollte das Ergebnis nicht für mich behalten, auch wenn Du eventuell nichts übernimmst.
@combie: Ich habe es schon wieder getan, ich habe strcpy_P() verwendet, weil ich es nicht besser hinbekommen habe
Antwort zu #380:
Aus #379 kann die Funktion utf8_strlenV4() gelöscht und txtlaenge() wie folgt ersetzt werden:
uint8_t txtlaenge(byte index) // https://forum.arduino.cc/t/frage-zu-chararray-strlen-und-umlaut/897224/12
{
const char * str = ConstStrPtr(&menue[index]);
int len = 0;
while (pgm_read_byte(str)) len += (pgm_read_byte(str++) & 0xc0) != 0x80;
return len;
}
@combie: Ich dachte schon, ich hätte alles probiert, aber pgm_read_byte(str) ist mir leider nicht in den Sinn gekommen.
Es gibt nur 0-10 Menu-Einträge, aber 1-11 cases. Ich wollte ja auch nur sehen, ob das mit der Einstellerei geht
Mal ne ganz andere Frage: Wenn Du die Helligkeit stellen willst, warum machst Du das einzeln und nicht im RGB-Menu? Wenn ich inhaltlich 4 byte anzeigen lassen mit einem Space zwischendurch, sind das 4*3+3 Char.
Jaja.
Dann musste mal warten - ich seh mal zu, was sich machen lässt.
Momentan bin ich auf Sparkurs.
Weil die Helligkeit für die TM1637 Anzeigen 1 bis 8 sind, und die RGB für die Farben der WS im Turm. Die Farben haben ja nichts mit den 7 Segmentanzeigen zu tun, daher würde ich nicht bei den RGB-Farben die Möglichkeit suchen die Helligkeit der TM1637 zu ändern. Oder siehst Du das anders?
das war, oder vielmehr ist jammern auf hohen Niveau....sollte nicht heißen Du sollst.
Alles gut xy Projekt, alles kann nichts muss. Bin Dir ja ehh schon tausendfach Dankbar. Ohne Dich wäre das Projekt aller wahrscheinlich nach, eh nicht so schön geworden wie es jetzt schon ist.
Aber ich habe mal grundsätzlich noch ein paar Fragen......
Warum ist es so wichtig Speicherschonend zu programmieren?
Kann man nicht den zu Verfügung gestellten Speicher des Boards ausnutzen?
Das programmieren gefällt mir als Hobby recht gut und macht mir Spaß wenn´s andere machen neee eben nicht, ich möchte es selber können. Wie sollte ein Anfänger wie ich es bin....
Null Vorkenntnisse, "C" für den dritten Buchstaben im Alphabet gehalten...
am besten anfangen oder bzw. weitermachen?
Ein Buch lesen, ja kann mir einer da eins empfehlen?
Oder besser irgendwie / wo ein Grundkurs belegen? Für die Uni bin ich ein paar Tage zu alt.
Obwohl praktisch wär`s......50 Schritte und ich wäre da.
Gut, man lernt auch durch abgucken und ausprobieren.....aber dieser weg macht mich nicht glücklich weil ich die ganzen "Kurz Schreibweisen" nicht immer nachvollziehen kann.
Wäre für jeden ernst gemeinten Tipp Dankbar.