Forum: Mikrocontroller und Digitale Elektronik MQTT Client mit ANSI-C programmieren. Library Verständnisprobleme


von Hanns-Jürgen M. (yogy)


Lesenswert?

Hallo, ich brauche etwas Unterstützung,

Ich bin auf Home-Assistant zur Haussteuerung gestoßen und versuche, 
meine Sensor/Actor Module dort einzubinden. HASSIO sowie den MQTT-Broker 
(Mosqitto) habe ich in VMWARE (Host: win 7) zum laufen gebracht.

Meine Sensor/Actor Systeme basieren auf Arduino-Controllerplatinen, die 
ich mittels ATMEL Studio in Ansi-C programmiere. C++ verwende ich nicht, 
da ich davon und allgemein von der OOP keine bzw. nicht viel Ahnung 
habe.

Um mal schnell etwas zum Spielen zu bekommen habe ich ein ESB8266-Modul 
unter Verwendung der Arduino-Plattform und der PubSubClient-Library mit 
"Q&D Programmiermethode" testweise zum Spielen gebracht. Leider ist 
diese Lib nur unter C++ verwendbar und nicht unter ANSI-C. Einen 
Versuch, sie nach ANSI-C zu portieren habe ich gleich abgebrochen, die 
Lib ist riesengroß.

Nach weiterer längerer Web-Suche stieß ich auf die MQTT-C Lib von Liam 
Bindle bei GITHUB, aber leider sind keine brauchbaren Anwendungs-Dokus 
oder gar brauchbare Beispiele zu finden. Es gelingt mir bis dato nicht, 
mein System mit dem MQTT Broker zu verbinden oder gar Daten hoch zu 
senden.

Wäre supertoll, falls hier jemand Ahnung von dieser Library hätte und 
ggf. Beispiele / Links nennen könnte. Vielleicht gibt es auch eine 
besser dokumentierte ANSI-C Library?

Danke in Voraus!

Yogy

von jemand (Gast)


Lesenswert?

https://mqtt.org/software/
Eine davon vielleicht?

von Hanns-Jürgen M. (yogy)


Lesenswert?

jemand schrieb:
> https://mqtt.org/software/
> Eine davon vielleicht?

Über diese Seite bin ich ja zu meinem MQTT-C von Liam Bindle gekommen. 
Diese Lib ist relativ "schmal" aber wohl für mich zu komplex im einsatz.

Ich werde mir wohl hier eine andere Lib aussuchen müssen.

VG  Yogy

von Christian K. (the_kirsch)


Lesenswert?

Vielleicht solltest du dich mal beschäftigen wie die ESP Firmware 
aufgebaut ist.

Es gibt ein SDK, das beinhaltet FreeRTOS, LwIP und Treiber für WLAN und 
BT.
Und einiges an Libraries.

https://github.com/espressif/ESP8266_RTOS_SDK

Das ist alles in C geschrieben.

Darauf aufbauend gibt es das Arduino Core, dass einen ermöglich Arduino 
Sketche für den ESP zu erstellen.
Verwenden viele muss man aber nicht.

Es gibt hier auch eine MQTT Lib die direkt auf dem SDK aufbaut und kein 
Arduino Core braucht.

https://github.com/espressif/esp-mqtt

Die ist in C geschrieben.

von Hanns-Jürgen M. (yogy)


Lesenswert?

Hallo zusammen,

ich habe mir die anderen Libs angeschaut und eigentlich keine einzige 
gefunden habe, die auf meinem System, das ohne weitere Firmware oder 
Betriebssystem arbeitet, ohnen weiteres ohne größerer Arbeiten 
einsetzbar wäre. Ich habe mich dann weiter mit  der Liam Bindle MQTT-C 
Lib beschäftigt und diese nun fast zu Spielen gebracht. Publish funzt.

Ursache der Probleme war/ist das Zusammenspiel mit „meinen“ anderen 
SW-Routinen sowie meiner relativen Unkenntnis der Pointersystematic bei 
C.

Schlußendlich scheitere ich an folgendem Problem:

Ich nutze auch die Client-Subscriber Funktion. Kommt eine Antwort vom 
Broker, dann ruft die MQTT-C -Lib die empfangenen Daten ab mit dem 
Funktionsaufruf:
1
ssize_t mqtt_unpack_response(struct mqtt_response* response, const U8 *buf, size_t bufsz)

So weit. So gut. Die Implementierung der Funktion ist meine Aufgabe, sie 
muß dazu „meine“ Routine (socket-read aus der W5500 Lib) aufrufen.

Nun scheitere ich daran, meine gelesenen Daten an den „buf“ zu 
übergeben.

Meine Routine derzeit:
1
ssize_t mqtt_pal_recvall(mqtt_pal_socket_handle fd, void* buf, size_t bufsz, int flags) 
2
{
3
  U8 recv_buff[1024];
4
    const void *const start = buf;   //Ist die Speicheradresse des Buffer-Starts
5
    ssize_t rv;
6
    
7
  rv =  (getSn_RX_RSR(_MQTT_Client_sock_nr));
8
  
9
  if (rv == 0)
10
  {
11
    return 0;
12
  }
13
  else 
14
  {
15
    
16
    W5500_socket_recv(_MQTT_Client_sock_nr, recv_buff, rv);  //ganzen buffer aus W5500 lesen!!! 
17
        bufsz -= rv;
18
// Nun umkopieren, das nicht funktioniert
19
    for (U8 i=0;i<rv;i++)
20
    {
21
       *(U8*)buf++ = recv_buff[i];
22
    }
23
    return buf - start;
24
  }
25
}
Der Problempunkt ist:
1
*(U8*)buf++ = recv_buff[i];
Das gibt zwar keine Kompilerfehler, funktioniert aber nicht.
IMHO wäre richtig
1
*buf++ = recv_buff[i];
aber das gibt die Fehlermeldung:  „invalid use of void expression.“

Der Versuch mit:
1
buf[i] = recv_buff[i];
bringt den gleichen Fehler. Und ein
1
(U8)buf[i] = recv_buff[i]; oder (U8*)buf[i] = recv_buff[i];
ebenso.

Wo liegt mein Fehler???

Danke und VG  Yogy

von Sebastian W. (wangnick)


Lesenswert?

Pointerarithmetik benötigt eine definierte Größe des Datentyps auf den 
gezeigt wird, und void hat keine definierte Größe.

Statt der for-Schleife würde ich memcpy(buf,recv_buff,rv); return rv; 
benutzen.

Rätselhaft bleibt warum *(U8*)buf++ = recv_buff[i]; nicht auch Fehler 
generiert, *(U8*)buf++ wird ja als *(U8*)(buf++) ausgewertet und buf++ 
ist wiederum verbotene void*-Arithmetik ...

LG, Sebastian

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Sebastian W. schrieb:
> Rätselhaft bleibt warum *(U8*)buf++ = recv_buff[i]; nicht auch Fehler
> generiert, *(U8*)buf++ wird ja als *(U8*)(buf++) ausgewertet und buf++
> ist wiederum verbotene void*-Arithmetik ...

Pointer-Arithmetik auf void Pointern ist eine gcc Erweiterung.

von Hanns-Jürgen M. (yogy)


Lesenswert?

Sebastian W. schrieb:
> Pointerarithmetik benötigt eine definierte Größe des Datentyps auf den
> gezeigt wird, und void hat keine definierte Größe.
>
> Statt der for-Schleife würde ich memcpy(buf,recv_buff,rv); return rv;
> benutzen.
>
> Rätselhaft bleibt warum *(U8*)buf++ = recv_buff[i]; nicht auch Fehler
> generiert, *(U8*)buf++ wird ja als *(U8*)(buf++) ausgewertet und buf++
> ist wiederum verbotene void*-Arithmetik ...
>
> LG, Sebastian

Ja, danke, mit memcopy funktioniert es. Nur, warum es nicht mit der 
for-Schleife funktioniert ist mir immer noch schleierhaft.

Übrigens kann ich nach memcopy den inhalt von buf korect auslesen mit:
1
       for (U8 i=0;i<rv;i++)
2
       {
3
       PutcService( *(U8*)(buf+i));
4
  }



(PutcService gibt bei mir ein Zeichen seriell aus)

Auf jeden Fall danke für den Tip.

Viele Grüße, Yogy

von Sebastian W. (wangnick)


Lesenswert?

Hanns-Jürgen M. schrieb:
> Nur, warum es nicht mit der
> for-Schleife funktioniert ist mir immer noch schleierhaft.
1
*(U8*)buf++
 entspricht
1
*(U8*)(buf++)
 und das ist void-Pointer-Arithmetik. Besser vermeiden.
1
*buf++
 ist void-Pointer-Arithmetik und dazu noch 
void-Pointer-Dereferenzierung. Das ist in jedem Fall ein Fehler.
1
buf[i]
 ist auch void-Pointer-Arithmetik und dazu noch 
void-Pointer-Dereferenzierung. Das ist auch in jedem Fall ein Fehler.
1
(U8)buf[i]
 ist dasselbe, und dann castest du void noch in U8. Ich glaube das geht 
nicht, man kann nur void-Pointer in andere Pointer casten, aber nicht 
void selbst. Ausserdem, selbst wenn das ginge, ist das Resultat nach dem 
cast ein Wert und kein lvalue.

Bei
1
(U8*)buf[i]
 castest du void in einen U8-Pointer. Das ist noch falscher.

Funktionieren müssten eigentlich
1
*((U8*)buf)++
 oder
1
((U8*)buf)[i]
 oder
1
*((U8*)buf+i)

LG, Sebastian

: Bearbeitet durch User
von Marco H. (damarco)


Lesenswert?

Das Spiel funktioniert anderes herum... Du prüfst das Emfangsregister 
und übergibst dann die Daten an den MQTT Teil.

Die Tücke beim w5500 ist das beim auslesen auch weitere Pakete im Buffer 
liegen können.

Den Code verstehe ich nicht, da fehlen mir die Zusammenhänge.

Es sei denn deine Library benutz Funktionspointer für den Ethernet 
Stack. Dann musst du dafür sorgen das die Daten so übergeben werden.

: Bearbeitet durch User
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.