Bit maskieren und abfragen, ich sehe das Muster, aber ?

Hallo,

um die Ausgaben eines Nextiondisplays auszuwerten habe ich ein Abfrageprogramm geschrieben.
In Zeile 69 wird anhand von Bit 6 des Befehlsbytes geprüft, ob Datenbytes anhängig sind.

Obwohl die Ausgabe des Vergleiches in Zeile 67 immer 64 ausgibt (wie es sein sollte) funktioniert die gleiche if-Abfrage nicht.

Die Datenbytes werden nur eingelesen wenn *Bit 0 des Befehlsbytes eins ist (siehe Ausgabe des Terminalprogramms, es wurden 0xC0 bis 0xC5 an das Programm gesendet.

Obwohl ich den Zusammenhang sehe ist mir die Wirkungsweise schleierhaft, was übersehe ich hier?

Die deb-Anweisungen sind lediglich serielle Ausgaben zu debuggen, ich habe sie aber mal drin gelassen, da man so die Ausgaben nachvollziehen kann ....

Programm:

#define DEBUG_ON //Debugger ist aktiviert (Definition muss vor der include-Anweisung stehen!)
#include <MyDebug.h>

byte byte_daten1;
byte byte_daten2;
byte byte_befehl;
byte befehlscode;

byte seite;

byte timer_pumpe_5;
byte timer_pumpe_30;
bool pumpe_on = false;

bool status_automatik_fenster;
byte temp_schwelle;
byte fenster_soll;

int loopschleife = 1;


void setup() {

  Serial.begin(115200);
  Serial.println("Setup");

}


void loop() {

  Serial.println(loopschleife);

  display_einlesen();

  delay(5000);

  loopschleife = loopschleife + 1;

}


void display_einlesen() {

  byte_befehl = 0;
  byte_daten1 = 0;
  byte_daten2 = 0;


  if (Serial.available() > 0) {     // neue Daten vom Display?
    debm("neue Daten");
    byte_befehl = Serial.read();
  }

  else {
    debm("keine neuen Daten");
    return;
  }

  debv("Befehlsbyte ist: ", byte_befehl);

  if (byte_befehl < 128) {            // kein Befefehlsbyte (Bit 7 = 1)
    debm("Fehler Befehlsbyte");
    return;
  }

  debv("Datenbytes? ", byte_befehl & 64);

  if (byte_befehl & 64 == 64) {      // zwei anhängende Datenbytes (Bit 6 = 1)
    debm("Datenbytes einlesen");
    byte_daten1 = Serial.read();    // Datenbytes lesen
    byte_daten2 = Serial.read();
    debv("Byte 1", byte_daten1);
    debv("Byte 2", byte_daten2);
  }

  befehlscode = byte_befehl & 63;    // Befehlscode (Bit 0-6) extrahieren

  debv("Befehlscode ist ", befehlscode);

  switch (befehlscode) {            // Befehle abarbeiten

    case 0:
      debm("Seite 0, keine Daten");
      seite = 0;
      break;

    case 1:
      debm("Seitenwechsel");
      seite = byte_daten1;
      break;

    case 2:
      debm("Rücksprung Pumpensteuerung");
      timer_pumpe_5 = byte_daten1;
      timer_pumpe_30 = byte_daten2;

      // Werte an Pumpensteuerung übergeben
      // Pumpe ausschalten

      seite = 0;
      break;

    case 3:
      debm("Pumpe umstellen");

      if (pumpe_on == true) {
        // Pumpe aus
      }
      // Pumpe ein
      break;

    case 4:
      debm("Rücksprung Fenstersteuerung");

      if (byte_daten1 > 63) {            // Automatikmode (Bit 6 = 1), temp_schwelle
        debm("Automatikmode ein");
        status_automatik_fenster = true;
        temp_schwelle = byte_daten1 & 127;
      }

      debm("Automatikmode aus");
      status_automatik_fenster = false;
      fenster_soll = byte_daten1 & 127;
      seite = 0;
      break;

  }

}

Terminalausgabe, man sieht bei allen ungeraden Befehlsbytes die Daten"lesung" ..

DEBUG: Marker keine neuen Daten

8
DEBUG: Marker keine neuen Daten

9
DEBUG: Marker neue Daten

DEBUG: Befehlsbyte ist: = 192

DEBUG: Datenbytes? = 64

DEBUG: Befehlscode ist = 0

DEBUG: Marker Seite 0, keine Daten

10
DEBUG: Marker neue Daten

DEBUG: Befehlsbyte ist: = 193

DEBUG: Datenbytes? = 64

DEBUG: Marker Datenbytes einlesen

DEBUG: Byte 1 = 255

DEBUG: Byte 2 = 255

DEBUG: Befehlscode ist = 1

DEBUG: Marker Seitenwechsel

11
DEBUG: Marker neue Daten>

DEBUG: Befehlsbyte ist: = 194

DEBUG: Datenbytes? = 64

DEBUG: Befehlscode ist = 2

DEBUG: Marker Rücksprung Pumpensteuerung

12
DEBUG: Marker neue Daten

DEBUG: Befehlsbyte ist: = 195

DEBUG: Datenbytes? = 64

DEBUG: Marker Datenbytes einlesen

DEBUG: Byte 1 = 255

DEBUG: Byte 2 = 255

DEBUG: Befehlscode ist = 3

DEBUG: Marker Pumpe umstellen

13
DEBUG: Marker neue Daten

DEBUG: Befehlsbyte ist: = 196

DEBUG: Datenbytes? = 64

DEBUG: Befehlscode ist = 4

DEBUG: Marker Rücksprung Fenstersteuerung

DEBUG: Marker Automatikmode aus

14
DEBUG: Marker neue Daten

DEBUG: Befehlsbyte ist: = 197

DEBUG: Datenbytes? = 64

DEBUG: Marker Datenbytes einlesen

DEBUG: Byte 1 = 255

DEBUG: Byte 2 = 255

DEBUG: Befehlscode ist = 5

cu

welche?

debv("Datenbytes? ", byte_befehl & 64); //zeigt 64
if (byte_befehl & 64 == 64) // zuerst wird verglichen und dann bitweise UND

Die identische direkt darunter ...

cu

probiere so
if (byte_befehl & 64)

oder

if ((byte_befehl & 64 )== 64)
oder

if ( bitRead(byte_befehl , 6) )

Aber Du hast sehr geholfen, es läuft, danke ...

Wenn jetzt noch einer erklären kann wie der Effekt mit Bit 0 funktioniert?

cu

Ich warne davor, das so zu machen!
Du gehst hier davon aus, dass vorher

mindestens 3 bytes inne hat.
Das geht gewaltig in die Hose, wenn .available() nur 1 zurück gibt.

byte_befehl & 64 gibt != 0 zurück. Damit ist die Bedingung {if (1)} immer erfüllt.

Ist das Gleiche in Grün.

Was möchtest Du jetzt noch?

Nein, Minimum ist ein Byte.
Die Datenbytes werden nur geholt wenn in byte_befehl Bit 6 eins ist.

Das Nextiondisplay sendet lediglich zwei Befehlsfolgen:
ein Befehsbyte (Bit 6 ist 0)
ein Befehlsbyte (Bit 6 ist 1) + zwei Datenbytes

Sollte es also im Empfangsbuffer klemmen läuft im Nextiondisplay etwas falsch, wäre dann auch mein Fehler.

Es gibt noch viel zu lernen ....
Leider ist das im Alter nicht mehr so leicht.

cu

Falsche Baustelle!
Du fragst bei 64 drei Bytes ab!
Du kannst aber nicht garantieren, dass 3 bytes schon empfangen sind!!!

Realistische Annahme:
NextionSend -> 1 Byte
ArduinoRead -> 1 Byte
NextionSend -> 1 Byte
ArduinoRead -> 1 Byte
ArduinoRead -> 1 Byte
NextionSend -> 1 Byte

Glaubst Du nicht?
Doch, das ist realistisch. Bedenke wie lange die Übertragung benötigt.

Ja.

Stimmt, der Prozessor kann den Puffer wesentlich schneller auslesen als das Display nachschieben kann. Wenn die sich dort also "treffen" ... :dizzy_face:
So weit habe ich gar nicht gedacht .... :pensive:

Ich müsste also für jedes Datenbyte einen gefüllten Puffer sicher stellen ..

if (byte_befehl & 64 == 64) {      // zwei anhängende Datenbytes (Bit 6 = 1)

  if (Serial.available() > 0) {         // Datenbyte 1 da ?
    byte_daten1= Serial.read();
  }

  if (Serial.available() > 0) {         // Datenbyte 2 da ?
    byte_daten2= Serial.read();
  }

}

daraus machen.

Korrekt?

cu

Gibt es einen speziellen Grund, warum Du nicht die Standard-Übertragungs-Funktionen des Nextion mit 3 Mal 0xFF als Endekennzeichen benutzt?

Gruß Tommy

Nein.
Es gäbe die Variante auf .available() >=3 zu testen, dass geht aber nicht, da Du mit 63 nur ein byte bekommst.
Also musst Du parsen.



byte data[3] = {0, 0, 0};
byte idx;

void setup()
{
  Serial.begin(115200);
  Serial.println("Setup");
}


void loop()
{
  display_einlesen();
}


void display_einlesen()
{
  if (Serial.available() > 0)       // neue Daten vom Display?
  {
    data[idx] = Serial.read();
    if (data[0] & 64)
    { idx++; }
    if (idx >= 3)
    {
      printData();
      memset(data, 0, sizeof(data));
      idx = 0;
    }
  }
}

void printData()
{
  for (byte b = 0; b < 3; b++)
  {
    Serial.print(data[b]);
    Serial.print(", ");
  }
  Serial.println();
}
if (byte_befehl & 64  {      // zwei anhängende Datenbytes (Bit 6 = 1)
  while (Serial.available() <2) ;      // einige ms nix tun, bis beide Bytes bereit sind
  byte_daten1= Serial.read();   // Datenbyte 1 
  byte_daten2= Serial.read();      // Datenbyte 2  //ob da noch '\n' bzw '\r' zu lesen ist, muss du wissen.
}

genau so. bitRead(ein_byte , 0)

das geht in die Hose, wenn das zweite Byte nicht kommt.

Die ganze angedachte Kommunikation geht gern in die Hose, weil es weder eine Anfangs- noch eine Endekennung gibt.

Gruß Tommy

if (byte_befehl & 64 ) {      // zwei anhängende Datenbytes (Bit 6 = 1)
  uint32_t oldMillis = millis();
  bool isTimeout = false;
  while (Serial.available() < 2) {     // einige ms nix tun, bis beide Bytes bereit sind
    if (millis() - oldMillis > 3) {
      isTimeout = true;
      break;
    }
  }
  if (isTimeout)Serial.print("NoDataBytes\n");
  else{
    byte_daten1 = Serial.read();  // Datenbyte 1
    byte_daten2 = Serial.read();     // Datenbyte 2  //ob da noch '\n' bzw '\r' zu lesen ist, muss du wissen.
  }
}

Sendet man mit den print-Befehlen wird nur der reine Wert gesendet.
Man müsste sich das im Display also selbst generieren.

Ja, ist mir gestern auch noch eingefallen :roll_eyes:, das ist ja eine einfache if-Abfrage und keine Warteschleife.

Das wäre aber doch nach der Abfrage ob Datenbytes anhängig sind möglich?

if ((byte_befehl & 64) == 64) {  
    if (Serial.available() > 1) {

Den Gedanken mit der while-Schleife hatte ich auch ....
In Deinem Programm läuft es ja weiter, auch wenn die Bytes nicht kommen sollten.

Mal sehen wie ich es mache, die Abfrage kann ja problemlos warten bis alle Bytes da sind.
Eine Fehlermeldung im Fehlerfall :rofl: fehlender Datenbytes sollte auch rein ....

Außerdem soll da noch ein Test der Datenbytes auf Bit 7 -> 0 rein und eventuell eine Schleife, um auch mehrere Datenpakete hintereinander abzuarbeiten (obwohl diese "Flut" äußerst unwarscheinlich ist).

cu

Genau.
Und wenn Du mit dem ersten Byte -> bei IDX 0, einen Timer startest, kannst Du auf ein Timeout prüfen.

Aber wie Tommy schon schrub: Es geht definitiv danaeben, wenn Du ein Byte verlierst und dann auf etwas wartest, was eigentlich mehrfach vorkommen könnte.
Daher die Frage: Kannst Du ausschliessen dass 63 und 64 nicht in den Daten vorkommen sondern nur zur Auswahl dienen?

Das widerspricht sich. Wenn Dein Sketch ewig auf das fehlende Byte wartet, kann er keine Fehlermeldung ausgeben. Du wirst halt einen Timeout-Mechanismus einbauen müssen.

Gruß Tommy

Mhhhmm, also 63 und 64 dienen doch m.E. nicht zur Auswahl?

Es ist doch die Kombination von 128 und 64.
Befehlsbyte ist immer 11xx xxxx oder 10xx xxxx. Bit 7 gesetzt ist immer ein Befehlsbyte.
Datenbyte ist immer 0xxx xxxx. Datenbyte Bit 7 ist immer 0.

Mit 63 wird lediglich der reine MCU-Befehlscode von 0 bis xx erzeugt, der dann direkt als case verarbeitet wid.

Vom Display wird auch nie etwas anderes als die Struktur

ein Befehsbyte (Bit 7 ist 1, Bit 6 ist 0)
ein Befehlsbyte (Bit 7 ist 1, Bit 6 ist 1) + zwei Datenbytes

kommen.
Ich lege ja selbst fest, wann das Display was sendet.

Das war etwas missverständlich ausgedrückt.
Ich meine natürlich innehalten für eine kurze Zeitspanne ...
Da die drei Bytes immer als Block gesendet werden dürfte selbst bei 9600 ein 10ms-Timeout mehr als ausreichend sein um diese zu senden und zu verarbeiten.
Nach Timeout alles gut oder Fehler ........

cu

Kleiner Nachtrag, wenn die Poolpumpe komplett aus ist kommen tatsächlich zwei Datenbytes mit 0 rein.
Ich werde die Datenbytes daher als 01xx xxxx kodieren und prüfen.