Arduino hängt sich auf - Probleme mit Serial und I2C?

Hallo

Ich habe seit der Umstellung von seriellen Verbindungen zwischen drei Arduinos auf I2C nun festgestellt, dass sich der Mega der die Daten empfängt und per I2C dann an die anderen Arduino Uno's weiterleitet nach willkürlicher Zeit hängen bleibt. Ich sende dem Mega dann Daten, aber es tut sich nichts mehr. Ich frage mich nun, ob dies hier Probleme mit dem Speicher verursacht?

while(Serial1.available() > 0){                          //while serial is available, make...
    var = Serial1.read();                                //store first character from serial to var
    switch(var){                                        
      case 'A' : DP1 = Serial1.parseInt();               //if var is equal to A, then store second character from serial to DP1
                 break;                                  //finish the command and return
      case 'B' : DP2 = Serial1.parseInt();
                 break;
      case 'C' : DP3 = Serial1.parseInt();
                 break;
      case 'D' : DP4 = Serial1.parseInt();
                 break;
      case 'E' : DP5 = Serial1.parseInt();
                 break;
      case 'F' : DP6 = Serial1.parseInt();
                 break;
      case 'G' : DP7 = Serial1.parseInt();               //if var is equal to G, then store second character from serial to DP7
                 Wire.beginTransmission(2);              //transmit to device #2
                 Wire.write('G');                        //send the character G by I2C
                 Wire.write((uint8_t*)&DP7, sizeof DP7); //send DP7 by I2C
                 Wire.endTransmission();                 //stops transmitting
                 break;
      case 'H' : DP8 = Serial1.parseInt();
                 Wire.beginTransmission(2);
                 Wire.write('H');
                 Wire.write((uint8_t*)&DP8, sizeof DP8);
                 Wire.endTransmission();
                 break;
                 ......
    }
}

Zur Erläuterung, ich sende mit einem Joystick Werte zwischen 0 und 180 und sende diese an einen Arduino Uno der zwei Motoren steuert. Im Prinzip sendet der Joystick ja "permanent" Werte wenn ich ihn immer bewege. Gibt es da Konflikte, da die Daten vom Mega eingelesen werden, über I2C aber nicht gleich schnell weg gehen? Oder gibt es hier generell Konflikte wenn die serielle Schnittstelle mit der Wire Library verwendet wird?

Grüsse

Stef

Nööö.....

Hmmm...

Schule - Diktat
Was passiert, wenn der Lehrer schneller spricht, als die Schüler schreiben können?
Ich kann es dir sagen: Der Ringpuffer zwischen den Ohren läuft über!

Wenn nachfolgend auf mehr als 1 Zeichen reagiert werden soll, dann sollte man vorher auch schon auf die (minimal) erwartete Zeichenzahl warten. Sonst wartet parseInt() bis mehr Zeichen eintrudeln, und so lange ist der Controller blockiert.

Wenn der Joystick permanent Daten schickt, dann würde ich auf 5 oder mehr Zeichen warten, bevor ich die Eingabe parse.

Oder Du implementierst einen nicht-blockierenden Parser.

Es wird immer ein Buchstabe mit einem Zahlenwert gesendet, z.B. A179, dieser wird dann mit dem Character var gelesen was das A liest und im switch/case durchläuft. Dort wird dann der Wert 179 mit parseInt() auf DP1 gespeichert. Der Joystick sendet so für jeden geänderten Wert, sprich für jede Bewegung den neuen Wert und dazugehörigen Buchstaben. Wenn ich dies so parse und wenn nötig sogar noch per I2C weiterleite, dann hat der Speicher ja sicherlich genügend empfangene Daten die gelesen werden sollten/möchten, oder nicht?

Das Problem ist, dass sich der Arduino nur nach einem Reset oder Neustart (stromlos und wieder anschliessen) funktioniert. Wenn man wartet passiert nichts mehr, er hängt und bleibt in diesem Zustand.

Einen nicht blockierenden Parser kann ich versuchen, hast Du da ein Beispiel? Die Frage stellt sich jedoch, da es vorher mit seriellen Verbindungen funktioniert hatte und ich keine Hänger hatte, ob das wirklich was hilft. Ist I2C schneller als eine serielle Verbindung, dass sich der Parser als blockierend erweisen kann?

Gibt es ein Ende-Zeichen nach der Ausgabe von "A179"? Dann kann man mit dem Parsen warten bis dieses Zeichen eintrifft, Dann blockiert parseInt() nichts mehr, da ja der String schon vollständig gelesen wurde. Oder man schickt alle Zahlen mit fester Stellenzahl (3-4), dann kann man auf das Eintreffen der entsprechenden Anzahl von Zeichen warten.

Nein, ein Endzeichen wird hier nicht gesendet. Der Buchstabe wird mit Serial.print() und der Zahlenwert dann per Serial.println() gesendet. Wenn ich hier dann einlese, dann ist das println doch auch wie ein Endzeichen und mit parseInt wird nur bis zur neuen Zeile/Buchstabe eingelesen?

Was ich noch gefunden habe, kann es sein, dass das Wire. endTransmission() hängen bleibt? Scheinbar hatten da andere so ihre Probleme mit. Die WSWire.h hatte abhilfe geschaffen. Das Problem was dort wäre, diese deaktiviert ja die internen Pullups. Ich habe aber meine externen Widerstände so gewählt, dass ich ca. 4.7kOhm mit den internen Pullups habe. Wenn diese jetzt deaktiviert werden, dann habe ich einen zu grossen Widerstand.
Ich schaue morgen mal noch, ob sich mit ändern der Taktrate des I2C Buses was positiv verändert.

Dann ist doch '\n' genau das Ende-Zeichen auf das man warten kann :slight_smile:

Wie kann man dies in dieser Form machen wie mein Sketch aktuell aufgebaut ist? Das Serial.parseInt() nimmt ja einfach den nächsten Wert und dann verlässt es die switch/case Anweisung. Danach geht es zwangsläufig wieder zurück zum Serial.read() was den neuen Character nimmt/liest.

Ich versuche heute mal per serielle Ausgabe den Wire.endTransmission auszulesen und sehen ob dieser hängen bleibt oder was passiert. Da alle Befehle nicht mehr weitergeleitet werden gehe ich mal davon aus, dass sich die Wire Library aufhängt, da ich wie gesagt, vorher per serieller Verbindungen keine Probleme hatte. Zumindest nicht in der Häufigkeit und relativ kurz nach dem Einschalten. Ich gebe mir sonst die empfangenen Werte in der Switch/Case Anweisung vor den Wire.begin() aus. Wenn diese bis zum Stillstand noch durchgekommen sind, dann sollte man den Fehler ja sehen?! Und die endTransmission Werte auch gleich, vielleicht sieht man dann genau wo es hängt.

Momentan fällt mir nur ein:
Alle Zeichen in ein Hilfsfeld lesen bis \n empfangen wurde, dann parsen.

Weiß jemand, wo das Serial Input Basics Tutorial hin verschwunden ist?
Die Doku der Serial Funktionen ist ja noch weniger als bescheiden :frowning:

Meinst Du https://forum.arduino.cc/t/serial-input-basics-updated/382007 das?

1 Like

Ich werde mir dies mal ausgeben und schauen wo es genau hängt:

while(Serial1.available() > 0){                        //while serial is available, make...
    var = Serial1.read();                                //store first character from serial to var
    switch(var){                                        
      case 'A' : DP1 = Serial1.parseInt();               //if var is equal to A, then store second character from serial to DP1
                 Serial.print("DP1 = ");
                 Serial.println(DP1);
                 break;                                  //finish the command and return
      case 'B' : DP2 = Serial1.parseInt();
                 Serial.print("DP2 = ");
                 Serial.println(DP2);
                 break;
      case 'C' : DP3 = Serial1.parseInt();
                 Serial.print("DP3 = ");
                 Serial.println(DP3);
                 break;
      case 'D' : DP4 = Serial1.parseInt();
                 Serial.print("DP4 = ");
                 Serial.println(DP4);
                 break;
      case 'E' : DP5 = Serial1.parseInt();
                 Serial.print("DP5 = ");
                 Serial.println(DP5);
                 break;
      case 'F' : DP6 = Serial1.parseInt();
                 Serial.print("DP6 = ");
                 Serial.println(DP6);
                 break;
      case 'G' : DP7 = Serial1.parseInt();               //if var is equal to G, then store second character from serial to DP7
                 Serial.print("DP7 = ");
                 Serial.println(DP7);
                 Wire.beginTransmission(2);              //transmit to device #2
                 Wire.write('G');                        //send the character G by I2C
                 Wire.write((uint8_t*)&DP7, sizeof DP7); //send DP7 by I2C
                 Wire.endTransmission();                 //stops transmitting
                 Serial.print("Status von endTransmission DP7 = ");
                 Serial.println(Wire.endTransmission());
                 break;
      case 'H' : DP8 = Serial1.parseInt();
                 Serial.print("DP8 = ");
                 Serial.println(DP8);
                 Wire.beginTransmission(2);
                 Wire.write('H');
                 Wire.write((uint8_t*)&DP8, sizeof DP8);
                 Wire.endTransmission();
                 Serial.print("Status von endTransmission DP8 = ");
                 Serial.println(Wire.endTransmission());
                 break;
                 ......

So, ich habe heute mal die serielle Ausgabe angeschaut! Es passiert folgendes:

FDUD = 63
Status von endTransmission FDUD = 0
FDUD = 63
Status von endTransmission FDUD = 0
FDUD = 62
Status von endTransmission FDUD = 0
FDUD = 63
Status von endTransmission FDUD = 0
FDLR = 180
Status von endTransmission FDLR = 0
FDUD = 61
Status von endTransmission FDUD = 0
FDLR = 179
Status von endTransmission FDLR = 0
FDLR = 17
Status von endTrans

Die Übertragung bricht komplett inmitten der Ausgabe ab und der Arduino/die Arduinos hängt/hängen. Also der Mega bleibt sicher hängen, von dem auch die Ausgabe.
Was ich gemerkt habe, dass die seriellen Ausgaben die ich wie im Sketch oben gepostet nun für die Ausgabe am Monitor eingefügt habe, alles verlangsamt und zum Teil etwas falsche Werte ausgibt/übermittelt. Die Motoren drehen zum Teil einfach weiter und stoppen erst nach nochmaligem betätigen des Joysticks. Als würde der Stopwert nicht oder viel zu langsam übermittelt mit all den seriellen Ausgaben am Monitor.

Nun zurück zum Problem. Was könnte ein kompletter Hänger des Megas verursachen?
Ich versuche jetzt noch den Takt des I2C Buses zu erhöhen. Vielleicht hilft dies?

Es macht doch keinen Sinn endTransmission ohne vorhergehendes beginTransmission durchzuführen.
Was soll da geschehen, nach den Gesetzen des Wahnsinns?

Entweder eine Fehlermeldung, oder ein Totalversagen.
Etwas anderes würde ich nicht erwarten!

Also:
Das Programm verhält sich wie erwartet!
(aus meiner Sicht)

Aber Wire.beginTransmission(2); habe ich doch durchgeführt/geschrieben! Im zitierten Teil ganz oben nach links gerutscht steht es doch da??

Die Ausgabe mit ergänztem RAM anzeigen gibt auch keine Fehler aus:

Free RAM: 5192
Free RAM: 5192
Free RAM: 5192
Free RAM: 5192
FDUD = 89
Status von endTransmission FDUD = 0
FDUD = 88
Status von endTransmission FDUD = 0
FDLR = 179
Status von endTransmission FDLR = 0
FDUD = 89
Status von endTransmission FDUD = 0
FDLR = 180
Status von endTransmission FDLR = 0
FDUD = 88
Status von endTransmission FDUD = 0
FDUD = 89
Status von endTransmission FDUD = 0
FDUD = 88
Status von endTransmission FDUD = 0
FDUD = 89
Status von endTransmission FDUD = 0
FDUD = 88
Status von endTransmission FDUD = 0
FDUD = 89
Status von endTransmission FDUD = 0
FDLR = 174
Status von endTransmission FDLR = 0
FDLR = 163
Status von endTransmission FDLR = 0
FDUD = 88
Status von endTransm

Der RAM bleibt bei 5192 bytes die noch frei sind, also mehr als genug!

Ich zähle anders als du, wie mir scheint!
Ein endTransmission hat sein beginTransmission.
Aber das andere steht da ohne Unterhosen im Regen.

Meinst du das endTransmission() im Serial.print? Das ist nur dazu da um die Übertragungsfehler anzuzeigen falls es hat. Oder muss ich dies direkt oben vor das erste endTransmission() setzen? Dann ist doch aber die Übertragung von Wire noch nicht erfolgt?!

Die kommt aber nicht (nur) vom gezeigten Code! Wo ist der Rest?

Ich kann schon den ganzen code posten, habe aber schonmal gesagt bekommen es sei zu viel code... Ich sehe im Moment ja nicht was wichtig ist und wo der Fehler sein könnte. Darum kann ich nur alles posten! Ich werde den Code vom Mega der hägen bleibt mal ohne Kommentare posten. Ich bearbeite die Kommentare kurz raus.

Das Verhalten habe ich aber schon vorher, bevor ich die Serial.print(Wire.endTransmission()); zugefügt habe. Dies habe ich nur getan, um mögliche Fehler bei der Übertragung von I2C zu sehen am seriellen Monitor.
Wenn ich jetzt nur so schreibe, funktioniert dann das senden per I2C da das endTransmission nur noch im print() steht?

```
                 Wire.beginTransmission(2);              //transmit to device #2
                 Wire.write('G');                        //send the character G by I2C
                 Wire.write((uint8_t*)&DP7, sizeof DP7); //send DP7 by I2C
                 Serial.print("Status von endTransmission DP7 = ");
                 Serial.println(Wire.endTransmission());
```