Forum: Mikrocontroller und Digitale Elektronik MQTT packet fragmentation


von Hammad S. (msaf)


Lesenswert?

Hallo zusammen,

Mein Code soll einen ADXL345-FIFO(32 Stufen) durch MQTT von meinem 
ESP32-Board an den PC senden.

Um die gesendeten Pakete zu überwachen, verwende ich Wireshark. Wenn ich 
Zeile 127 bzw. 128 kommentiere , wird mein Paket fragmentiert und ich 
sehe Pakete von 1490 Byte und 235 Byte, obwohl meine tatsächliche 
Paketgröße (msg) 860 Byte beträgt. Außerdem zeigt wire shark nur die 
publish messages und benachrichtigt dass die connect command fehlt(aber 
ich sehe die Sensor Daten).

Wenn ich Zeile 127 "client.disconnect();" bzw. 128 "delay(1000);" in dem 
Code nehme, ist alles in Ordnung und in Wireshark sehe ich die 860 Bytes 
so, wie es sein soll. Außerdem kann ich dann ein Connect command und 
alle ACK und ein Disconnect command sehen. Die 2 Lösungen mit disconnect 
und das Delay will ich nicht machen damit die Verbindung sich nicht 
verlangsamt.

Kann mir bitte jemand sagen, warum ich 1490 Bytes sehe, obwohl ich nur 
860 Bytes sende?

Hier ist mein Code:
1
#include <WiFi.h>
2
#include <PubSubClient.h>
3
#include<ADXL345_WE.h>
4
#include<SPI.h>
5
6
#define CS_PIN 5   // Chip Select Pin
7
bool spi = true;    // flag that SPI shall be used
8
const int int2Pin = 4;
9
volatile bool event = false;
10
11
String FifoData;
12
String myStrings[32];
13
String data;
14
xyzFloat g;
15
16
const char* ssid = "NETGEAR61";
17
const char* password = "classyflute500";
18
const char* mqtt_server = "192.168.1.4";
19
20
21
WiFiClient espClient;
22
PubSubClient client(espClient);
23
char msg[1000];
24
25
ADXL345_WE myAcc = ADXL345_WE(CS_PIN, spi);
26
27
void eventISR() {
28
  event = true;
29
}
30
31
void setup() {
32
  delay (500);
33
  Serial.begin(115200);
34
  pinMode(int2Pin, INPUT_PULLDOWN);
35
36
  if (!myAcc.init()) {
37
    Serial.println("ADXL345 not connected!");
38
  }
39
40
41
  myAcc.setDataRate(ADXL345_DATA_RATE_3200);
42
  delay(100);
43
  Serial.print("Data rate: ");
44
  Serial.print(myAcc.getDataRateAsString());
45
  myAcc.setRange(ADXL345_RANGE_8G);
46
  Serial.print("  /  g-Range: ");
47
  Serial.println(myAcc.getRangeAsString());
48
  Serial.println();
49
50
  setup_wifi();
51
  client.setServer(mqtt_server, 1883);
52
  myAcc.setMeasureMode(false);
53
  attachInterrupt(digitalPinToInterrupt(int2Pin), eventISR, RISING);
54
  myAcc.setInterruptPolarity(ADXL345_ACT_HIGH);
55
  myAcc.setInterrupt(ADXL345_WATERMARK, INT_PIN_2); // Interrupt when FIFO is full
56
  myAcc.setFifoParameters(ADXL345_TRIGGER_INT_1, 32);
57
  myAcc.setFifoMode(ADXL345_FIFO);
58
}
59
60
void setup_wifi() {
61
  delay(10);
62
  // We start by connecting to a WiFi network
63
  Serial.println();
64
  Serial.print("Connecting to ");
65
  Serial.println(ssid);
66
67
  WiFi.begin(ssid, password);
68
69
  while (WiFi.status() != WL_CONNECTED) {
70
    delay(500);
71
    Serial.print(".");
72
  }
73
74
  Serial.println("");
75
  Serial.println("WiFi connected");
76
  Serial.println("IP address: ");
77
  Serial.println(WiFi.localIP());
78
}
79
80
void reconnect() {
81
  while (!client.connected()) {
82
    Serial.print("Attempting MQTT connection...");
83
    if (client.connect(mqtt_server)) {
84
      Serial.println("connected");
85
      client.subscribe("esp32/output");
86
    } else {
87
      Serial.print("failed, rc=");
88
      Serial.print(client.state());
89
      Serial.println(" try again in 5 seconds");
90
      delay(5000);
91
    }
92
  }
93
}
94
95
void loop() {
96
97
  if (!client.connected()) {
98
    reconnect();
99
  }
100
  client.loop();
101
  long rssi = WiFi.RSSI();
102
  event = false;
103
104
  myAcc.readAndClearInterrupts();
105
  myAcc.setMeasureMode(true); // this is the actual start
106
  while (!event) {} // wait until the fifo get full //event = true means WATERMARK interrupt triggered -> means FIFO is full
107
  myAcc.setMeasureMode(false); // stop measuring
108
  //Serial.println("FiFo full");
109
110
  for (int i = 0; i < 32; i++) { // when i > 32 samples, the values do not change because the FIFO is full
111
    g = myAcc.getGValues();
112
    myStrings[i] = "x: " + String(g.x) + " y: " + String(g.y) + " z: " + String(g.z) + "\n";
113
    data = data + myStrings[i];
114
115
  }
116
  data =  " RSSI: " + String(rssi) + "dB" + data;
117
118
  Serial.println(data.length());
119
120
  data.toCharArray(msg , data.length() + 1);
121
122
  client.publish("test1", msg);
123
  //client.disconnect();
124
  //delay(1000);
125
126
  //emptying the strings for the next FIFO set.
127
  data = "";
128
  for ( int i = 0; i < MQTT_Size;  ++i ) {
129
    msg[i] = (char)0;
130
  }
131
132
}

von Einer (Gast)


Lesenswert?

Hammad S. schrieb:
> Hier ist mein Code:

Wer soll diesen Kram lesen?


> Kann mir bitte jemand sagen, warum ich 1490 Bytes sehe,

Du benutzt nicht ernsthaft Wireshark, siehst ein Paket mit 1490 Bytes, 
und schaust nicht hinein, wie es aufgebaut ist?

Ernsthaft?

von Hammad S. (msaf)


Lesenswert?

Einer schrieb:
> Hammad S. schrieb:
>> Hier ist mein Code:
>
> Wer soll diesen Kram lesen?
 der helfen will vielleicht :D?
>
>> Kann mir bitte jemand sagen, warum ich 1490 Bytes sehe,
>
> Du benutzt nicht ernsthaft Wireshark, siehst ein Paket mit 1490 Bytes,
> und schaust nicht hinein, wie es aufgebaut ist?
>
> Ernsthaft?

Ja ernsthaft. Was denkst du dir? Wieso gibt es Forums auf der Welt?
Man stellt nur eine Frage wenn man die Antwort davon nicht weiß. Also 
entweder seist du nett und du antwortest die Frage wenn du die Antwort 
schon kannst oder du reagierst gar nicht darauf :)

von Hmmm (Gast)


Lesenswert?

Hammad S. schrieb:
> Ja ernsthaft. Was denkst du dir? Wieso gibt es Forums auf der Welt?
> Man stellt nur eine Frage wenn man die Antwort davon nicht weiß.

Du guckst also mit Wireshark, siehst dort etwas, was Deiner Meinung nach 
seltsam ist - und stellst dann Deine Frage, ohne uns überhaupt zu 
zeigen, worum es genau geht.

Lade doch mal PCAP-Files hoch.

von Hammad S. (msaf)


Angehängte Dateien:

Lesenswert?

Hmmm schrieb:
> Hammad S. schrieb:
>> Ja ernsthaft. Was denkst du dir? Wieso gibt es Forums auf der Welt?
>> Man stellt nur eine Frage wenn man die Antwort davon nicht weiß.
>
> Du guckst also mit Wireshark, siehst dort etwas, was Deiner Meinung nach
> seltsam ist - und stellst dann Deine Frage, ohne uns überhaupt zu
> zeigen, worum es genau geht.
>
> Lade doch mal PCAP-Files hoch.
habe die hochgeladen

von Hmmm (Gast)


Angehängte Dateien:

Lesenswert?

In SPI_MQTT_ohne_disconnect.pcapng sieht man es gleich beim ersten 
Paket:

Da kommen erst Dein MQTT-Publish, dahinter folgt aber plötzlich noch ein 
zweiter Datensatz. Siehe beigefügter Screenshot, der markierte Bereich 
ist die eigentliche Publish-Message, der Rest gehört dort nicht hin.

Hammad S. schrieb:
> Wenn ich
> Zeile 127 bzw. 128 kommentiere , wird mein Paket fragmentiert

In Zeile 127 wird der Buffer geleert. Wenn das nicht passiert, ist die 
nächste Nachricht halt grösser und wird deshalb fragmentiert.

von Hammad S. (msaf)


Lesenswert?

Hmmm schrieb:
> In SPI_MQTT_ohne_disconnect.pcapng sieht man es gleich beim ersten
> Paket:
>
> Da kommen erst Dein MQTT-Publish, dahinter folgt aber plötzlich noch ein
> zweiter Datensatz. Siehe beigefügter Screenshot, der markierte Bereich
> ist die eigentliche Publish-Message, der Rest gehört dort nicht hin.
ja das ist genau mein Problem
> Hammad S. schrieb:
>> Wenn ich
>> Zeile 127 bzw. 128 kommentiere , wird mein Paket fragmentiert
>
> In Zeile 127 wird der Buffer geleert. Wenn das nicht passiert, ist die
> nächste Nachricht halt grösser und wird deshalb fragmentiert.
damit das nicht auftritt leere ich den String(  data=""; ) dierekt nach 
dem publish aber anscheinend wird es komischer Weise nicht geleert.

: Bearbeitet durch User
von Michael U. (amiga)


Lesenswert?

Hallo,

wenn ich nicht irre war 1490Byte die Puffergröße des TCP-Stack beim 
ESP8266/ESP32.
Wenn weniger Daten gesendet werden schickt er das Paket nach einem 
internen Timeout raus, wenn es mehr ist fragmentiert er auf die 
Puffergöße.
Allerdings habe ich das bisher nur mal nachgeschaut weil das Holen eines 
jpg aus dem SPIFFS mal schnell ging und mal "ewig" dauerte. Ich habe 
damals die Schleife etwas angepasst damit sich die Zugriffe nicht 
gegenseitig ausgebremst haben. Bei MQTT hatte ich noch keinen Grund, 
darüber nachzudenken.

Gruß aus Berlin
Michael

von Hmmm (Gast)


Lesenswert?

Hammad S. schrieb:
> Wenn ich Zeile 127 "client.disconnect();" bzw. 128 "delay(1000);" in dem
> Code nehme, ist alles in Ordnung

Du verstehst einen grundlegenden Punkt von TCP falsch: Im Gegensatz zu 
UDP hast Du keinen Einfluss darauf, wie der TCP-Stack die gesendeten 
Daten in Pakete verpackt.

Wenn Du sie schnell hintereinander sendest, kann es also durchaus 
passieren, dass sie zu einem Paket zusammengefasst werden - im Idealfall 
so, dass die Path MTU ausgereizt wird und somit der geringste Overhead 
entsteht.

Wenn Du die Verbindung zwischendurch trennst oder einen Moment wartest, 
hebelst Du dieses Verhalten aus.

von Forist (Gast)


Lesenswert?

Hammad S. schrieb:
> Hier ist mein Code:
> ...

Was verstehst du an "Längeren Sourcecode nicht im Text einfügen, sondern 
als Dateianhang" nicht?

von Stefan F. (Gast)


Lesenswert?

Hmmm schrieb:
> Du verstehst einen grundlegenden Punkt von TCP falsch: Im Gegensatz zu
> UDP hast Du keinen Einfluss darauf, wie der TCP-Stack die gesendeten
> Daten in Pakete verpackt.

Dazu kommt, dass jede Netzwerk-Komponente (z.B. Router) die Stückelung 
des TCP Datenstroms ändern darf.

Es gibt immer wieder Produkte, die wegen Missachtung dieses Fakts 
Probleme machen. Das war schon vor 30 Jahren so, und heute immer noch.

von Hammad S. (msaf)


Lesenswert?

Michael U. schrieb:
> Hallo,
>
> wenn ich nicht irre war 1490Byte die Puffergröße des TCP-Stack beim
> ESP8266/ESP32.
ja genau
> Wenn weniger Daten gesendet werden schickt er das Paket nach einem
> internen Timeout raus, wenn es mehr ist fragmentiert er auf die
> Puffergöße.
> Allerdings habe ich das bisher nur mal nachgeschaut weil das Holen eines
> jpg aus dem SPIFFS mal schnell ging und mal "ewig" dauerte. Ich habe
> damals die Schleife etwas angepasst damit sich die Zugriffe nicht
> gegenseitig ausgebremst haben.
wie hast du genau die Schleife angepasst?

Bei MQTT hatte ich noch keinen Grund,
> darüber nachzudenken.
>
> Gruß aus Berlin
> Michael

von Hammad S. (msaf)


Lesenswert?

Hmmm schrieb:
> Hammad S. schrieb:
>> Wenn ich Zeile 127 "client.disconnect();" bzw. 128 "delay(1000);" in dem
>> Code nehme, ist alles in Ordnung
>
> Du verstehst einen grundlegenden Punkt von TCP falsch: Im Gegensatz zu
> UDP hast Du keinen Einfluss darauf, wie der TCP-Stack die gesendeten
> Daten in Pakete verpackt.

Vielen Dank für die Erklärung. Ich habe darauf geachtet, dass meine 
Pakete wegen die MTUs die 1500 Bytes nicht überschritten werden.
>
> Wenn Du sie schnell hintereinander sendest, kann es also durchaus
> passieren, dass sie zu einem Paket zusammengefasst werden - im Idealfall
> so, dass die Path MTU ausgereizt wird und somit der geringste Overhead
> entsteht.

Soweit ich weiß ist tcp/ip zuständig die Pakete zu fragmentieren und 
danach wiederherzustellen. Also die Daten werden ankommen aber langsamer 
halt. Verstehe ich das richtig?
>
> Wenn Du die Verbindung zwischendurch trennst oder einen Moment wartest,
> hebelst Du dieses Verhalten aus.
kann man ausrechnen wie viel man mindestens warten muss, damit das ganze 
ohne Fragmentation funktioniert?

von Hammad S. (msaf)


Lesenswert?

Stefan ⛄ F. schrieb:
> Hmmm schrieb:
>> Du verstehst einen grundlegenden Punkt von TCP falsch: Im Gegensatz zu
>> UDP hast Du keinen Einfluss darauf, wie der TCP-Stack die gesendeten
>> Daten in Pakete verpackt.
>
> Dazu kommt, dass jede Netzwerk-Komponente (z.B. Router) die Stückelung
> des TCP Datenstroms ändern darf.
ja genau und darauf habe ich geachtet, dass mein MTU von dem Router, 
ESP-Board auf 1500 Byte sind und dass meine Paketgröße nicht 900 Byte 
überschreitet.
>
> Es gibt immer wieder Produkte, die wegen Missachtung dieses Fakts
> Probleme machen. Das war schon vor 30 Jahren so, und heute immer noch.

von Stefan F. (Gast)


Lesenswert?

Hammad S. schrieb:
> Soweit ich weiß ist tcp/ip zuständig die Pakete zu fragmentieren und
> danach wiederherzustellen.

Nein, niemand garantiert dir, dass die Pakete bei TCP in der 
ursprünglichen Stückelung beim Empfänger ankommen. Denn TCP ist in 
Gegensatz zu UDP nicht Paket orientiert.

TCP stellt nur sicher, dass dein Datenstrom korrekt fließt. Dass heißt: 
Die Bytes kommen in der richtigen (ursprünglichen) Reihenfolge beim 
Empfänger an und werden durch Störsignale nicht verfälscht. Entweder 
fließt der Strom, oder er wird vom Netzwerk Treiber unterbrochen. 
Falsche Daten gibt es bei TCP nicht.

Aber auf due Stückelung der Daten in Form von Paketen ist keinerlei 
Verlass. Wenn du da brauchst, musst du UDP verwenden. UDP garantiert 
aber nicht, dass die Pakete in der richtigen Reihenfolge ankommen. Es 
dürfen auch welche verloren gehen.

Lies dazu dies: 
https://serverfault.com/questions/534063/can-tcp-and-udp-packets-be-split-into-pieces

von Gustl B. (-gb-)


Lesenswert?

Er sendet lange Strings ... welch Verschwendung! Die paar Register vom 
ADXL345 passen in sehr viel kleinere Nachrichten.

von Sebastian W. (wangnick)


Lesenswert?

Hammad S. schrieb:
>> Da kommen erst Dein MQTT-Publish, dahinter folgt aber plötzlich noch ein
>> zweiter Datensatz. Siehe beigefügter Screenshot, der markierte Bereich
>> ist die eigentliche Publish-Message, der Rest gehört dort nicht hin.
> ja das ist genau mein Problem

Das Problem ist die PubSubClient-Bibliothek. "It can only publish QoS 0 
messages.", das heisst es wird nach einem PUBLISH nie auf einen PUBACK 
gewartet. Tatsächlich wird nach einem PUBLISH noch nicht einmal ein 
flush() ausgeführt.

Versuch das einmal selbst. Ruf nach client.publish() mal 
espClient.flush() auf. Vielleicht zwingt das das TCP-Paket raus, und 
verhindert das sich der nächste PUBLISH hinten ans selbe Paket noch dran 
klebt.

LG, Sebastian

von Hmmm (Gast)


Lesenswert?

Hammad S. schrieb:
> Ich habe darauf geachtet, dass meine
> Pakete wegen die MTUs die 1500 Bytes nicht überschritten werden.

Das ist bei TCP unnötig, darum kümmert sich komplett der TCP/IP-Stack. 
Du kannst daher beliebig Daten raussenden, und am anderen Ende kommen 
sie in derselben Reihenfolge (aber ggf. in anderen Paketgrössen) wieder 
raus.

Beachten musst Du dabei im wesentlichen zwei Dinge:

1. Der TX-Buffer ist nicht unendlich gross, es kann also passieren, dass 
mal keine Daten (oder nur ein Teil davon) rausgesendet werden können. 
Darum dürfte sich hier die verwendete Library kümmern.

2. Die empfangende Seite kann beim read()/recv() sowohl einen Bruchteil 
eines abgesendeten Datenpakets als auch mehrere davon direkt 
hintereinander bekommen und muss damit umgehen können.

Hammad S. schrieb:
> Soweit ich weiß ist tcp/ip zuständig die Pakete zu fragmentieren und
> danach wiederherzustellen. Also die Daten werden ankommen aber langsamer
> halt. Verstehe ich das richtig?

Ja, im schlimmsten Fall steigt der Overhead.

Was hier passiert, ist übrigens auch keine Fragmentierung (die passiert 
auf IP-Ebene, insbesondere durch Router), sondern Segmentierung.

Hammad S. schrieb:
> kann man ausrechnen wie viel man mindestens warten muss, damit das ganze
> ohne Fragmentation funktioniert?

Das ist implementationsabhängig. Ich würde mir darüber keine Gedanken 
machen und die Daten so schnell schicken, wie es für die Anwendung 
sinnvoll ist.

von Sebastian W. (wangnick)


Lesenswert?

Hmmm schrieb:
> [Einiges über TCP/IP]

Hammad benutzt eine MQTT-Client-Bibliothek (PubSubClient). Insofern hat 
er auf all das wenig Einfluss.

LG, Sebastian

von Peyer (Gast)


Lesenswert?

Sebastian W. schrieb:
> Hmmm schrieb:
>> [Einiges über TCP/IP]
>
> Hammad benutzt eine MQTT-Client-Bibliothek (PubSubClient). Insofern hat
> er auf all das wenig Einfluss.

Das Problem sitzt aber doch auf der PC-Seite, oder? TCP wurde kompatibel 
zur seriellen Schnittstelle entworfen. Beim Empfänger fällt ein Byte 
nach dem anderen raus, keine Pakete.

von Sebastian W. (wangnick)


Lesenswert?

Sebastian W. schrieb:
> Hammad S. schrieb:
>>> Da kommen erst Dein MQTT-Publish, dahinter folgt aber plötzlich noch ein
>>> zweiter Datensatz. Siehe beigefügter Screenshot, der markierte Bereich
>>> ist die eigentliche Publish-Message, der Rest gehört dort nicht hin.
>> ja das ist genau mein Problem
>
> Versuch das einmal selbst. Ruf nach client.publish() mal
> espClient.flush() auf. Vielleicht zwingt das das TCP-Paket raus, und
> verhindert das sich der nächste PUBLISH hinten ans selbe Paket noch dran
> klebt.

Hammad, hattest du Zeit meinen Vorschlag auszuprobieren?

LG, Sebastian

von Hammad S. (msaf)


Lesenswert?

Sebastian W. schrieb:
> Sebastian W. schrieb:
>> Hammad S. schrieb:
>>>> Da kommen erst Dein MQTT-Publish, dahinter folgt aber plötzlich noch ein
>>>> zweiter Datensatz. Siehe beigefügter Screenshot, der markierte Bereich
>>>> ist die eigentliche Publish-Message, der Rest gehört dort nicht hin.
>>> ja das ist genau mein Problem
>>
>> Versuch das einmal selbst. Ruf nach client.publish() mal
>> espClient.flush() auf. Vielleicht zwingt das das TCP-Paket raus, und
>> verhindert das sich der nächste PUBLISH hinten ans selbe Paket noch dran
>> klebt.
>
> Hammad, hattest du Zeit meinen Vorschlag auszuprobieren?
>
> LG, Sebastian
Hallo Sebastian,

sorry für die späte Antwort aber mein Laptop ist kaputt gegangen. Ich 
habe es probiert aber hat leider nichts gebracht. Ich würde den 
Vorschlag von @Hmmm nutzen und die Daten so weiter schicken auch wenn 
die segmentiert werden.

von Hammad S. (msaf)


Lesenswert?

Hmmm schrieb:

>
> Das ist implementationsabhängig. Ich würde mir darüber keine Gedanken
> machen und die Daten so schnell schicken, wie es für die Anwendung
> sinnvoll ist.

Vielen Dank für deine Antwort.
Wie kann ich am besten testen welchen Durchsatz meine WLAN-Verbindung 
hat?
Im Datasheet steht 150Mb/s, was ich nicht realistisch finde

von Sebastian (Gast)


Lesenswert?

Hammad S. schrieb:
> Ich habe es probiert aber hat leider nichts gebracht.

Ok. Ich hatte mir den EthernetClient Quellcode von flush() angeschaut, 
da wird anscheinend der Hardware der Befehl zum Senden geheben; aber bei 
ESP WiFi hab ich dann nicht mehr kontrolliert.

LG, Sebastian

von Paul (Gast)


Lesenswert?

Was für ein Problem besteht denn?

Das Verhalten mit 1490/235 Byte wurde oben ja schon erklärt und ist auch 
völlig in Ordnung.

von Hammad S. (msaf)


Lesenswert?

Paul schrieb:
> Was für ein Problem besteht denn?
>
> Das Verhalten mit 1490/235 Byte wurde oben ja schon erklärt und ist auch
> völlig in Ordnung.

Ich will nur die WLAN-Datenrate ausrechnen

von Hmmm (Gast)


Lesenswert?

Hammad S. schrieb:
> Ich will nur die WLAN-Datenrate ausrechnen

Das wird nichts, im Gegensatz zu Ethernet ist die nicht fest, sondern 
hängt von vielen Faktoren ab.

von Stefan F. (Gast)


Lesenswert?

Hmmm schrieb:
> Das wird nichts, im Gegensatz zu Ethernet ist die nicht fest, sondern
> hängt von vielen Faktoren ab.

Uns zwar so stark, dass ich zu Hause alles verkabele, was geht. Mein 
Sohn hat sich sogar ein Ethernet Kabel zum Bett hin verlegt - für den 
Laptop.

Man kann es auch übertreiben. Aber ich was damals nicht anders drauf, 
deswegen sage ich nichts dazu.

von Hammad S. (msaf)


Lesenswert?

Hmmm schrieb:
> Hammad S. schrieb:
>> Ich will nur die WLAN-Datenrate ausrechnen
>
> Das wird nichts, im Gegensatz zu Ethernet ist die nicht fest, sondern
> hängt von vielen Faktoren ab.
das ist ja klar.

Ich habe die Datenrate mit Hilfe von Iperf getestet und hab 15.2 Mb/s 
bekommen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.