Nextion Display und die Tücken mit dem Senden von Informationen

Hallo,

Ich habe mir einmal die Arbeit gemacht eine .h zu erstellen wo ich alles enthalten habe was man so braucht zum Senden verschiedener Befehle die an ein Nextion Display gesendet werden.
Ich wollte den .ino Code so kurz wie möglich halten und auch die Befehle die zum Senden benötigt werden Vereinfachen. Ich hoffe es gelungen.

Ihr könnt gerne noch Sachen Verändern oder verbessern. Schaut es euch einfach an

/*****************************************************************************
****                   Beispiele                                          ****
****                                                                      ****
****       Wechsel zu einer Seite namens "page1"                          ****
****  sendToNextionPage("page1");                                         ****
****       Einen Textwert an das Nextion-Display senden                   ****
****  sendToNextion("t0", "Hallo Welt!");                                 ****
****       Einen Integer-Wert an das Nextion-Display senden               ****
****  sendToNextion("n0", counter);                                       ****
****      Einen Float-Wert an das Nextion-Display senden                  ****
****  sendToNextion("f0", temperature, 2);  // , 2 Dezimalstellen         ****
****      Einen Boolean-Wert an das Nextion-Display senden                ****
****  sendToNextion("b0", isOn ? 1 : 0);                                  ****
****                                                                      ****
****      Senden eines Strings und eines Float-Wertes                     ****
****      an verschiedene Variablen auf dem Nextion-Display               ****
****                                                                      ****
****  sendToNextion("t0", "Temperatur:");  // Text                        ****
****  sendToNextion("n1", 25); // Integer (z.B. für einen Statuswert)     ****
****  sendToNextion("f0", 23.75, 2); // Float-Wert mit 2 Dezimalstellen   ****
****                                                                      ****
*****************************************************************************/

#ifndef __NEXTCOMMAND_H__
#define __NEXTCOMMAND_H__

#include <Arduino.h>
#include "nano_config.h"

/**********************************************************
* Defination der Serialen Schnittstelle Arduino NanoESP32 *
*                                                         *
* #define TXPin 18   // GPIO2    Nextion Display          *
* #define RXPin 19   // GPIO3    Nextion Display          *
* HardwareSerial nextion(1);                              *
*                                                         *
* nextion.begin(115200, SERIAL_8N1, RXPin, TXPin);        *
*                                                         *
**********************************************************/
// Konstante für Endzeichen
const char END_CHAR[] = "\xFF\xFF\xFF";

// Senden eines generischen Befehls an das Nextion-Display
void sendToNextion(const String& command) {
  if (!nextion) {
    Serial.println("Fehler: Nextion-Display nicht verbunden!");
    return;
  }
  nextion.print(command);
  nextion.print(END_CHAR);
}

// Überladung: Senden eines numerischen Wertes an eine Variable (unsigned long)
void sendToNextion(const char* varName, unsigned long value) {
  String command = String(varName) + ".val=" + String(value);
  sendToNextion(command);
}

// Überladung: Senden eines Textwertes an eine Variable (String)
void sendToNextion(const char* varName, const String& value) {
  String command = String(varName) + ".txt=\"" + value + "\"";
  sendToNextion(command);
}

// Steuert die Sichtbarkeit einer Komponente (z. B. sichtbar/unsichtbar)
void sendToNextion(const String& componentName, int visibility) {
  String command = "vis " + componentName + "," + String(visibility);
  sendToNextion(command);
}

// Wrapper für Rückwärtskompatibilität
void writeHMICmd(const String& cmd) {
  sendToNextion(cmd);
}

// Überladung: Senden eines Float-Wertes an eine Variable (mit Dezimalstellen)
void sendToNextion(const char* varName, float value, int decimalPlaces = 2) {
  char buffer[16]; // Buffer für die Umwandlung von Float in String
  dtostrf(value, 0, decimalPlaces, buffer); // Float-Wert umwandeln
  String command = String(varName) + ".txt=\"" + String(buffer) + "\"";
  sendToNextion(command);
}

// Überladung: Senden eines int-Wertes an eine Variable
void sendToNextion(const char* varName, int value) {
  String command = String(varName) + ".val=" + String(value);
  sendToNextion(command);
}

// Senden eines Seitenwechsels
void sendToNextionPage(const String& pageName) {
  String command = "page " + pageName;
  sendToNextion(command);
}

#endif // __NEXTCOMMAND_H__

in diesen Sinne euch noch ein Schönes Neues Jahr Wünsche

Das ist löblich!

Aber was mir so gar nicht gefallen mag ist die sehr intensive Verwendung der String-Klasse.
Das ist eigentlich nicht erforderlich, weil entweder die einzelnen Teile auch nacheinander an das Nextion geschickt werden können (mehrere print()) oder das Kommandozum Senden in einem char-Array (Puffer) mittels snprintf() zusammengebaut werden kann.

Das wünsche ich Dir auch!

überarbeit :wink:

/*****************************************************************************
****        Erstellt by @Luzi und ist zur freien Nutzung,                 ****
****             es gibt keine Beschränkungen,                            ****
****               dieser Code kann beliebig                              ****
****             kopiert oder verändert werden.                           ****
****                                                                      ****
****                   Beispiele                                          ****
****                                                                      ****
****       Wechsel zu einer Seite namens "page1"                          ****
****  sendToNextionPage("page1");                                         ****
****       Einen Textwert an das Nextion-Display senden                   ****
****  sendToNextion("t0", "Hallo Welt!");                                 ****
****       Einen Integer-Wert an das Nextion-Display senden               ****
****  sendToNextion("n0", counter);                                       ****
****      Einen Float-Wert an das Nextion-Display senden                  ****
****  sendToNextion("f0", temperature, 2);  // , 2 Dezimalstellen         ****
****      Einen Boolean-Wert an das Nextion-Display senden                ****
****  sendToNextion("b0", isOn ? 1 : 0);                                  ****
****                                                                      ****
****      Senden eines Strings und eines Float-Wertes                     ****
****      an verschiedene Variablen auf dem Nextion-Display               ****
****                                                                      ****
****  sendToNextion("t0", "Temperatur:");  // Text                        ****
****  sendToNextion("n1", 25); // Integer (z.B. für einen Statuswert)     ****
****  sendToNextion("f0", 23.75, 2); // Float-Wert mit 2 Dezimalstellen   ****
****                                                                      ****
*****************************************************************************/

#ifndef __NEXTCOMMAND_H__
#define __NEXTCOMMAND_H__

#include <Arduino.h>
#include "nano_config.h"

/**********************************************************
* Defination der Serialen Schnittstelle Arduino NanoESP32 *
*                                                         *
* #define TXPin 18   // GPIO2    Nextion Display          *
* #define RXPin 19   // GPIO3    Nextion Display          *
* HardwareSerial nextion(1);                              *
*                                                         *
* nextion.begin(115200, SERIAL_8N1, RXPin, TXPin);        *
*                                                         *
**********************************************************/

// Konstante für Endzeichen
const char END_CHAR[] = "\xFF\xFF\xFF";

// Senden eines generischen Befehls an das Nextion-Display
void sendToNextion(const char* command) {
    if (!nextion) {
        Serial.println("Fehler: Nextion-Display nicht verbunden!");
        return;
    }
    nextion.write(command);  // Senden des Befehls
    nextion.write(END_CHAR, sizeof(END_CHAR) - 1);  // Senden der Endzeichen
}

// Überladung: Senden eines Integer-Wertes
void sendToNextion(const char* varName, int value) {
    char command[32];
    snprintf(command, sizeof(command), "%s.val=%d", varName, value);
    sendToNextion(command);
}

// Überladung: Senden eines unsigned long-Wertes
void sendToNextion(const char* varName, unsigned long value) {
    char command[32];
    snprintf(command, sizeof(command), "%s.val=%lu", varName, value);
    sendToNextion(command);
}

// Überladung: Senden eines Textwertes (mit const char*)
void sendToNextion(const char* varName, const char* value) {
    char command[64];
    snprintf(command, sizeof(command), "%s.txt=\"%s\"", varName, value);
    sendToNextion(command);
}

// Überladung: Senden eines Textwertes (mit String)
void sendToNextion(const char* varName, const String& value) {
    sendToNextion(varName, value.c_str());  // Umwandlung von String zu const char* und dann weiterleiten
}

// Überladung: Senden eines Float-Wertes mit Dezimalstellen
void sendToNextion(const char* varName, float value, int decimalPlaces = 2) {
    char buffer[16];
    char command[64];
    dtostrf(value, 0, decimalPlaces, buffer);  // Float in String umwandeln
    snprintf(command, sizeof(command), "%s.txt=\"%s\"", varName, buffer);
    sendToNextion(command);
}

// Sichtbarkeit einer Komponente steuern
void setComponentVisibility(const char* componentName, int visibility) {
    char command[32];
    snprintf(command, sizeof(command), "vis %s,%d", componentName, visibility);
    sendToNextion(command);
}

// Seitenwechsel auf dem Display
void sendToNextionPage(const char* pageName) {
    char command[32];
    snprintf(command, sizeof(command), "page %s", pageName);
    sendToNextion(command);
}

#endif // __NEXTCOMMAND_H__

1 Like

Ich komm mit der festen Länge von command in den Funktionen nicht klar.
Wenn Du jeweils die Länge mit übergibst, kannst Du das dynamisch zur Laufzeit zusammenstellen.

Dann würde das so aussehen:

// Seitenwechsel auf dem Display
void sendToNextionPage(const char *pageName, const byte &len)
{
  
  char command[len+8];
  snprintf(command, sizeof(command), "page %s", pageName);
  sendToNextion(command);
}

Und der Aufruf via:

char pagename[]={"page1"};
sendToNextionPage(pagename, sizeof(pageName));

Hallo my_xy_projekt.

erst einmal wünsche ich dir ein Fohes neues Jahr und danke dir dafür für die Hilfe die ich immer von dir bekommen habe.

Ich werde den Code etwas Anpassen, siehe ende und sag mir ob dir das besser gefällt?

es gibt eine Kleinigkeit, die noch umgestellt werden können, um potenzielle Fehler zu vermeiden, insbesondere beim Erstellen des command-Arrays mit einer dynamischen Länge.
Das Problem liegt in der Berechnung der Array-Größe in char command[len+8];. Der Wert len ist die Länge des pageName, aber wir benötigen zusätzlich Platz für den Text page und die Nullterminierung des Strings.
Die Größe des Befehlsstrings muss also mindestens len + 6 sein:

  • 4 Zeichen für das Wort "page" und ein Leerzeichen (page ),
  • und 2 Zeichen für das Nullterminierungszeichen \0.

Aber es würde etwas anderst gehen
Eine sichere Methode wäre die Verwendung eines Puffers, der groß genug ist, um auch längere Seitennamen zu handhaben.

// Seitenwechsel auf dem Display
void sendToNextionPage(const char *pageName) {
  size_t len = strlen(pageName); 
  char command[len + 6];  
  snprintf(command, sizeof(command), "page %s", pageName); 
  sendToNextion(command);
}

so macht es dann mehr sinn denke ich

und der befehl für das senden bleibt dann auch einfacher

sendToNextionPage("page1");  // Wechselt zu der Seite "page1"

in dem Sinne, hab noch einen Schönen 1 Januar 2025 :slightly_smiling_face:

1 Like

Moijn,
also nochmal zurück.
Denkfehler.
Die Übergabe erfolgt nicht mit "page1", sondern mit 1
Der Aufruf dann also:

char pagename[]={"1"};
sendToNextionPage(pagename, sizeof(pageName));

Die Funktion baut das dann zusammen mit:

// Seitenwechsel auf dem Display
void sendToNextionPage(const char *pageName, const byte &len)

Dabei ist jetzt pagename nur die Ziffer und in len steht die Länge der Zahl.
len+8ergibt sich aus "page %s" wobei es richtigerweise +6 heißen muß.

{
  char command[len+6];
  snprintf(command, sizeof(command), "page %s", pageName);
  sendToNextion(command);
}

Ob man die Zahl als char übergeben muß, wäre zu überlegen.

haste auch wieder recht. kommt schon mal vor heute*gg

Aber was passiert wenn du nur die Zahl übergibst und irgendwann änderst du am nextion code etwas und fügst noch eine seite oder mehr hinzu die vieleicht dazwischen liegen ? aus 1 wird 2 aus 4 wird 6. ist es dan nicht ratsamer den eindeutigen namen zu benutzen. Weil so musst du alles wieder anpacken und anpassen.
Aber zum Glück brauch man die seiten übergabe normal nicht, man spricht ja im regelfall eh gleich den Variablennamen an den man braucht.
Vieleicht habe ich ja auch gerade einen kleinen denkfehler, ist ja noch früh am morgen*gg und es nervt mich noch ein anderes Problem gerade beim nextion und zwar das empfangen von daten. Weil das wollte ich hier dann auch noch reinstellen wenn es fertig ist, das der arduino oder esp alle daten die vom nextion kommen auswertet und verarbeitet. aber das ding will nicht so wie ich es gerne hätte. Aber das ein anderes Thema.

1 Like

Warum sollte Mensch das tun?
Eine neue Seite kommt einfach hinten dran.
Die Auswahl erfolgt doch mit der Bezeichnung, Da ist es egal an welcher "Stelle" die liegt.

Die Seitenübergabe brauchst Du schon.
Und vor allem ist die Datenübergabe recht tricky.
In einem anderen Thread hatten wir das mal zerlegt.
Beschreibst Du eine Variable im Nextion, die nicht auf der aktuellen Seite ist, gibt der Nextion einen Fehlercode zurück.
Das fummelt dir dann aber in die Kommunikation vom Ereignis Nextion -> Auswertung Arduino und muss zusätzlich abgefangen werden.

Von daher ist das alles doch etwas aufwändiger....

Hatte ich mir auch überlegt, aber dann verworfen.
Wozu auch Parameter an eine Funktion übergeben die diese mittels print an den Nextion sendet, da kann ich auch direkt ein print machen.

Naja, viele print und unübersichtlich.

So in etwa mache ich es, das Kommando wird "gebastelt" und

void senden_an_display() {  // sendet an Display

  debm("senden_an_display");
  debv("Text an Display", text_send);

  Serial.print(text_send);
  Serial.print(term);

  delay(10);  // für Zeilenvorschub in HTerm

  //stopper("gesendet");

  debln();
  debm("ende senden_an_display");
}
// Ende senden_an_display ---------------------------------

Das hat auch den Vorteil, dass man in der Testphase das komplette Kommando prüfen kann, welches rausgeht.

Wozu und wieso?
Etwas simpleres als einer Variable einen Wert mit =xy zuzuweisen gibt es doch nicht.

Das ist falsch und passiert nur wenn man die Variable nicht als global definiert.
:dizzy_face: Vor allem, was will man auf dem Display sehen, wenn permanent die Daten auf irgendwelchen Seiten aktualisiert werden und hierzu immer die entsprechende Seite angesprungen werden müsste.
Lokale Variablen kann man nur dann ansprechen wenn das Display auf der entsprechenden Seite ist, dieses Handling ergibt aber nicht viel Sinn.
Das m.E. einzige was nicht global funktioniert ist -> click.

cu

und ein gesundes neues Jahr

Es wird ein klein wenig Off Topic - @luzie : Wenn es Dir zuviel wird, sag Bescheid, dann lösche ich meinen Beitrag wieder und nach 24h ist er dann weg.

Ja, was wir bei dem elektrifizierten BMW ja leidvoll erfahren durften.

Hier noch das HMI-File zum Ausprobieren (Endung umbenennen!) - es gibt auf jeder Seite einen globalen Text (p0t1 und p1t1). Global führt nur dazu, dass der aktuelle Wert erhalten bleibt, wenn man die Page verlässt und später wieder anzeigt.

test2.hmi.txt (7,5 MB)

Schau ich mir mal an, aber es geht definitiv.
Deswegen ja auch der Hinweis bei Nextion für click:
As event code is always local, object can not be page prefixed

Hier ein Beispiel aus meinem derzeitigen Projekt:

text_send = "start.t" + String(pos_text_start * 2) + ".txt=\"heute\"";
senden_an_display();

if (entries[pos].start.hour > 0) {  // Termin heute mit Zeitangabe?

   int pos_display_h = pos + 31;  // Variablennummer auf page meldungen berechnen
   int pos_display_m = pos + 41;

   debv("pos_h: ", pos_display_h);
   debv("pos_m: ", pos_display_m);

   // Terminzeit auf page test
   text_send = "test.n" + String(pos_display_h) + ".val=" + String(entries[pos].start.hour); 
   senden_an_display();

   text_send = "test.n" + String(pos_display_m) + ".val=" + String(entries[pos].start.min);
   senden_an_display();
}

Wie Du siehst wird einmal auf page start ausgegeben und, falls Termin mit Zeit die Zeit auf page test.

Auf dem Display im Wintergarten werden sämtliche Datenänderung, Iconänderungen usw. lediglich über -> wetter.name.wert= aktualisiert.

cu

Danke für die Unterschiedlichen Meinungen und Anregungen. @wno158, du musst kein Beitrag löschen.

Ooops - er hat recht - ich muss reumütig zurückrudern!

Der Unterschied ist der, dass Du den Namen der Seite voranstellst.
In meinem Test-HMI führt also - auf Page 0 ("Startseite") stehend -

p1t1.txt="Das ist ein Test"

zur beschriebenen Fehlermeldung, während

Canvas.p1t1.txt="Das ist ein Test"

ohne Fehlermeldung funktioniert (jedenfalls im Simulator).

:smile:
Wollte Dir gerade mitteilen:


Natürlich muss man die Seite mit angeben, erstens nummeriert das Display auf jeder Seite gleichlautend und selbst eindeutige Namen funktionieren nicht.

Meinen Debugger hast Du anscheinend nicht angeschaut, dann wäre Dir das dort schon aufgefallen.

@luzie
Schau Dir mal meinen Beitrag bezüglich VS an, ich bastele gerade meinen Empfang vom Nextion um (64 Befehle mit 0, 2 oder 4 Datenbytes.

cu

Nachtrag: Timer funktionieren auch nicht global, sie laufen nur wenn das Display auf der entsprechenden Seit ist.

@ hawe07546

Schau Dir mal meinen Beitrag bezüglich VS an, ich bastele gerade meinen Empfang vom Nextion um (64 Befehle mit 0, 2 oder 4 Datenbytes.

hast du auch einen Link dazu?

Nachtrag: Timer funktionieren auch nicht global, sie laufen nur wenn das Display auf der entsprechenden Seit ist.

Ja das ist klar.

Ao nun nocheinmal zurück zu übergabe mit Seitenamen. Ja ich Stimme zu das es von Vorteil ist den mit zu übegeben, somit ist das leerzeichen was im Debugger angezeigt wird nicht vorhanden.

12:48:10.117 -> Empfangene Nachricht: ���zelle25

Naja, sonst macht das Ganze keinen Sinn, wie soll das funktionieren wenn die Seite die man anschaut ständig weg geht?

????

nextion_004.zip (398 Bytes)
Zwei Funktionen sollen da noch rein, einmal lesen Daten Anzahl und lesen Daten bis Terminator kommt.

cu

cu

wie ich das sehe bindest du aber die nextion lib ein?
auf die habe ich aber verzichtet, sie brauch einfach zuviel speicher und laut aussage von vielen hier ist sie schlecht geschrieben.

das mit den drei ? habe ich auch rausbekommen.
Am anfang wenn das Display startet sendet es die 88. display reday und im selben augenblick sendet er invalie Code. obwohl vom display code nichts abgeschickt wird. dies kommt regelmäßig. bei jedem senden an das display, aber das ein anderes thema.

hier ging es ja nur um den Code den ich am Anfang gepostet habe und ob er noch stellen beinhaltet die verbessert werden können. wie am anfang durch my_xy_projekt

P.S
ich habe aber bemerkt wenn ich den Code so verwende wie er oben steht und ich sende den befehl
sendToNextion("wlan.cb_ssids", ssid);
kommt es auf dem Display an der richtigen Stelle an, Sende ich aber diesen
sendToNextion("main.va_ipaddr", ipAddress);
wird es nicht auf dem Display angezeigt. schreibe ich es aber so
sendToNextion("va_ipaddr", ipAddress);
kommt es an der richtigen Stelle am display an.
Fazit für micht. Ich muss den Seitennamen nicht zwingend mit angeben.

Wo genau willst Du das sehen?
Da ist nix.

Das ist nicht korrekt, das Display sendet nichts was Du ihm nicht sagst.
Wenn Du die Standardeinstellungen drin lässt kommt natürlich die Startsequenz:

Das Display sendet auch nichts zurück wenn es Daten bekommt.

Deine Fehlerbeschreibung kann so nicht stimmen, entweder das Display verarbeitet ankommende Daten oder sendet eine Fehlermeldung.
Und Du kannst definitiv nicht auf eine Seite schreiben, auf der Du nicht bist wenn Du die Seite nicht nennst.

cu

in deinem link mit der nextion_004 die du gepostet hast

#include <Arduino.h>

#include "nextion.h"


NEXTION nextion;


void setup() {

  Serial.begin(115200);

  nextion.begin(25, 41);  // Variablengrenzen

}


void loop() {

  nextion.read();
  
  Serial.println(nextion.nextion_command);

  Serial.println(nextion.nextion_data[0]);
  Serial.println(nextion.nextion_data[1]);
  Serial.println(nextion.nextion_data[2]);
  Serial.println(nextion.nextion_data[3]);


  delay(5000);

}

das ist so nicht ganz richtig. das display sendet am anfang ein ready "00 00 00 ff ff ff 88 ff ff ff" case 0x88 und es sendet dir noch andere Infos die du abfragen kannst.
Da ich jede Komonunikation auf der schnittstelle abfrage wegen debuggug sehe ich das doch.
kannst du gerne einmal selber testen was es dir sendet.

          switch (commandbuffer[0]) {
            case 0x00:
              Serial.println(F("Invalid instruction "));
              break;
            case 0x01:
              Serial.println(F("Nextion is started "));
              break;
            case 0x1C:
              Serial.println(F("Assignment failed to assign "));
              break;
            case 0x1A:
              Serial.println(F("Invalid Variable Name "));
 //             string_bytes(commandbuffer);
              break;

            case 0x88:
              Serial.println(F("Nextion ready "));
              break;
            case 0xA1:
              Serial.println(F("Invalide variable "));
              break;
            }

Ich frage diese sachen Explizit ab zum testen ob alles richtig übertagen wird, was ja leider nicht der Fall ist. Aber es liegt am Code vom arduino und nicht am display