Hallo zusammen,
für mein Studium muss ich im Bereich Netzwerke ein Projekt umsetzen, ich
habe mich für das Übertragen von Temperaturdaten via MQTT von einem
ESP8266 zu einem Raspi, auf dem Mosquitto läuft, entschieden.
Ich habe versucht, das MQTT PUBLISH Paket selbst zu implementieren,
hierzu hab ich mich an dieser Dokumentation orientiert:
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718037
Die folgende Funktion befüllt diese MQTT-Struktur:
1 | typedef struct MQTT {
|
2 | uint8_t fix_header[FIXED_HEADER_SIZE];
|
3 | uint8_t *var_header;
|
4 | uint8_t payload[PAYLOAD_SIZE];
|
5 |
|
6 | uint8_t var_header_size;
|
7 |
|
8 | uint8_t (*createPacket)(struct MQTT*, int32_t);
|
9 | void (*fillPacket)(struct MQTT*, uint8_t*);
|
10 | } MQTT;
|
1 | #define PACKET_TYPE_PUBLISH 0x30
|
2 | #define PACKET_PUBLISH_NO_DUP 0x00
|
3 | #define PACKET_PUBLISH_QOS_AT_MOST_ONCE 0x00
|
4 | #define PACKET_PUBLISH_RETAIN 0x00
|
5 |
|
6 | uint8_t ICACHE_FLASH_ATTR mqttCreatePacket(MQTT *self, int32_t tempData) {
|
7 | self->fix_header[0] = PACKET_TYPE_PUBLISH | PACKET_PUBLISH_NO_DUP | PACKET_PUBLISH_QOS_AT_MOST_ONCE | PACKET_PUBLISH_RETAIN;
|
8 |
|
9 | char topic[3];
|
10 |
|
11 | topic[0] = 'a';
|
12 | topic[1] = '/';
|
13 | topic[2] = 'b';
|
14 |
|
15 | const uint8_t topicStrLen = sizeof(topic) / sizeof(topic[0]);
|
16 | self->var_header_size = 1 + 1 + topicStrLen;
|
17 |
|
18 | os_free(self->var_header);
|
19 | self->var_header = os_zalloc(sizeof(uint8_t) * self->var_header_size);
|
20 |
|
21 | self->var_header[0] = 0;
|
22 | self->var_header[1] = topicStrLen;
|
23 | for(uint8_t i = 0; i < topicStrLen; i++) {
|
24 | self->var_header[2 + i] = topic[i];
|
25 | }
|
26 |
|
27 | self->payload[0] = (tempData >> 24) & 0xFF;
|
28 | self->payload[1] = (tempData >> 16) & 0xFF;
|
29 | self->payload[2] = (tempData >> 8) & 0xFF;
|
30 | self->payload[3] = (tempData & 0xFF);
|
31 |
|
32 | uint8_t completeLen = self->var_header_size + PAYLOAD_SIZE;
|
33 | uint8_t encodedByte = 0;
|
34 | do {
|
35 | encodedByte = completeLen % 128;
|
36 | completeLen /= 128;
|
37 | if(completeLen > 0)
|
38 | encodedByte = encodedByte | 128;
|
39 | else {
|
40 | self->fix_header[1] = encodedByte;
|
41 | break;
|
42 | }
|
43 | }
|
44 | while(completeLen > 0);
|
45 |
|
46 | return (1 + 1) + self->var_header_size + 4;
|
47 | }
|
Am Algorithmus für die Längenangabe im FixedHeader hab ich mich am
vorgeschriebenen Algorithmus orientiert (Punkt 2.2.3:
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718024)
In der *user_send_data*-Funktion kopiere ich die Struktur einfach nur in
ein Array:
1 | void ICACHE_FLASH_ATTR user_send_data(struct espconn *pespconn) {
|
2 | char *pbuf = (char*)os_zalloc(sizeof(uint8_t) * mqttSize);
|
3 |
|
4 | mqtt.fillPacket(&mqtt, pbuf);
|
5 | espconn_send(pespconn, (uint8_t*)pbuf, mqttSize);
|
6 |
|
7 | os_free(pbuf);
|
8 | }
|
Die Variable mqttSize ist hierbei der Rückgabewert der oben gezeigten
Funktion mqttCreatePacket.
Hier noch die Funktion fillPacket
1 | void ICACHE_FLASH_ATTR fillPacket(MQTT *self, uint8_t *packet) {
|
2 | // Fill Fixed Header
|
3 | packet[0] = self->fix_header[0];
|
4 | packet[1] = self->fix_header[1];
|
5 |
|
6 | // Fill Variable Header
|
7 | packet[2] = self->var_header[0];
|
8 | packet[3] = self->var_header[1];
|
9 |
|
10 | // Fill variable TopicBytes
|
11 | for(uint8_t i = 0; i < self->var_header_size - 2; i++) {
|
12 | packet[4 + i] = self->var_header[i + 2];
|
13 | }
|
14 |
|
15 | // Fill payload
|
16 | for(uint8_t i = 0; i < PAYLOAD_SIZE; i++) {
|
17 | packet[2 + self->var_header_size + i] = self->payload[i];
|
18 | }
|
19 | }
|
Leider funktioniert das ganze nicht, der Broker auf dem Raspberry
registriert kein MQTT Packet auf dem Channel *a/b*.
Ich hab mir den Inhalt des Pakets kurz vorm Senden nochmal byteweise
ausgeben lassen:
1 | mqttSize: 11
|
2 | FIXED HEADER BYTE 0: 0x30
|
3 | FIXED HEADER BYTE 1: 0x9
|
4 | VAR HEADER BYTE 0: 0
|
5 | VAR HEADER BYTE 1: 3
|
6 | VAR HEADER BYTE 2: a
|
7 | VAR HEADER BYTE 3: /
|
8 | VAR HEADER BYTE 4: b
|
9 | TEMP FROM PAYLOAD: 2681
|
Das ist meiner Meinung nach korrekt.
Um zu Überprüfen, ob ein grundsätzlicher Übertragungsfehler vorliegt,
hab ich schnell ein Arduino-Beispielprogramm mit der *PubSub*-Bibliothek
zusammengeschrieben - so funktioniert es einwandfrei!
Meines Wissens nach ist die Publish-Nachricht mit QoS = 0 auch komplett
verbindungslos, d.h. man benötigt keinen vorherigen Verbindungsaufbau?
Ich frage mich nur, wo mein Fehler liegt?