Nextion Display mit eigenem Code

Hallo,

ich habe meinen recht simplen Befehlscode für die Kommunikation mit dem Display umgeschrieben.
Was haltet Ihr davon?

Die Befehlsausgabe zum Display beschränkt sich auf simples ausgeben der "extern" zusammen gebastelten Kommandokette, eine weitere Funktion zum einfügen von z.B. ".txt=" in die Struktur halte ich nicht für sinnvoll.

In Richtung Display -> Controller verwende ich eine Struktur, die entsprechend gesendet werden muss:
Startbyte, Kommandobyte, Datenbytes, Endbyte.
Jeder weiß ja was der entsprechende Tastendruck bewirken soll und kann die entsprechenden Daten zusammenstellen.

Das Kommandobyte enthält entweder die aktuelle Seitennummer oder eine selbst definierte "externe" ID von 0x20 bis 0xFF, das sind 224 ID's, die bis zu 15 Byte Datenanhang haben können.
Das sollte für fast alle Anwendungen reichen.
Falls nicht wird der Bereich von 0x00 bis 0x31 als Seitennummer behandelt, der 127 Komponenten-ID's folgen können. Von diesen kann eine beliebige Anzahl als dual-state deklariert werden, die direkt im Bit 7 ihren Status senden.

Das Endbyte besteht aus 0xF und Anzahl gesendete Datenbytes.

AA C1 33 44 55 F3
ext. ID C1
drei Bytes 33 44 55

Kommando bzw. Page liegen als Byte (switch, case) vor, die Daten (max. 15 Byte) in einem Array.
Es gibt eine weitere Funktion, die direkt den "Wert" einer Komponente abfragt
(get page.component) und als int oder Liste (txt) ablegt.

Vielleicht hat jemand Interesse und testet mal mit.

cu
cu

Anzahl der Datenbytes nach dem 3 0xFF halte ich für ungünstig.
Welchen Vorteil soll Dein System gegenüber der normalen Kommunikation ohne Nextion-Lib bringen?
Wenn da jemand mit testen können soll, fehlt Deine Lib dazu.

Gruß Tommy

Das verstehe ich nicht? Zeige mal ein Beispiel.

Was meinst Du mit "normaler" Kommunikation?

cu

Erst mal

Ich habe überhaupt keine lilablassblaue Verschimmerung wie deine Excel-Tabelle mit vorhanden Nextion-Projektdateien zusammenhängt.

In einem Nextion-Projekt werden üblicherweise im Nextion-Editor Objekte wie Labels, Buttons usw. definiert und diese Objekte kann mann dann über ihre Namen ansprechen.

Und wie hängen dann deine Bytefolgen damit zusammen?
Ich frage mich sogar ob du überhaupt ein Nextion-Display hast das du damit ansteuerst.

void ButtonStartRot() {
  Serial2.print(F("b0.bco=63488"));  // "b0" Bezeichner de Buttons ".bco" Eigenschaft backgroundcolor des Buttons
  Serial2.print(F("\xFF\xFF\xFF"));         // Sends 0xff 0xff 0xff, which tell the Nextion that it has a complete string of data
}

void ButtonStartWeiss() {
  Serial2.print(F("b0.bco=65535"));
  Serial2.print(F("\xFF\xFF\xFF"));         // Sends 0xff 0xff 0xff, which tell the Nextion that it has a complete string of data
}


void ButtonStopRot() {
  Serial2.print(F("b1.bco=63488"));
  Serial2.print(F("\xFF\xFF\xFF"));         // Sends 0xff 0xff 0xff, which tell the Nextion that it has a complete string of data
}

void ButtonStopWeiss() {
  Serial2.print(F("b1.bco=65535"));
  Serial2.print(F("\xFF\xFF\xFF"));         // Sends 0xff 0xff 0xff, which tell the Nextion that it has a complete string of data
}

Er beendet den Inhalt mit 3 Mal 0xff, wie die normale Übertragung und sendet danach die Anzahl der Nutzbytes:

0xff,0xff,0xff,Anzahl

Ich meine die Nutzung des Nextion Instruction Set, da man meist sowieso nur wenige Funktionen braucht.
Dazu die universelle Empfangsroutine nach Whandall

// nach Whandall 05.09.2019 forum.arduino.cc
// https://forum.arduino.cc/t/nextion-component-id-trennen/609217/19
const byte bufferSize = 15;
uint8_t buffer[bufferSize];

void setup() {
  Serial.begin(115200);
  Serial2.begin(9600); 
}

bool readNex(Stream &s) {
  static uint8_t bIndex = 0;
  bool allesDa = false;
  if (s.available()) {
    uint8_t inChar = s.read();
    buffer[bIndex++] = inChar;
		// 0x71 bei diesen Paketen können drei 0xFF Bestandteil des Pakets sein,
    // also ergibt es nur Sinn auf die Delimiter hinter dem Wert zu testen
    if (inChar == 0xFF && (bIndex >= (*buffer == 0x71 ? 8 : 3)) &&
        buffer[bIndex - 2] == 0xFF && buffer[bIndex - 3] == 0xFF) {
      allesDa = true;
    } else {
      allesDa = (bIndex == bufferSize);
    }
  }
  if (allesDa) bIndex = 0; // Index zurücksetzen 
  return allesDa;
}

void loop() {

unsigned long varVomNextion =0;
bool fertig = readNex(Serial2);
  if (fertig && buffer[0] == 0x71) { // Buffer[0] enthält das Kennbyte, nach dem die Auswertung erfolgen muss - hier numerische Daten
    varVomNextion = buffer[1]+buffer[2]*256+buffer[3]*65536+buffer[4]*16777216;
	  Serial.println(varVomNextion);
	}
}

Gruß Tommy

Wie wird denn diese Anzahl verarbeitet wenn die Kennung "Ende des Strings" = 0xFF0xFF0XFF schon gesendet wurde?

Mit meinem Halbwissen würde ich jetzt vermuten, dass bytes die nach der Ende-Kennung gesendet werden als Beginn der nächsten Bytefolge interpretiert werden.

@hawe07546

wie wäre es denn mit einem vollständigen und compiliefähigen Democode der deine Sachen verwendet?

Sorry - langer Text.

tl;dr
Da ich momentan kein freies Nextion liegen habe kann ich keinen Beitrag zum Entwanzen leisten.

Die mir bekannte Quelle für den Nextion Instruction Set weist drei Möglichkeiten auf:

  • Die allseits bekannte Form mit dem Vorteil, dass die Kommandos (bis auf die drei FF am Ende) im Klartext lesbar sind, was m.E. die Fehlersuche doch sehr erleichtert
  • Den "Address mode", der offensichtlich besonders dann nützlich sein könnte, wenn mehrere Display an einer seriellen Verbindung hängen und jedes davon getrennt angesprochen werden soll (General Rules 19)
  • Und am Ende auch den "Protocol Reparse" - möglicherweise Voraussetzung für Deine Idee, weil Du die gesendeten Bytes auf dem Display selbst dekodieren kannst bzw. sogar musst (General Rules 20).

Es gibt nun wohl zwei Möglichkeiten:

  • eine Library auf der CPU (Arduino, ESP) setzt Deine Binärkommandos in normale Nextion-Instruktionen um. Dafür benötigt sie aber Kenntnis der UI-Konfiguration des Displays
  • Parser auf dem Display selbst

In beiden Fällen fehlen nötige Informationen. Ich persönlich bin bisher auch mit dem normalen Modus der Displayansteuerung, insbesondere wegen der Klartextkommandos, ganz gut gefahren und zufrieden.

Vielleicht habe ich es auch noch nicht ganz verstanden und wesentlicher Zweck sind nur die Rückmeldungen vom Display.

Habt Ihr meinen Text gelesen?

Damit ist das schon mal raus, da man da logischerweise nichts ändern kann.

Was macht die ?

Liest einen vom Display gesendeten little endian - Wert ein, von dem vorher noch ermittelt werden muss von wem er stammt.

Wovon ich rede:

D.h. ich lege fest was und wie gesendet wird gesendet wird wenn etwas gesendet werden soll.
simples praktisches Beispiel, ein Button schaltet eine Pumpe um (an/aus).
Der Button sendet AA B4 F0
case 0xB4:
-> alles was mit Pumpe umschalten zu tun hat
break;

Ich lese also nicht kompliziert irgendwelche Daten aus, sondern sende was genau benötigt wird dorthin wo es benötigt wird (eindeutige Zuordnung durch die "externe ID".
Damit kann man z.B. auf dem Display Daten verknüpfen und senden, die kompletten Einstellungsdaten einer Seite senden usw.

:+1: Genau das habe ich in #1 geschrieben, es geht ausschließlich um die Steuerung des Controllers.

Ich bin noch am basteln der *.hmi zu Demozwecken, dann stelle ich alles ein.

cu

Deswegen hatte ich ja auch geschrieben, dass ich die Länge nach den 3xFF als ungünstig empfinde. Lieber als 1. oder 2. Byte.

Gruß Tommy

Du kannst lesen?

Gruß Tommy

Ja, ich kann lesen und sogar den Inhalt von zwei Sätzen hintereinander erfassen.
Wie so etwas:

Deshalb würde ich aus

auch nicht solchen Unsinn schließen:

Aber wahrscheinlich reichen 10 Klassen POS nicht um in meinem Text die drei 0xFF zu finden.

@wno158
Natürlich geht es nur um die Kommunikation Display -> Mikrokontroller, die andere Richtung wäre ja völlig unsinnig.


Der "grüne Haken" verlässt die Seite und sendet alle relevanten Informationen zur Fenstersteuerung an den Mikrokontroller:

printh C4
prints var_automatik.val,1
if(sch_auto.val==1)
{
  prints var_temp.val,1
}
if(sch_auto.val==0)
{
  prints var_fenster.val,1
}
page start

cu

P.S. Das ist noch die alte Variante ohne Start- und Kontrollbyte und nicht so komplex.

Man kann wie in meinem Beispiel gezeigt vom Microcontroller zum Display Befehle senden die zum Beispiel die Farbe eines Buttons ändern. Im Nextion-Editor gibt es eine Menge Eigenschaften die man durch Senden von Befehlen Microcontroller==>Display anders setzen kann.

void ButtonStartRot() {
  Serial2.print(F("b0.bco=63488"));  // "b0" Bezeichner de Buttons ".bco" Eigenschaft backgroundcolor des Buttons
  Serial2.print(F("\xFF\xFF\xFF"));         // Sends 0xff 0xff 0xff, which tell the Nextion that it has a complete string of data
}

Und für die Richtung Microcontroller ==> Display braucht man das dreifach 0xFF

Ok, ich hatte übersehen, dass es bei Dir nur 1 0xf sein soll. Sorry, mein Fehler.
Kannst Du garantieren, dass das 0xf nicht irgendwo als Inhalt auftaucht?
Dass die Länge nach dem Ende in meinen Augen unsinnig ist, bleibt aber bestehen.

Aber egal, ich werde Deine Lib nicht nutzen.

Gruß Tommy

Wenn Du meinen Text wirklich mal gelesen hättest wüsstest Du das es eben nicht das 0xF allein ist.
Wie hoch schätzt Du die Wahrscheinlichkeit, dass in den Daten ein 0xF mit exakt der Anzahl der bereits empfangenen Bytes codiert in den unteren vier Bit vorkommt?

Klar kann man die Bytezahl an den Anfang setzen, nur weiß der Mikrocontroller dann nicht wann Schluss ist. Wie hoch hättest Du den timeout denn gern?

Sorry, aber lies Dir mal meine Beiträge zum Thema Nextion durch.

Das ist falsch, den dann würde z.B. mein Debugger für das nextion instruction set auf dem Display nicht laufen können.
Nur macht es in Richtung Controller -> Display außer in zeitkritischen Anwendungen keinen Sinn den aktiven protocol reparse mode zu verwenden.

cu

Okay, so langsam fange ich an zu verstehen was Du da treibst (jedenfalls glaube ich das :slight_smile: ).

So auf die Schnelle und ohne Bewertung, nur erster Eindruck:
Du ersetzt einen zugegebenermaßen u.U. aufwendigen Parser auf dem Controller durch einen einfachen, generischen und durch Code auf dem Display für jede erforderliche Rückmeldung.

Muss ich mal in einer ruhigen Stunde genauer drüber nachdenken.

Hmm. Vielleicht sollte "auf Anfang" doch eine Chance bekommen können:
Controller empfängt erstes Byte (oder zweites, wenn 0xAA das Startbyte bleiben soll), maskiert das High Nibble aus und hat dann mit dem Low Nibble (ggf. einmal inkrementiert) gleich einen Zähler für den Rest der Übertragung. Wenn der auf 0 runtergezählt hat, ist fertig.

Naja, Code ist etwas übertrieben.

Anstatt kompliziert zu ermitteln was den auf dem Display gedrückt wurde meldet sich die Komponente mit ihrer ID (wenn sie denn etwas mitzuteilen hat :grin:). Der Mikrocontroller "weiß" also sofort um was es geht und kann die entsprechende Routine anspringen.
Und anstatt anschließend kompliziert mit get page.component.(val,txt) alle Werte zusammen zu suchen werden sie eben gleich mitgeliefert.

Geht prinzipiell, widerspricht aber dem Grundgedanken der Funktionsweise des Displays, welches selbst komplizierte Berechnungen durchführen kann.

Warum sollte man aufwendig Daten vom Display lesen, bearbeiten und wieder zum Display schicken wenn dieses das alles selbst kann und sich selbst meldet wenn erforderlich?

Meine Kalenderdaten z.B. werden vom ESP einfach an das Display durchgereicht, diese stellt dann eigenständig die Termine von z.B. morgen 6:00 Biotonne Leerung auf heute Biotonne rausstellen um, generiert Alarme wenn erforderlich und was sonst noch so anfällt.
Der Timer der das handelt hat über 500 Codezeilen.

Warum so etwas verschwenden, daher werden die Datenbytes über eine eigene Funktion gelesen und auf dem Display kann eine Routine laufen die den Datenstrom generiert, sendet und dabei einfach mitzählt.

Steht die Anzahl vorn muss auf dem Display die Routine durchlaufen werden, alles zwischengespeichert werden (was schon wieder komplizierte Vorgänge erfordert), nachgezählt werden und kann erst dann gesendet werden.

cu

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.