2 Topics über MQTT empfangen und auf LC Display ausgeben

Hey Leute,

ich bin gerade ein wenig am verweifeln. Ich habe schon einiges rum gegooglt und rumprobiert, nur leider ohne nennenswerten Erfolg.

Erstmal zu meiner Zielsetzung:

Ich habe einen MQTT Broker am laufen und möchte von diesem 2 Topics (vielleicht in Zukunft auch was mehr, aber ich möchte mit 2 anfangen) abonieren. Es geht um ein Bool Wert und eine Zeichenkette. Diese beiden Werte möchte ich auf einem kleinen LC Display ausgeben lassen. Zum Test habe ich erstmal eine Ausgabe mittels Serial.print gemacht. Das Anschließen und ansteuern eine LC Displays selbst habe ich schon öfter gemacht und auch ohne irgendwelche Probleme.

Ich hoffe man kann mir dabei irgendwie helfen?

Michael

Vielleicht verstehe ich Deinen Post nicht.

Ist das eine Frage?
Dann ist die Antwort Nein.
Es fehlt jedwede Anforderung.
Ausser helfen.

Du musst schon beschreiben, wo es klemmt. :wink:
Ausgaben auf einem LCD mit Variablen ist kein Hexenwerk - aber ich befürchte, das es nicht Deine Fragestellung abdeckt.

Ne, war es nicht. Es ging eher um den Teil, wie kann ich Topics abonieren und diese Werte in einer Variable speichern. Den Punkt habe ich nach weiterem googlen und probieren mittlerweile hin bekommen. Jetzt muss ich gucken, wie ich das noch schön Formatieren kann. Vor allem geht es um den zweiten Topic. Der ist in der Regel zwischen 600 und 0.01 (hier Watt). Leider wird das recht "einfach" veröffentlicht.

Aber hier erstmal mein Code, das macht das ganze einfacher. Noch Ohne LC Display.

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
 
const char* SSID = "SSID";
const char* PSK = "Streng geheimes Passwort";
const char* MQTT_BROKER = "192.168.178.43";
 
WiFiClient espClient;
PubSubClient client(espClient);
 
void setup() {
    Serial.begin(115200);
    setup_wifi();
    client.setServer(MQTT_BROKER, 1883);
    client.setCallback(callback);
}
 
void setup_wifi() {
    WiFi.begin(SSID, PSK);
 
    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
    }
    
    Serial.println(WiFi.localIP());
}
void loop() {
    if (!client.connected()) {
        while (!client.connected()) {
            client.connect("ESP8266Client");
            client.subscribe("alias/0/Shellys/Flaechenheizung/LEISTUNG_AKTUELL");
            client.subscribe("alias/0/Shellys/Flaechenheizung/POWER");
            delay(100);
        }
    }
    client.loop();
}
void callback(char* topic, byte* payload, unsigned int length) {
    String msg;
    if(strcmp(topic, "alias/0/Shellys/Flaechenheizung/LEISTUNG_AKTUELL") == 0) {
      for (byte i = 0; i < length; i++) {
        char tmp = char(payload[i]);
        msg += tmp;
      }
    } else if(strcmp(topic, "alias/0/Shellys/Flaechenheizung/POWER") == 0) {
      for (byte i = 0; i < length; i++) {
        char tmp = char(payload[i]);
        msg += tmp;
      }
    }
    
    Serial.println(msg);
}

Mit ein wenig streicheln und so bekomme ich jetzt eine Ausgabe für das Topic mit "../LEISTUNG_AKTUELL", den Wert von ".../POWER", hier True und False bekomme ich nur bei Änderung des Wertes, solange sich nichts ändert bleibt die Variable leer. Da werde ich ein "NC" oder so rein packen, wenn noch nichts da ist an Werten.

Zu meiner Ausgabe des Wertes LEISTUNG _AKTUELL, im Moment schwankt der ganz schön hin und her. Daran wird sich nichts ändern. Deshalb wollte ich gucken, dass ich die "Ausgabe" im Display etwas "optimiere", nur ich weiß nicht wie ich das am besten umstellen soll.

Ich denke an sowas in der Art: bei Werten von 0,01 - 9,99 W, werden 2 zusätzliche Leerzeichen hinzugefügt, bei Werten von 10.00 - 99,99 W wird eine zusätzliches Leerzeichen hinzugefügt und ab 100,00 W kommt kein Leerzeichen hinzu. Mein Problem ist weniger die If Abfrage, eher ich bekomme den Wert als Zeichenkette oder auch String und müsste diesen in eine Zahl umwandeln, auf 2 Stellen nach dem Komma runden und dann könnte ich die Abfrage starten und entsprechend lcd.setCursor und lcd.print(" ") die Ausgabe "Formatieren" oder gibt es da was einfaches?

Ich wollte gerne, dass der Dezimalpunkt an der gleichen Stelle bleibt. Finde ich Optisch etwas schöner.

Grob soll es wie folgt auf dem Display aussehen:

Leistung: 0,15 W
Zustand: ON / OFF

Michael

Ich habe aktuell die Ausgabe auf das Display weiter geleitet, doch das Bild auf dem LC Display ist eher zitterig und ohne Werte. Also Leidiglich "Leistung" und "Zustand" wird angezeigt.

Hier der erweiterte Code:

void setup() {
    Serial.begin(115200);
    setup_wifi();
  
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0,0);
    lcd.print("Arbeitsdisplay");
    lcd.setCursor(0,1);
    lcd.print("IP: ");
    lcd.setCursor(4,1);
    lcd.print(WiFi.localIP());
    lcd.setCursor(0,2);
    lcd.print("Messdatenanzeige");
    delay(1500);
        
    client.setServer(MQTT_BROKER, 1883);
    client.setCallback(callback);
}
 
void setup_wifi() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(SSID, PASSWORD);
    WiFi.config(staticIP, gateway, subnet, dns);
 
    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
    }
    
    Serial.println(WiFi.localIP());
}
void loop() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Leistung: ");
  lcd.setCursor(0,1);
  lcd.print("Zustand: ");
  
    if (!client.connected()) {
        while (!client.connected()) {
            client.connect(clientId);
            client.subscribe("alias/0/Shellys/Flaechenheizung/LEISTUNG_AKTUELL");
            client.subscribe("alias/0/Shellys/Flaechenheizung/POWER");
            delay(100);
        }
    }
    client.loop();
}
void callback(char* topic, byte* payload, unsigned int length) {
    String msg;
    if(strcmp(topic, "alias/0/Shellys/Flaechenheizung/LEISTUNG_AKTUELL") == 0) {
      for (byte i = 0; i < length; i++) {
        char tmp = char(payload[i]);
        msg += tmp;
      }
      Serial.println("Leistung: ");
      Serial.println(msg);

      lcd.setCursor(11, 0);
      lcd.print(msg);
      
    } else if(strcmp(topic, "alias/0/Shellys/Flaechenheizung/POWER") == 0) {
      for (byte i = 0; i < length; i++) {
        char tmp = char(payload[i]);
        msg += tmp;
      }
      Serial.println("Power: ");
      Serial.println(msg);

      lcd.setCursor(11, 1);
      lcd.print(msg);
    }
}

Ich habe oben die Variabeln und so weg gelassen. So muss ich nicht jedes Mal WLAN - Daten und so maskieren :wink:

Nur irgendwie kommt so keine vernünftige Anzeige zu stande. Der Teil beim Setup ist deutlich lesbar, der Rest nicht wirklich brauchbar.

Michael

Las mir mal nen kurzen Moment - ich hatte was im Kopf, als ich meine Frage stellte - ich muss das mal kurz mit Deinem vergleichen.
Das wird. Ganz bestimmt.

Das fällt dir auf die Füsse.
Dein Display wird flackern.

Ha, hier wird das zum Problem:

(bzw. weiter oben mit 11,0)
Du weisst nicht, wie sich die Nachkommastelle abbildet.
Das ist recht einfach zu lösen, wenn Du msg formatierst.

kurze Nachfragen dazu:
Werden die Werte mal ohne (600) und mal mit (550.1) ausgeliefert?
oder immer wenigstens mit einer Nachkommastelle? (600.0)
Welche Anzahl Nachkommastellen werden maximal übertragen? Geht auch 500.1111?
Du schreibst auf 11,0 / 11,1 - was ist das für ein Display? 16 oder 24?
Edit:
Das wird dann vermutlich heute nichts mehr - mal schaun, was morgen kommt. Na dann....

Es gibt immer mindestens 1 zum Teil 2 Stellen nach dem Komma, bei 0 kam auch schon mal ohne Stellen nach dem Koma, mal mit. Kann aber zwischen durch eine Softwarebug sein.

Das Display ist ein 20x4 Display, daher Position 11, dann muss das Wort Leistung nicht jedes Mal neu geschrieben werden. Zumindest war das so mein Plan.

Bei der Ausgabe im Seriellmonitor gibt es max 2 Stellen nach dem Koma.

Und diese 2 Stellen wären schön, theoretisch würde aber auch locker eine Stelle ausreichen. Man könnte so fragen ob sich was verändert hat, sonst muss Display auch nicht ständig neu beschrieben werden.

Michael

PS: ich bemühe mich immer zeitnah zu reagieren. Doch zwischendurch braucht meine Frau oder der hin Aufmerksamkeit :wink:

Auf einem LCD kannst du aber nicht einmal zwei Stellen und danach eine Stelle haben. Dann bleibt eine Ziffer stehen. Du musst deine Ausgabe auf einen konstante Breite formatieren

Das ist der Defaultwert, wenn Du keine andere Anzahl Nachkommastellen angibst.

Gruß Tommy

Nein.
Wenn mit Nachkommastelle angezeigt wird, muss die Anzahl möglicher Stellen nach dem Komma bekannt sein, um die Position des Komma zu bestimmen. Nicht mehr.

Es sollte bekannt sein, welche Anzahl Stellen der String vor dem Komma hat um den Rest zwischen der Startposition von .print und dem tatsächlichen Datum mit Spaces aufzufüllen.

Schliesslich muss bekannt sein, welches Datum die Nachkommastellen haben, um den Teil nach der letzten Stelle bis zum Zeilenende erneut mit Spaces aufzufüllen.

Zudem muss bekannt sein, ob überhaupt ein Komma vorhanden ist - wenn nicht muss dieses an der richtigen Stelle erzeugt werden, um zu garantieren, das das Datum (der ausgegebene Wert) nicht verrutscht.

Das geht.
Und ja, heute nicht mehr. Ich habe es schon als Ansatz für den TO - aber auch meine Standorte ändern sich...

Der sinnvollere Weg, auch für eine intuitive Ablesung, ist aber eine feste Anzahl an Nachkommastellen.
Nicht alles, was technisch machbar ist, ist auch sinnvoll für den Betrachter.

Gruß Tommy

Ich zitier mich mal selbst und habe einen ausführlichen Codeschnipsel den ich gestern schon vorbereitet hatte.

Teste den aus.
Wenn was unklar ist, frag.

String test = "1035.96";
const uint8_t maxNK = 3;
const uint8_t maxLength = 12; // Start bei 11 - Ende bei 24 auf dem LCD
void setup()
{
  Serial.begin(115200);
  delay(250);
  Serial.println(F("Start..."));
  Serial.print(F("BeispielString: "));
  Serial.println(test);
  unsigned int laenge = test.length();
  Serial.print(F("Länge des String in Zeichen: "));
  Serial.println(laenge);
  unsigned int kommastelle = test.lastIndexOf('.');
  Serial.print(F("Position des letzten(!) . beginnend ab 0!! von links: "));
  Serial.println(kommastelle);
  if (kommastelle > laenge)
  {
    Serial.println("kommastelle liegt drüber");
    test = test + ".";
    laenge = test.length();
    kommastelle = test.lastIndexOf('.');
    Serial.print(F("addiere . auf "));
    Serial.println(laenge);
  }
  while (laenge - kommastelle <= 3)
  {
    Serial.println(F("hänge Nachkommastellen an"));
    test = test + "0";
    laenge = test.length();
    Serial.print(F("neue Länge: "));
    Serial.println(laenge);
  }
  Serial.println(F("Ausgabe auf dem Seriellen Monitor mit Nachkommastellen immer an der selben Position von rechts: "));
  Serial.println(test);
  Serial.print(F("Startposition der Anzeige: "));
  Serial.println(maxLength - laenge + 11);
  // auffüllen mit Leerzeichen
  String msg = "";
  while (msg.length() < maxLength - laenge)
  {msg = msg + " ";}
  msg = msg + test;
  Serial.println(msg);
}

void loop()
{
}

Hey,

ich hab den Code mal getestet und oben bei dem "Teststring" etwas rum gespielt. Mal eine Stelle vor, mal eine nach dem Komma hinzugefügt oder gelöscht und so weiter. Jetzt guck ich mal den Teil irgendwie in mein Script zu integrieren und gucken wir mal ob er es dann tut, wie ich es möchte.

Danke fürs testen - und da es Dir offensichtlich gefällt, empfehle ich mal daraus eine Funktion zu machen:
Die Funktion bekommt den "Wert" übergeben, sowie die Anzahl der NK und die Anzahl der anzuzeigenden tatsächlichen Zeichen.

Zurück gibt die Funktion dann den String zur Ausgabe.
Damit ist das auch zu @Serenifly seiner Idee kompatibel, bzw. wird der Ansatz übernommen und Du kannst auf das .clear vollständig verzichten, wenn Du nur neu schreibst.

Dann gehört zudem eine Prüfung auf die Länge der NK sowie folgend eine Prüfung der Gesamtlänge hinein.
Das lässt sich da gleich mit abhandeln.

Wenn Hilfe brauchst, meld Dich.
PS: Hier:

gehört an Stelle der 3 ein maxNK rein.

Mit der eigenen Funktion dazu werde ich mir mal angucken.

Aber erstmal wollte ich den Code zeigen. Ganz passte das Original, nur eines habe ich noch, was noch nicht richtig läuft. Wenn ich die Nachkommastellen auf 1 setze schneidet er nichts ab. Aber mit 2 NKStellen klappt es so wie geplant.

...
void callback(char* topic, byte* payload, unsigned int length) {
    String msg;
    const uint8_t maxNK = 2;
    const uint8_t maxLength = 12;
    
    if(strcmp(topic, "alias/0/Shellys/Flaechenheizung/LEISTUNG_AKTUELL") == 0) {
      for (byte i = 0; i < length; i++) {
        char tmp = char(payload[i]);
        msg += tmp;
      }
      Serial.println("Leistung: ");
      Serial.println(msg);
      unsigned int laenge = msg.length();
      unsigned int kommastelle = msg.lastIndexOf('.');
      if (kommastelle > laenge)
      {
          Serial.println("kommastelle liegt drüber");
          msg = msg + ".";
          laenge = msg.length();
          kommastelle = msg.lastIndexOf('.');
      }
      while (laenge - kommastelle <= maxNK)
      {
        msg = msg + "0";
        laenge = msg.length();
      }
      while (msg.length() < maxLength - laenge)
      {
        msg = msg + " ";
      }
      
      lcd.setCursor(maxLength - laenge + 4, 0);
      lcd.print(msg);
      lcd.setCursor(16,0);
      lcd.print("W");
      
    } else if(strcmp(topic, "alias/0/Shellys/Flaechenheizung/POWER") == 0) {
      for (byte i = 0; i < length; i++) {
        char tmp = char(payload[i]);
        msg += tmp;
      }
      if(msg == "true")
      {
        msg = "ON ";
      }
      else {
        msg = "OFF";
      }

      lcd.setCursor(14, 1);
      lcd.print(msg);
    }
}

Ja.
Das ist das, was ich beschrieben habe.
Wenn Du nur 1NK darstellen möchstest, aber der Wert mit 2NK kommt, musst Du das bauen.

Ich seh mal zu, ob ich das umbaue :wink:

Aber hier mal ein Foto zu dem Ergebnis:

Denke wird auch mal gerne gesehen.

1 Like

:+1:

Ich würde die Zeile Zustand eins nach rechts schieben, damit die : untereinander stehen.
Und dann das on weiter links mit einem Leerzeichen ansetzen.

Schicke Lötstellen.

Ich hab nach genauer Beobachtung gemerkt, dass wenn die Leistung unter 10,00 geht, dass dann die Zehner bzw. Hunderter Stelle erhalten bleibt. Also das die nicht aus dem Display "verschwinden".

Wollte aber parallel dazu auch mal selbst was testen. Vielleicht komme ich auch dahin ;-).

Ja, das ist ok. Du musst Dir nur noch Gedanken machen, wie Du die weg bekommst, wenn Du den Ansatz von mir nicht nutzt.

Das geht, in dem z.B. die Zeile vor der Ausgabe komplett mit " " überschrieben wird. - oder eben mit Bestimmung der Länge die Position feststellen ab wann das Datum ausgegeben wird und vorher ab erster Position (11) bis zum Begin der Ausgabe einfach nur jeweils ein weiteres Leerzeichen schreiben.