Wie struct einlesen? & serielles Empfangsproblem

Hallo,

wer größere Werte wie Byte übertragen möchte muss vorher ein wenig mehr rechnen ...
Die Datentypen darf man nicht mischen, sonst schlägt der index fehl.

// definieren der Nachricht
union Nachricht
{
    struct
    {
       uint16_t adresse;
       uint16_t funktion;
       uint16_t wert;
       uint16_t yxz;
    };
    uint16_t asArray[4];
} daten;


void setup() {
  Serial.begin(500000);

  daten.adresse = 134;
  daten.funktion = 211;
  daten.wert = 30456;
  daten.yxz = 18;
  byte GdA = (sizeof(Nachricht));                   // Größe des Arrays ermitteln
  byte GeD = (sizeof(daten.asArray[0]));            // Größe eines Datentyps ermitteln
  byte AdE = GdA/GeD;                               // Länge des Arrays ermitteln
  
  Serial.print("GdA "); Serial.println(GdA);        // Größe des Arrays
  Serial.print("GeD "); Serial.println(GeD);        // Größe eines Datentyps
  
  for (byte i=0; i<AdE; i++) {
    Serial.println(daten.asArray[i]);
  }  
  Serial.println();
}

  
void loop() {
  
}

Wenn Du die Structvariable daten auf ein byteArray castest oder mit einem byteArray als union kombinierst, kannst Du im Struct die Datentypen beliebig mischen.
Das funktioniert in einer Kommunikation zwischen Systemen aber nur, solange alle beteiligten Compiler/Prozessorarchitekturen keine Ausrichtung an "Prozessorwortgrenzen" (ein anderer Begriff fällt mir gerade nicht ein) vornehmen und die Speicherungsart gleich sind (z.B. LSB first).

Also Arduino und NodeMCU mit ArduinoIDE funktionieren.

Gruß Tommy

Die Datentypen darf man nicht mischen, sonst schlägt der index fehl.

Natürlich geht das. Du hast es nur falsch verstanden

Das Array ist immer vom Datentyp Byte! Serial übertragt doch immer ein Byte auf einmal. Und es muss genauso groß sein wie die anderen Datentypen zusammen. Also bei für 4 16-Bit Werte sind es 8 Byte.

Hallo,

casten muss ich nichts. Ich habe den Fehler begangen das union daten.asArray Indexweise auszulesen, womit natürlich mein 16Bit Wert falsch war, weil in 2 Byte zerlegt und angezeigt. Einfach nicht mitgedacht. Ich muss wie Serenifly schreibt, immer Byteweise senden und empfangen und dann wie vorgesehen mit daten.yxz auf bestimmte Werte zugreifen.

Ich habe den Fehler begangen das union daten.asArray Indexweise auszulesen

Nein. Dein Fehler war, Nachricht::asArray als uint16_t - Array zu deklarieren :wink:

union Nachricht { struct { uint16_t adresse; uint16_t wert;};
                  byte asArray[];
} daten;

 
void setup() {
  Serial.begin(9600);
  daten.adresse = 0x1234;
  daten.wert = 0xaffe;
  for (byte i = 0; i< sizeof(Nachricht) ; i++) {
    Serial.write(' ');  Serial.print (daten.asArray[i], HEX); 
  }
  Serial.println();
}

void loop() { }

Hallo,

das außerdem, hier kamen mehr Fehler zusammen als mir lieb war. :slight_smile: Vielleicht meinte Tommy deswegen ich soll casten.
Das indexweise auslesen klappt bzw. macht nur Sinn mit byte. Das ist dann das gleiche wie der struct Zugriff daten.adresse usw. Sobald größer Byte im Spiel ist, kann man nur indexweise/Byteweise senden und empfangen. Da kam ich gestern durcheinander.
Jetzt sehe ich wieder klare Bilder.

union Nachricht
{
    struct
    {
       uint8_t adresse;
       uint8_t funktion;
       uint16_t wert;
       uint8_t yxz;
    };
    uint8_t asArray[5];
} daten;

...
...

for (byte i=0; i<AdE; i++) {
    Serial.println(daten.asArray[i]);  // bei "daten.wert" kommt Müll raus, weil in Byte getrennt      
  }                                                
  Serial.println();

Mit dem Casten auf ein byteArray musst Du keine union bauen, sondern kannst das Strukt direkt verwenden. Es macht im Prinzip das Gleiche, nur auf einem anderen Weg.

// damit etwas drin steht
struct
    {
       uint16_t adresse = 0x1234;
       uint16_t funktion = 0x5678;
       uint16_t wert = 0x2468;
       uint16_t yxz = 0xabcd;
    } daten;


char *byteToHex(uint8_t b) {
static char buffer[4];  
  buffer[3] = '\0';
  buffer[0] = ' ';
  buffer[1] = ((b >> 4) < 10) ? (b >> 4) + '0' : (b >> 4) - 10 + 'A';
  buffer[2] = ((b & 0xF) < 10) ? (b & 0xF) + '0' : (b & 0xF) - 10 + 'A';
  return buffer;
}

void showArray(uint8_t *zeiger, byte len) {
  for (uint8_t i=0; i<len; i++) Serial.print(byteToHex(*zeiger++));
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  byte * bytePtr = (byte *)&daten;
  showArray(bytePtr, sizeof(daten)); // Achtung Byteorder
}

void loop() {

}

Hallo,

Danke für die Mühe Tommy, aber leider ungünstiger Zeitpunkt. :wink:

Ich habe erstmal ganz andere Probleme warum ich nochmal stören muss.

Habe obigen Code aus #19 der auf dem Mega lokal wunderbar läuft, nun auf den ATtiny und in den eigentlichen Sketch auf dem Mega übernommen, damit sich ATtiny und Mega unterhalten können. Der Mega sendet, der ATtiny empfängt und sendet es unverändert zum Mega zurück. Allerdings empfängt der Mega mehrfach die '255' Startkennung. Angeblich. Gesendet wird nur einmal.

Dadurch stimmt der index nicht mehr und dadurch schlägt dann auch die Checksummenberechnung fehl, sodass gar nichts funktioniert mit dem Empfang.

Geändert hat sich nur das nun der ATtiny empfängt und sendet und der Mega zyklisch sendet und wartet bis was kommt.
Beim lokalen Test waren Serial 1 und 2 über kreuz verbunden. Serial 1 hat gesendet und Serial 2 hat empfangen.

mit byte c = Serial1.read() wird doch c jedesmal neu erstellt, damit kann doch kein alter Wert erhalten bleiben.
Ich kann debugen wie ich möchte, ich raffe es nicht.

Irgendwelche Ideen wodurch der Effekt entsteht?

Auszug:
0 0 255
1 0 255
1 1 255
1 2 255
1 3 2
1 4 111
1 4 14
1 4 127
1 4 255
1 4 255
1 4 255
1 4 255
1 4 2
1 4 111
1 4 15
1 4 128
1 4 0
Ende
0 0 255
1 0 255
1 1 255
1 2 2
1 3 111
1 4 16
1 4 129
1 4 0
Ende
0 0 255
1 0 255
1 1 255
1 2 2
1 3 111
1 4 17
1 4 130
1 4 0
Ende

Du liest ein, ohne auf available zu prüfen.

Fehler: kein Zeichen da gibt -1 in c, entspicht 255 unsigned

Hallo,

eigentlich mach ich das ...

bool read_Serial_1()
{
  static byte index = 0;
  static bool state = false;
    
  if (Serial1.available() > 0)  {
    byte c = Serial1.read();
    Serial.print(state); Serial.print('\t');                      // Debug
    Serial.print(index); Serial.print('\t'); Serial.println(c);   // Debug
    
    if (c == ENT && index > (sizeof(Nachricht)-1) ) {     // Ende-Kennung 0 ?
      Serial.println("Ende");
      state = false;
    }
    if (state == true && (index < sizeof(Nachricht))) {
      empfDaten.asArray[index++] = c;
    }
    if (c == SNT && state == false) {                     // Start-Kennung 255 ?
      state = true;
    }
    if (state == false) {                                 // Übertragungsende
      //Serial.print("emfg_chk "); Serial.println(calc_Checksumme(empfDaten.asArray, sizeof(Nachricht)));
      index = 0;
      // Checksumme korrekt ?
      if ( empfDaten.checksumme == calc_Checksumme(empfDaten.asArray, sizeof(Nachricht)) ) {
        return true;
      }
    }
  }  
  return false;
}


void read_Serial_1_to_Serial_0 ()
{
  if ( read_Serial_1() == true)  {                // wird nie ausgeführt
    Serial.println(F("Serial1 hat empfangen:"));  
    Serial.println(empfDaten.adresse);
    Serial.println(empfDaten.funktion);
    Serial.println(empfDaten.wert);
    Serial.println(empfDaten.checksumme);
  }  
}

Dann war meine Theorie wohl nix.

Hallo,

leider. Wie gesagt, irgendwoher kommt eine "255" im seriellen Puffer, die nicht von mir stammt. Womit daraufhin Serial1.available gültig wird und das Desaster seinen Lauf nimmt. Ändere ich meine Startkennung von 255 auf 244, dann funktioniert es zu 99%. Manchmal funktioniert die jedoch Endekennung nicht, weil wieder irgendwoher eine "255" einstreut.

Mache ich byte c statisch, sehe ich die falsche 255 in einer Debugausgabe.
Stimmt irgendwas mit dem seriellen Buffer nicht? Bug?

bool read_Serial_1()
{
  static byte index = 0;
  static bool state = false;
  static byte c = 0; 
    
  if (Serial1.available() > 0)  {
    c = Serial1.read();
    Serial.print(state); Serial.print('\t');                      // Debug
    Serial.print(index); Serial.print('\t');                      // Debug
    Serial.println(c);                                            // Debug
    
    if (c == ENT && index > (sizeof(Nachricht)-1) ) {     // Ende-Kennung ?
      Serial.println("Ende");                                     // Debug
      state = false;
    }
    if (state == true && (index < sizeof(Nachricht))) {
      empfDaten.asArray[index++] = c;
    }
    if (c == SNT && state == false) {                     // Start-Kennung ?
      state = true;
    }
    if (state == false) {                                 // Übertragungsende
      index = 0;
      // Checksumme korrekt ?
      if ( empfDaten.checksumme == calc_Checksumme(empfDaten.asArray, sizeof(Nachricht)) ) {
        return true;
      }
    }
  }  
  return false;
}


void read_Serial_1_to_Serial_0 ()
{
  if ( read_Serial_1() == true)  {                // wird nie ausgeführt
    Serial.println(F("Serial1 hat empfangen:"));  
    Serial.println(empfDaten.adresse);
    Serial.println(empfDaten.funktion);
    Serial.println(empfDaten.wert);
    Serial.println(empfDaten.checksumme);
  }  
}

Monitor Auszug

// bei 3 Spalten: state | index | c
Serial1 hat empfangen:
2
111
16
129
0 0 244
1 0 2
1 1 111
1 2 17
1 3 130
1 4 0
Ende
Serial1 hat empfangen:
2
111
17
130
0 0 255
Serial1 hat empfangen:
2
111
17
130
0 0 255
Serial1 hat empfangen:
2
111
17
130
0 0 244
1 0 2
1 1 111
1 2 18
1 3 131
1 4 0
Ende
Serial1 hat empfangen:
2
111
18
131
0 0 255

Hallo,

ändere ich das Ganze so ab, dass der ATtiny nur zyklisch sendet und der Mega nur empfängt, dann kann ich keinen Fehler erkennen. Demnach ist was faul wenn der Mega sendet. Nur was? Auf dem Datalogger sehe ich auch keine Fehler.

void send_Nachricht ()
{       
      sendDaten.checksumme = calc_Checksumme(sendDaten.asArray, sizeof(Nachricht)) ;        
      Serial1.write(SNT);                           // Startkennung
      for (byte i=0; i < sizeof(Nachricht); i++) {
        Serial1.write(sendDaten.asArray[i]);        // Bytes rausschieben
      }
      Serial1.write(ENT);                           // Endkennung
      Serial1.flush();                              // warten bis alles raus ist
      state_UART1 = WAIT;
}

alles wie #19.

Wie schon gesagt wurde: Die 255 kann eine verstümmelte -1 sein.
Das solltest du nicht ignorieren.
Zumindest dann nicht, wenn dir die 255 Probleme bereiten.

Wenn du wissen willst, ob dir Serial.read() einen Fehler melden will, dann solltest du das Gelieferte nicht in ein char, oder byte, packen, sondern in einen int.
Dann kannst du das int Dingen ganz entspannt auf -1 überprüfen.
Wenn != -1 dann ist ein gültiger Wert eingetroffen.

Kann evtl. auch eine elektrische Störung sein, die fälschlich als Startbit interpretiert wird.
Das hatte ich mal bei AltSoftserial, wo ein winzigkurzer Einbruch einen Interrupt auslöst der gnadenlos als Startbit interpretiert wird. (War damals ein Fall für den berühmten 100nF in der Versorgungsleitung)

Ob bei HardwareSerial auch egal ist, wie das Startbit aussieht (d.h. ob es mindestens eine halbe Bitlänge lang anstehen muss), weiss ich nicht.

Das LA-Bild in deinem Post <2017-09-15 19:42:01> sieht doch super aus, oder?

Hallo,

ich werde das nochmal näher untersuchen. Auch mit int statt byte.

Auch wenn ich mich wiederhole. Der Datalogger sieht immer astrein aus, dass ist ja das verrückte. Mit 24MS/s sollte die Abtastrate für 125kHz (250kBaud) locker reichen um Peaks zu sehen, auch wenn das nicht analog gemessen wird. Ich habe ja nur den Code vom "lokalen Testcode" auf den ATtiny übernommen und in meinen anderen eigentlichen Sketch. Auf dem ATtiny nutze ich die uart Lib von Peter Fleury. Der ATtiny macht mit dem Code keine Probleme, sonst könnte er nicht gnadenlos immer richtig senden. Von Störungen gehe ich erstmal nicht aus, ich verwende eine RS485 Übertragung mit MAX487. Mein Oszi werde ich nochmal anschließen. Vor dem Umbau auf das union-struct hatte ich immer Strings hin und her geschoben. Das klappte mit der gleichen Hardware einwandfrei.

Da ich jetzt binär übertrage, keine Strings, muss ich auch nirgendswo ein Null-Terminator setzen. Richtig? Der Lib interne serielle Ringbuffer seitens Arduino macht das ja selbst mit Head und Tail. Genauso wie das die uart Lib auf dem ATtiny macht.

Falls das noch wichtig wäre für jemanden. Den ATtiny programmiere ich klassisch C++ in Atmel Studio und flashe über ISP. Den Mega wie immer mit der Arduino IDE.

Danke für die Anteilnahme. Ich werde berichten. Jede Idee ist willkommen.

Der Null-Terminator ist dafür das Ende eines Strings zu markieren. Wenn du Binär überträgst hast du die Länge anderswo her.
Entweder ergibt sich die Länge direkt aus den Datenstrukturen wenn sie auf beiden Seiten identisch sind und man immer das Gleiche überträgt. Oder man hat Telegramme variabler Länge (z.B. ein Array) und überträgt die Länge mit. Terminiert werden muss da so oder so nichts.

Nimm doch mal spaßeshalber einen anderen Arduino mit Hardware seriell anstatt dem Attiny

Hallo,

@ Spanier, wäre eine Idee für morgen.

@ Serenifly, wegen '\0', dann bin ich doch nicht so blöd. Danke für Rückmeldung.
Man zweifelt bei der Fehlersuche an den banalsten Dingen. :slight_smile:

Jetzt möchte ich euch kurz an der Fehlersuche teilhaben lassen. Mit dem Oszi sieht man Dinge, die einem mit dem Datalogger verborgen bleiben.
Das Highsignal auf der Mega-Empfangsleitung wird langsam runtergezogen. In dieser fallenden Kurve sieht man das Sendesignal eingekoppelt.
Ich finde das nicht in Ordnung. Frage. Was ist hier los? Hardwarecheck ...
Das 15cm Doppelkabel zwischen Steckbrett und Mega komplett aufgetrennt und verdrillt wieder gesteckt. Ohne Erfolg.
Vermutung, Hardware zerstört.
Auf dem Mega die Serial1 gegen Serial2 getauscht. Ohne Erfolg.
Wieder zurück geändert.
Mein verdrilltes RS485 Verbindungkabel ist ca. 1m lang.
An den seriellen Uart's vom ATtiny und Mega ist je ein MAX487 IC dran.
Der 130 Ohm Abschlusswiderstand zwischen A-B ist auf beiden Seiten vorhanden.
Der Pullup an A und Pulldown an B ist nur auf der ATtiny Platine vorhanden.
Dachte mir bis jetzt, Mega seitig nicht so wichtig, weil Leitung so kurz ist.

Habe den Pullup/Pulldown Mega seitig nachgerüstet.
Fallende Kurve ist weg. Nur noch im "Grundrauschen" ist das Gezappel zu sehen.
Nicht perfekt, aber wesentlich besser. Seltsamerweise sieht man das Sendesignal nicht im Empfangssignal. Jedoch sieht man das vor der Startkennung 244 ein zusätlich Byte gesendet wird. Und das Sendesignal hat die Polarität geändert. Warum ist mir immer noch schleierhaft.

Die Fragezeichen in der grünen Decodierzeile erscheinen wenn der Dekoder meint er hätte keinen Platz zur Darstellung.
Im später eingeblendeten Fenster sieht man das er alles dekodieren kann.
Das was ich auf dem Oszi sehe, spiegelt sich im seriellen Monitor 1:1 wieder.

Jetzt weiß ich nicht was passiert ist bzw. was ich verstellt habe, ich bekomme auf dem Oszi kein Signal mehr angezeigt. Ab hier bin ich erstmal blind für heute.

Dann habe ich ATtiny seitig im Code vor der uart Initialisierung ein löschen des Ringbuffers eingefügt. Da ich diesen Ringbuffer vorm uart init für was anderes verwende. Sollte keine Rolle spielen, da Head und Tail beim init beide gleich auf 0 gesetzt werden. War natürlich ohne Erfolg. Hatten mir schon combie und Wandhall vor längerer Zeit gesagt das es überflüssig wäre.

Dann habe ich Mega seitig den Pullup/Pulldown von A/B wieder entfernt und bekomme gültige Werte "erstmal" übermittelt.
Es wird jedoch trotz
int c = Serial1.read()
im Fehlerfall immer eine 0 als eingelesen angezeigt. Was nicht sein kann, weil vorher mit
if (Serial1.available() > 0)
überprüft wird, dass heißt die 0 dürfte gar nicht gültig werden.
Wenn ich morgen wieder mit dem Oszi etwas sehe, dann weiß ich mehr ... hoffentlich.

bool read_Serial_1()
{
  static byte index = 0;
  static bool state_Read = false;
  static bool state_Complete = false;
  //static int c = 0; 
    
  if (Serial1.available() > 0)  {
    int c = Serial1.read();
    Serial.print("read"); Serial.print('\t');                 // Debug
    Serial.print(state_Read); Serial.print('\t');             // Debug
    Serial.print(state_Complete); Serial.print('\t');         // Debug
    Serial.print(index); Serial.print('\t');                  // Debug
    Serial.println(c);                                        // Debug

    c  = (byte) c;    // ggf. casten
    if (c == ENT && index > (sizeof(Nachricht)-1) ) {         // Ende-Kennung ?
      Serial.println("read Ende");
      state_Read = false;
      state_Complete = true;
    }
    
    if (state_Read == true && (index < sizeof(Nachricht))) {
      empfDaten.asArray[index++] = c;
    }
    
    if (c == SNT && state_Read == false) {                     // Start-Kennung ?
      state_Read = true;
    }
    
    if (state_Complete == true) {                              // Übertragung fertig ?
      index = 0;
      // Checksumme korrekt ?
      if ( empfDaten.checksumme == calc_Checksumme(empfDaten.asArray, sizeof(Nachricht)) ) {
        state_Complete = false;
        return true;
      }
    }
  }  
  return false;
}


void read_Serial_1_to_Serial_0 ()
{
  if ( read_Serial_1() == true)  {                // wird nie ausgeführt
    Serial.println(F("Serial.1 hat empfangen:"));  
    Serial.print(empfDaten.adresse); Serial.print('\t');
    Serial.print(empfDaten.funktion); Serial.print('\t');
    Serial.print(empfDaten.wert); Serial.print('\t');
    Serial.println(empfDaten.checksumme);
    Serial.println("komplett");
    Serial.println();
  }  
}

Nur warum jetzt die falsch eingelese 0 (wo immer die auch herkommt) als > 0 gültig durch kommt ist mir dann doch für heute zu viel des Guten.

read    0    0    0    0
read    0    0    0    244
read    1    0    0    78
read    1    0    1    179
read    1    0    2    61
read    1    0    3    62
read    1    0    4    0
read Ende
Serial.1 hat empfangen:
78    179    61    62
komplett

read    0    0    0    0
read    0    0    0    244
read    1    0    0    78
read    1    0    1    179
read    1    0    2    62
read    1    0    3    63
read    1    0    4    0
read Ende
Serial.1 hat empfangen:
78    179    62    63
komplett

read    0    0    0    0
read    0    0    0    244
read    1    0    0    78
read    1    0    1    179
read    1    0    2    63
read    1    0    3    64
read    1    0    4    0
read Ende
Serial.1 hat empfangen:
78    179    63    64
komplett

read    0    0    0    0
read    0    0    0    244
read    1    0    0    78
read    1    0    1    179
read    1    0    2    64
read    1    0    3    65
read    1    0    4    0
read Ende
Serial.1 hat empfangen:
78    179    64    65
komplett

Hallo,

ich bin ja schon völlig bekloppt geworden. Serial1.available() gibt "ja nur" die Anzahl der neu im Ringbuffer stehenden Zeichen zurück.