MQTT Publish mit ESP8266 implementieren

OP #5878541
Lesenswert?

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?
Gast #5878618
Lesenswert?

Anstatt deine Zeit zu verschwenden, solltest du eine Lib nehmen die 
funktioniert.

Du sendest ein Byte zu viel bei/für QoS 0. Lass den Packet ID weg und 
gut.

> A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 
0 [MQTT-2.3.1-5].

Das ist aber nicht die Lösung des Problems - die steht im ersten Satz.

MfG
OP #5878638
Lesenswert?

Zeitverschwendung schrieb:
> Lass den Packet ID weg und
> gut.

Blöde Frage von mir vielleicht - aber wo sende ich eine Packet ID?

Genau den Satz, den du zitierst, habe ich nämlich eigentlich beachtet?
1
A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0

Zeitverschwendung schrieb:
> Anstatt deine Zeit zu verschwenden, solltest du eine Lib nehmen die
> funktioniert.

Da gebe ich dir grundsätzlich recht, nur leider hängt noch etwas mehr 
drann als das Senden des Paketes (z.B. Auslesen des Temperatur-Sensors, 
das müsste ich ja dann ebenfalls auf das Arduino Framework portieren) - 
der Rest klappt ja wunderbar, nur dieses MQTT Paket nicht. Und so viel 
kann man doch eigentlich bei 11 Bytes nicht falsch machen - oder?
Gast #5878660
Lesenswert?

Da liegt der Sinn bei MQTT, möglichst sparsam sein.

Lass das Byte weg.

Du willst aber bestimmt auch mal andere Features nutzen (Mqtt5), 
deswegen nimm die Lib.

Das mit Arduino würde ich schnell vergessen, das hält nur auf. Besorge 
dir PlatformIO und nutze das esp8266 SDK.

Und schau doch mal bspw. beim esp8266 Wifi repeater rein, da ist für 
dich alles dabei.. ;-)

MfG
Gast #5878701
Lesenswert?

Bis zur Länge 127 ist das nur ein Byte. Du sendest also definitiv ein 
Byte zu viel.

Wenn du mal mehr Daten (>127) hast dann wendest du das Verfahren nach 
Punkt 2.2.3 an.

Nimm eine Lib. (:
OP #5878855
Lesenswert?

Hab jetzt sowohl das
1
mqttSize: 10
2
FIXED HEADER BYTE 0: 0x30
3
FIXED HEADER BYTE 1: 0x9
4
VAR HEADER BYTE 0: 3
5
VAR HEADER BYTE 1: a
6
VAR HEADER BYTE 2: /
7
VAR HEADER BYTE 3: b
8
TEMP FROM PAYLOAD: 2983

als auch das
1
mqttSize: 10
2
FIXED HEADER BYTE 0: 0x30
3
FIXED HEADER BYTE 1: 0x8
4
VAR HEADER BYTE 0: 3
5
VAR HEADER BYTE 1: a
6
VAR HEADER BYTE 2: /
7
VAR HEADER BYTE 3: b
8
TEMP FROM PAYLOAD: 2983

ausprobiert. Der Broker sagt nur
1
1560806826: Socket error on client <unknown>, disconnecting.

:(

Edit: Das Arduino-Programm macht noch ein Connect:
1
void reconnect() {
2
    while (!client.connected()) {
3
        Serial.print("Reconnecting...");
4
        if (!client.connect("ESP8266Client")) {
5
            Serial.print("failed, rc=");
6
            Serial.print(client.state());
7
            Serial.println(" retrying in 5 seconds");
8
            delay(5000);
9
        }
10
    }
11
}

Braucht man das wenn man nur ein publish machen will?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren