Wemos D1 Mini und LCD 1602(A) Umlaute

Hi,

ich habe folgende Problematik. Ich habe mir mit einem Wemos D1 Mini und einem 16x2 Display (1602A) eine Anzeige gebastelt mit Progressbar. Nun habe ich das Problem mit den Umlauten "äöüÄÖÜ" das diese als Heroglyphen angezeigt werden.

Da ich nur der Rechte Teil der ersten Reihe Scrollen soll muss ich etwas tricksen. Die habe ich mit:

String Scroll_LCD_Left(String StrDisplay){
  String result;
  String StrProcess = "         " + StrDisplay + "         ";
  result = StrProcess.substring(Li,Lii);
  Li++;
  Lii++;
  if (Li>StrProcess.length()){
    Li=10;
    Lii=0;
  }
  return result;
}

und

void loop () {
  client.loop();
          
  lcd.setCursor(5, 0);
  
  if (game != "None")
  {
    if (strtopic != "")
    {
      lcd.print(Scroll_LCD_Left(title + " | " + strtopic + " | " + game));
    }else
    {
      lcd.print(Scroll_LCD_Left(title + " | " + game));
    } 
  }else
  {
    if (strtopic != "")
    {
      lcd.print(Scroll_LCD_Left(title + " | " + strtopic));
    }else
    {
      lcd.print(Scroll_LCD_Left(title));
    }

  }
  delay(350);
}

gelöst.

Nun gibt es ja einmal die möglichkeit die Hex? "Codes" für Umlaute zu nutzen zB.

lcd.print("\xE1");

Nur wie bekomme ich das vernünftig in den loop bzw. Scroll_LCD_Left?

Andere Möglichkeit wären ja die Custom Character. Da sind durch die Progressbar 0-6 belegt und laut Kommentar sind noch 7 Übrig was ja reichen würde. Aber auch dort habe ich das Problem das dies ja via

lcd.write(0);

aufgerufen wird.

Die Strings um die es geht kommen Dynamisch rein und können sind Dynamisch. Ist also nichts statisches sonst würde ich das hinbekommen glaube ich.

Hier mein kompletter Code:

#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
 
const char* ssid = "";
const char* password =  "";
const char* mqttServer = "";
const int mqttPort = 1883;
const char* mqttUser = "";
const char* mqttPassword = "";

String title = "";
String strtopic = "";
String game = "None";
 
WiFiClient espClient;
PubSubClient client(espClient);
 
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x3F for a 16 chars and 2 line display

const int LCD_NB_ROWS = 2;
const int LCD_NB_COLUMNS = 16;

int Li = 10;
int Lii = 0;

byte START_DIV_0_OF_1 [ 8 ] = {
  B01111, 
  B11000,
  B10000,
  B10000,
  B10000,
  B10000,
  B11000,
  B01111
};  // Start tank 0/1

byte START_DIV_1_OF_1 [ 8 ] = {
  B01111, 
  B11000,
  B10011,
  B10111,
  B10111,
  B10011,
  B11000,
  B01111
};  // Tank start 1/1

byte DIV_0_OF_2 [ 8 ] = {
  B11111, 
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B11111
};  // Middle tank 0/2

byte DIV_1_OF_2 [ 8 ] = {
  B11111, 
  B00000,
  B11000,
  B11000,
  B11000,
  B11000,
  B00000,
  B11111
};  // Middle tank 1/2

byte DIV_2_OF_2 [ 8 ] = {
  B11111, 
  B00000,
  B11011,
  B11011,
  B11011,
  B11011,
  B00000,
  B11111
};  // Middle tank 2/2

byte END_DIV_0_OF_1 [ 8 ] = {
  B11110, 
  B00011,
  B00001,
  B00001,
  B00001,
  B00001,
  B00011,
  B11110
};  // Char end 0/1

byte END_DIV_1_OF_1 [ 8 ] = {
  B11110, 
  B00011,
  B11001,
  B11101,
  B11101,
  B11001,
  B00011,
  B11110
};  // Fine tank 1/1


// Configuration function of the LCD screen for the progress bar.
// Use custom characters from 0 to 6 (7 remains available).

void setup_progressbar () {

  // Stores custom characters in the LCD screen memory
  lcd.createChar ( 0 , START_DIV_0_OF_1);
  lcd.createChar ( 1 , START_DIV_1_OF_1);
  lcd.createChar ( 2 , DIV_0_OF_2);
  lcd.createChar ( 3 , DIV_1_OF_2);
  lcd.createChar ( 4 , DIV_2_OF_2);
  lcd.createChar ( 5 , END_DIV_0_OF_1);
  lcd.createChar ( 6 , END_DIV_1_OF_1);
}

void draw_progressbar (byte percent) {

  // Displays the new value in numerical form on the first line
  lcd.setCursor ( 0 , 0 );
  lcd.print (percent);
  lcd.print (F ( "%" ));
  // NB The two spaces at the end of the line allow you to delete the percentage figures
  // previous when going from a two or three digit value to a two or one digit value.

  // Move the cursor to the second line * /
  lcd.setCursor ( 0 , 1 );

  // Map the range (0 ~ 100) to the range (0 ~ LCD_NB_COLUMNS * 2 - 2) * /
  byte nb_columns = map (percent, 0 , 100 , 0 , LCD_NB_COLUMNS * 2 - 2 );
  // Each character displays 2 vertical bars, but the first and last character displays only one.

  // Draw each character of the line * /
  for (byte i = 0 ; i < LCD_NB_COLUMNS; ++ i) {

    if (i == 0 ) { // First box

      // Displays the starting char according to the number of columns * /
      if (nb_columns > 0 ) {
        lcd.write ( 1 );  // Tank start 1/1
        nb_columns -= 1 ;

      } else {
        lcd.write ((byte) 0 );  // Start tank 0/1
      }

    } else if (i == LCD_NB_COLUMNS -1 ) { // Last box

      // Displays the end char according to the number of columns * /
      if (nb_columns > 0 ) {
        lcd.write ( 6 );  // Fine tank 1/1

      } else {
        lcd.write ( 5 );  // Char end 0/1
      }

    } else { // Other boxes

      // Displays the correct char according to the number of columns * /
      if (nb_columns >= 2 ) {
        lcd.write ( 4 );  // Char div 2/2
        nb_columns -= 2 ;

      } else if (nb_columns == 1 ) {
        lcd.write ( 3 );  // Char div 1/2
        nb_columns -= 1 ;

      } else {
        lcd.write ( 2 );  // Char div 0/2
      }
    }
  }
}

String Scroll_LCD_Left(String StrDisplay){
  String result;
  String StrProcess = "         " + StrDisplay + "         ";
  result = StrProcess.substring(Li,Lii);
  Li++;
  Lii++;
  if (Li>StrProcess.length()){
    Li=10;
    Lii=0;
  }
  return result;
}

void callback(char* topic, byte* payload, unsigned int length) {
  

  if (strcmp(topic,"d1mini/rbtvdisplay/progress") == 0) {
    lcd.setCursor(0, 0);
    lcd.print("    ");
    payload[length] = '\0'; // Make payload a string by NULL terminating it.
    int progress = atoi((char *)payload);
    draw_progressbar(progress);
    Serial.println("Progress Update");
  }

  if (strcmp(topic,"d1mini/rbtvdisplay/title") == 0) {
    payload[length] = '\0';
    title = String((char*)payload);
    Serial.println("Title Update");
  }

  if (strcmp(topic,"d1mini/rbtvdisplay/topic") == 0) {
    payload[length] = '\0';
    strtopic = String((char*)payload);
    Serial.println("Topic Update");
  }

  if (strcmp(topic,"d1mini/rbtvdisplay/game") == 0) {
    payload[length] = '\0';
    game = String((char*)payload);
    Serial.println("Game Update");
  }
  
}

void setup () {

  // Initializes the LCD screen
  lcd.init();
  lcd.backlight();
  setup_progressbar();
  lcd.clear();
  Serial.begin(115200);
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
 
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
 
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("RBTVDisplay", mqttUser, mqttPassword )) {
 
      Serial.println("connected");  
 
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
 
  client.subscribe("d1mini/rbtvdisplay/#");
  client.publish("d1mini/rbtvdisplay/getstatus", "true", false);
}


// loop () function
void loop () {
  client.loop();
          
  lcd.setCursor(5, 0);
  
  if (game != "None")
  {
    if (strtopic != "")
    {
      lcd.print(Scroll_LCD_Left(title + " | " + strtopic + " | " + game));
    }else
    {
      lcd.print(Scroll_LCD_Left(title + " | " + game));
    } 
  }else
  {
    if (strtopic != "")
    {
      lcd.print(Scroll_LCD_Left(title + " | " + strtopic));
    }else
    {
      lcd.print(Scroll_LCD_Left(title));
    }

  }
  delay(350);
}

Und ja ich weiss die Variablen pack ich noch in eine Settings.h :smiley:

Vielen Dank schonmal für Vorschläge.

Hast Du mal den Zeichenvorrat Deines LCD von 0 ... 255 getestet? Teilweise sind da evtl. Umlaute mit dabei. Ansonsten die verbleibenden User-Zeichen nehmen.

In beiden Fällen beim "Reinkommen" der Zeichenketten ein Ersetzen durchführen.
Dabei sollten die Zeichenketten aber ASCII/ANSI und nicht UTF8 sein.

Gruß Tommy

Tommy56:
Hast Du mal den Zeichenvorrat Deines LCD von 0 ... 255 getestet? Teilweise sind da evtl. Umlaute mit dabei. Ansonsten die verbleibenden User-Zeichen nehmen.

Nein habe ich nicht. Wie gesagt die "Anzeige" funktioniert (zumindest mit "lcd.print("\XE1")") aber ich habe keine idee wie ich das in den Scroll Text bringen soll.

Die Zeichen kann ich aufrufen wenn ich einen festen Text vergebe. Aber wie mach ich das über den dynamischen MQTT Payload der reinkommt?

Das habe ich Dir doch geschrieben. Jedes Zeichen was rein kommt anschauen, ob es ein Umlaut ist. Wenn ja, das entsprechende Ersatzzeichen rein schreiben.

Gruß Tommy

Tommy56:
Das habe ich Dir doch geschrieben. Jedes Zeichen was rein kommt anschauen, ob es ein Umlaut ist. Wenn ja, das entsprechende Ersatzzeichen rein schreiben.

Gruß Tommy

Ja genau das ist mein Problem. Wie muss die funktion aussehen? Den String in ein char array und dann in einer schleife durchgehen?

Ich hab einfach keine Idee Wie.

Das wäre ein Weg.
Ansonsten wie liest Du die Daten von MQTT ein? Wahrscheinlich auch zeichenweise. Da könntest Du das gleich an der Stelle machen.

Gruß Tommy

Tommy56:
Das wäre ein Weg.
Ansonsten wie liest Du die Daten von MQTT ein? Wahrscheinlich auch zeichenweise. Da könntest Du das gleich an der Stelle machen.

Gruß Tommy

Hast du ein Beispiel?

Ich bekomm es ehrlich gesagt nicht hin.

wenn man sich drüberdraut könnte man auch die Lib patchen und in der write Methode
im Anlassfall auf das Sonderzeichen gehen. Print wird sowieso schon vereinzelt.

Nur wundert mich, dass das noch niemand gemacht hätte. Gegoogelt nach einer eingedeutschten Lib hast schon?

noiasca:
wenn man sich drüberdraut könnte man auch die Lib patchen und in der write Methode
im Anlassfall auf das Sonderzeichen gehen. Print wird sowieso schon vereinzelt.

Nur wundert mich, dass das noch niemand gemacht hätte. Gegoogelt nach einer eingedeutschten Lib hast schon?

Nein habe ich noch nicht. War am Anfang froh das das Display überhaupt läuft :smiley: Aber vielen dank für den Tip!

Ich raff es einfach nicht. Mit lcd.print und gewissen Codes geht es ja. Ich bekomm es einfach nur nicht hin das dementsprechend zu printen da ich ja eine String Variable habe. Eingedeutschte Libs gibt es zumindest bei PlatformIO nicht. Russisch könnte ich anbieten :smiley:

a) mit welcher i2c Lib arbeitest du (GANZ GENAU angeben, sodass ich die auch finde)

b) mach mal einen Minimalen Sketch der nur folgendes Ausgibt:

Zeile 0
a A o Ö u U s

Zeile 1
ä Ä ö Ö ü Ü ß

poste den hier in Code-Tags

(hab aktuell keinen Zugriff auf ein LCD, daher musst du fleißig mitarbeiten).

a)

b)

//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <Arduino.h>

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

void setup()
{
  lcd.init();                      // initialize the lcd 
  lcd.init();
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("a A o O u U s");
  lcd.setCursor(0,1);
  lcd.print("ä Ä ö Ö ü Ü ß");
}


void loop()
{
}

Nö schon so dass man die Umlaute sieht!!!

noiasca:
Nö schon so dass man die Umlaute sieht!!!

Ja man sieht was. Aber nicht wie die Umlaute eigentlich aussehen sollten. Versteh die aussage nicht sorry!

Die Umlaute sind nunmal nicht teil des einfachen ASCII Zeichensatzes und liegen im Code ROM des Displays weiter hinten

Nun gibt es ja einmal die möglichkeit die Hex? "Codes" für Umlaute zu nutzen zB.

Das kann man doch direkt in einen String einfügen:

lcd.print("R\xE4ume");

Wo ist da das Problem?

Das einzige wo du aufpassen musst ist wenn das Zeichen direkt nach dem Hex-Byte auch als Hex-Ziffer interpretiert werden kann (z.B. 'a' bis 'f'). Da versucht der Compiler das auch als Teil der Hex-Zahl zu Parsen. Dann kann man aber das machen:

lcd.print("R\xE4""ume");

Serenifly:
Die Umlaute sind nunmal nicht teil des einfachen ASCII Zeichensatzes und liegen im Code ROM des Displays weiter hinten
Das kann man doch direkt in einen String einfügen:

lcd.print("R\xE4ume");

Wo ist da das Problem?

Das einzige wo du aufpassen musst ist wenn das Zeichen direkt nach dem Hex-Byte auch als Hex-Ziffer interpretiert werden kann (z.B. 'a' bis 'f'). Da versucht der Compiler das auch als Teil der Hex-Zahl zu Parsen. Dann kann man aber das machen:

lcd.print("R\xE4""ume");

Ja

lcd.print("R\xE1""ume");

funktioniert. Aber wenn ich einen String habe bekomme ich das nicht ersetzt. Habe folgendes versucht:

String str = "Ein sehr langer String welcher ersetzt werden soll mit verschiedenen äää";
str.replace("ä", "\xE1");

Komischerweise im kleinen Sketch funktioniert es. Aber im großen nicht.

Gebe dir mal einzelnen Zeichen des Strings als Hex-Codes aus. Dann siehst du genau was da drin steht und musst dich nicht auf Interpretationen des Compilers verlassen

Serenifly:
Gebe dir mal einzelnen Zeichen des Strings als Hex-Codes aus. Dann siehst du genau was da drin steht und musst dich nicht auf Interpretationen des Compilers verlassen

Bekomm ich auch grad nicht hin.

Habe aber eine Möglichkeit gefunden. Ich schicke einfach die Umlaute codiert via MQTT mit. Jetzt habe ich nur das Problem mit den hex geschichtet mit "0123456789abcdef" nach einem Umlaut.

Hast du eine Idee?

Der MQTT Payload beinhaltet dann "L\xE1t's Play" was wunderbar funktioniert. Aber "L\xE1at's Play" geht nicht.

Aber "L\xE1at's Play" geht nicht.

Das liegt an dem "a"
0xE1a ist leider eine gültige Hex-Zahl, aber nicht die von dir gewünschte.

Hex E1 ist Oktal 341. "L\341at's Play" wäre also die einfachste Lösung

will das vieleicht wer mit einem I2C LCD ausprobieren?

Die Klasse hat ein neues
private uint8_t special;

wird gesetzt wenn die write Methode ein 0xC3 bekommt,
dann wird das nächste Zeichen umgeschrieben auf einen Umlaut (und das special wieder zurückgesetzt.

aktuell nur die kleinen Umlaute und das ß

Soweit die Theorie. Ich kanns aber momentan nicht testen.