TLS mit dem NodeMCU über MQTT

Hallo liebe Arduino Community,
ich habe jetzt seit einigen Tagen das Problem das ich es nicht hinbekomme über TLS auf meinen EXTERNEN MQTT Server zu kommen. Ich habe nun schon viele Beispielcodes gesehen und auch einige versucht einzubinden, habe mich aber selbst so verwirrt das ich einfach nur noch total auf dem Schlauch stehe. Den jedes Codebeispiel ist rätselhafter weise (für meine Augen) völlig anders. Es werden jedes Mal andere libraries verwendet andere befehle für Fingersprints… Ich habe inzwischen auch schon so viel in meinem Code rum gemurkst das es inzwischen nur noch ein verzweifeltes try and error kopieren ist.
Deshalb wollte ich mal hier um Hilfe fragen kann mir jemand kurz verraten was ich alles falsch manche und/oder wo ich etwas Passendes finde?

#include <ESP8266WiFi.h>                      //Einfügen der ESP und der MQTT Library
#include <ESP8266mDNS.h>
#include <PubSubClient.h>

 
const char* ssid = "---";                   //Loggon Daten für W-lan
const char* password = "---";
const char* mqttServer = "---";  
const int mqttPort = ---;
const char* mqttUser = "---";
const char* mqttPassword = "---";

const uint8_t fingerprint[20] = {0xDA, 0xC9, 0x02, 0x4F, 0x54, 0xD8, 0xF6, 0xDF, 0x94, 0x93, 0x5F, 0xB1, 0x73, 0x26, 0x38, 0xCA, 0x6A, 0xD7, 0x7C, 0x13};

 
WiFiClientSecure espClient;                         
PubSubClient client(espClient);
                                             //Initalisierung aller Variablen
long lastMsg = 0;                            //---------------------------------------------------
int i=0;                                     // für die for schleife
const int LEDI = 2;                          // Interne LED des ESP8266
const int REEDA = 16;                        // GPIO Pin des REED Kontaktes
int REEDstatusA = 0;                         // Variable zum speichern des REED Statuses
const int REEDB = 5;                         // GPIO Pin des REED Kontaktes
int REEDstatusB = 0;                         // Variable zum speichern des REED Statuses
const int REEDC = 4;                         // GPIO Pin des REED Kontaktes
int REEDstatusC = 0;                         // Variable zum speichern des REED Statuses
const int REEDD = 14;                        // GPIO Pin des REED Kontaktes
int REEDstatusD = 0;                         // Variable zum speichern des REED Statuses
const int REEDE = 12;                        // GPIO Pin des REED Kontaktes
int REEDstatusE = 0;                         // Variable zum speichern des REED Statuses
char msg[50];                                // Speicher für die Zu übermittelnde nachricht
int value = 0;                               //----------------------------------------------------
 

 void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  
  //espClient.setInsecure();
  
  espClient.setFingerprint(fingerprint);
  
  client.setServer(mqttServer, mqttPort);
  Serial.println(mqttServer);
  while (!client.connected()) 
  {
    Serial.print("Connecting to MQTT Broker...");
    if (client.connect("ESP8266Client", mqttUser, mqttPassword))
    {
      Serial.println("connected");  
    } 
    else 
    {
      Serial.print("failed with state ");
      Serial.println(client.state());
      delay(2000);
    }
  }
    pinMode(LEDI, OUTPUT);
    pinMode(REEDA, INPUT);
    pinMode(REEDB, INPUT);
    pinMode(REEDC, INPUT);
    pinMode(REEDD, INPUT);
    pinMode(REEDE, INPUT);
}

void reconnect() {                           //Funktion für die ausgabe des Status bei einem Reconect Über den Micro-USB port (Boud rate 115200)
    while (!client.connected()) {
        Serial.print("Reconnecting...");
        if (!client.connect("ESP8266Client", mqttUser, mqttPassword)) {
            Serial.print("failed, rc=");
            Serial.print(client.state());
            Serial.println(" retrying in 5 seconds");
            for(i=0;i<10;i++){
              digitalWrite(LEDI, HIGH);
              delay(250);
              digitalWrite(LEDI, LOW);
              delay(250);
            }
        }
    }
}

void loop() {
    digitalWrite(LEDI, LOW);
    if (!client.connected()) {                            //Abbfrage ob die Verbindung verloren wurde. Wenn ja wird die reconnect funktion aufgerufen
        reconnect();
    }
    //client.loop();                                      //Nicht nötich da der client ausschließlich Pupliziert
    REEDstatusA = digitalRead(REEDA);                       //Abfrage des REED-Kontaktes
    REEDstatusB = digitalRead(REEDB);
    REEDstatusC = digitalRead(REEDC);
    REEDstatusD = digitalRead(REEDD);
    REEDstatusE = digitalRead(REEDE);   

    if(REEDstatusA == LOW || REEDstatusB == LOW || REEDstatusC == LOW || REEDstatusD == LOW || REEDstatusE == LOW){ //Festlegen der nächsten nachricht anhand des REED-Kontakt statuses
      snprintf (msg, 50, "open");
    }
    else{
      if(REEDstatusA == HIGH && REEDstatusB == HIGH && REEDstatusC == HIGH && REEDstatusD == HIGH && REEDstatusE == HIGH)
      {
        snprintf (msg, 50, "closed");
      }
      else
      {
        snprintf (msg, 50, "undef");
      }
    }
    //Serial.print("Publish message: ")                   //Auskommentiert da nur zu Fehler analyse nützlich. Pushed Nachricht auf Micro-USB port (Boud rate 115200)
    Serial.println(msg);                                //Auskommentiert da nur zu Fehler analyse nützlich. Pushed Nachricht auf Micro-USB port (Boud rate 115200)
    client.publish("/data/MCU000/", msg);    //Senden der Festgelegten Nachricht auf den chanel pfad
    delay(1000);
}

"Geht nicht" ist schon mal eine schlechte Fehlerbeschreibung. Was genau geht denn nicht? Was sagt die serielle Konsole?

Ich weiß nicht, ob man sich da irgendwie mehr zurückholen kann an Informationen, aber über meinen COM-port bekomme ich nur das hier zurück:

08:36:28.845 -> Connecting to WiFi..
08:36:29.360 -> Connecting to WiFi..
08:36:29.829 -> Connecting to WiFi..
08:36:30.345 -> Connecting to WiFi..
08:36:31.636 -> Connecting to WiFi..
08:36:32.106 -> Connecting to WiFi..
08:36:32.106 -> Connected to the WiFi network
08:36:32.153 -> --SSID--
08:36:32.153 -> Connecting to MQTT Broker...failed with state -2
08:36:49.626 -> Connecting to MQTT Broker...failed with state -2
08:37:07.288 -> Connecting to MQTT Broker...failed with state -2
08:37:24.920 -> Connecting to MQTT Broker...

Ich habe auch versucht mit dem von client.state() zurück gegebenen -2 zu erfahren was das Problem ist. Mit der Hilfe von Tommy56 diese List gefunden: Github liste. Ich muss auch noch dazu sagen, das auch versuche, beim Server ankommen, aber wohl SSL und nicht TSL ist? Ich weiß nur weder WAS nicht funktioniert (ist es das falsche Format für den Fingerprint? Oder der falsche Fingerprint? Oder ist es der falsche Befehl?) und was mich noch sehr viel mehr stört ist das ich auch nicht weis wie ich das herausfinden könnte. Oder kann mir da jemand sagen wie ich vielleicht etwas mehr aus dem NodeMCU raus Kitzeln kann, was Fehler Meldungen angeht.

Hast Du mal die Methode ausprobiert?

int WiFiClientSecure::getLastSSLError(char *dest, size_t len)

Gruß Tommy

Es tut mir echt leid aber ich bekomme es gerade nicht mal mehr hin den Befehl von dir richtig einzufügen :’( . Ich bekomme dann das hier von der IDE zurück.

Arduino: 1.8.10 (Windows 10), Board: "NodeMCU 0.9 (ESP-12 Module), 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:2MB OTA:~1019KB), v2 Lower Memory, Disabled, None, Only Sketch, 115200"

C:\Users\xxx\xxx\xxx\xxx.ino: In function 'void setup()':

Arduino_window:56:63: error: invalid use of qualified-name 'BearSSL::WiFiClientSecure::getLastSSLError'

int WiFiClientSecure::getLastSSLError(char *dest, size_t len);

^

Mehrere Bibliotheken wurden für "ESP8266WiFi.h" gefunden
Benutzt: C:\Users\xxx\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.2\libraries\ESP8266WiFi
Mehrere Bibliotheken wurden für "ESP8266mDNS.h" gefunden
Benutzt: C:\Users\xxx\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.2\libraries\ESP8266mDNS
Mehrere Bibliotheken wurden für "PubSubClient.h" gefunden
Benutzt: C:\Users\xxx\Documents\Arduino\libraries\PubSubClient
exit status 1
invalid use of qualified-name 'BearSSL::WiFiClientSecure::getLastSSLError'

Dieser Bericht wäre detaillierter, wenn die Option
"Ausführliche Ausgabe während der Kompilierung"
in Datei -&gt; Voreinstellungen aktiviert wäre.

Oder setze ich denn Befehl einfach nur an der falschen Stelle? Ich hatte ihn jetzt in void setup()
drin Liegen.

Dein Client heißt espClient. Also:

char puffer[100];
espClient.getLastSSLError(puffer,sizeof(puffer));
Serial.println(puffer);

Dahin, wo Du versucht hast, Dich zum Server zu connecten.

Gruß Tommy

Ok danke. Also jetzt bekomme ich das hier zurück.

16:47:16.425 -> Connecting to WiFi..
16:47:16.929 -> Connecting to WiFi..
16:47:16.929 -> Connected to the WiFi network
16:47:16.929 -> xxx.xxx.biz
16:47:19.018 -> 
16:47:19.018 -> Soft WDT reset
16:47:19.018 -> 
16:47:19.018 -> >>>stack>>>
16:47:19.018 -> 
16:47:19.018 -> ctx: cont
16:47:19.018 -> sp: 3ffffdd0 end: 3fffffc0 offset: 01b0
16:47:19.018 -> 3fffff80:  3ffee6a4 3ffee5f4 3ffee80c 402010fc  
16:47:19.018 -> 3fffff90:  feefeffe feefeffe feefeffe 3ffee894  
16:47:19.018 -> 3fffffa0:  3fffdad0 00000000 3ffee854 40206184  
16:47:19.018 -> 3fffffb0:  feefeffe feefeffe 3ffe84fc 40100c39  
16:47:19.018 -> <<<stack<<<
16:47:19.018 -> 
16:47:19.018 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,6)
16:47:19.018 -> 
16:47:19.018 -> load 0x4010f000, len 1384, room 16 
16:47:19.064 -> tail 8
16:47:19.064 -> chksum 0x2d
16:47:19.064 -> csum 0x2d
16:47:19.064 -> vbc204a9b
16:47:19.064 -> ~ld
16:47:19.628 -> Connecting to WiFi..
16:47:20.098 -> Connecting to WiFi..

Du hast es hier eingefügt?

Serial.print("failed with state ");
      Serial.println(client.state());

Dann weiß ich auch nicht.

Gruß Tommy

Nein, aber wenn ich es dorthin verschiebe wo du geschrieben hast bekomme ich diese vielsagende Rückmeldung:

17:13:17.569 -> Connecting to WiFi..
17:13:17.569 -> Connected to the WiFi network
17:13:17.569 -> xxx.xxx.biz
17:13:17.569 -> Connecting to MQTT Broker...failed with state -2
17:13:33.174 -> Unknown error code.
17:13:35.206 -> Connecting to MQTT Broker...failed with state -2
17:13:50.835 -> Unknown error code.
17:13:52.828 -> Connecting to MQTT Broker...

Dann wissen die Macher der Lib nicht, was da schief geht.

Gruß Tommy

Also bunt zusammenkopieren ist so schon schlecht, da man schon wissen sollte, was wo passiert. Gerade hier kann es zu unschönen Überschneidungen kommen: es gibt 2 Pubsubclient-Libs. Eine von knolleary (char-Arrays) und eine von Imroy (Strings). Auch wenn sie gleich heißen, funktionieren sie anders. Also am besten an einem Beispiel der Lib orientieren.

Was mir auffällt: Du übergibst scheinbar nirgends die IP-Adresse des Brokers.

Bei mir sieht das so aus:

Deklarierung des MQTT-Parts:

const char* nodeName ="----";                                                   //Name des WiFi-Clienten
const char* ssid = "----";                                                      //SSID des WiFi-Netzwerkes
const char* password = "----";                                               //Passwort des WiFi-Netzwerkes
IPAddress server(xxx,xxx,xxx,xx);                                                   //IP-Adresse des MQTT-Servers
PubSubClient mqttClient(WiFiClient, server);

Reconnect-Teil des MQTT-Parts:

//MQTT-Verbindung
  if(!mqttClient.connected()){                                                      //wenn keine Verbindung zu MQTT-Broker
    #ifdef DEBUG
      Serial.print("Verbindungsaufbau zu Server: ");
      Serial.println(server);
    #endif
    mqttClient.set_callback(callback);                                              //callback() zuweisen
    while(!mqttClient.connected()){                                                 //solange keine Verbindung zu MQTT-Broker
      #ifdef DEBUG
        Serial.print(":");
      #endif
      mqttClient.connect(nodeName);                                                 //MQTT-Client-Verbindung starten
      uint8_t timeout = 8;                                                          //8 Sekunden bis neuer Versuch
      while (timeout && (!mqttClient.connected())){
        timeout--;
        #ifdef DEBUG
          Serial.print(".");
        #endif
        delay(1000);
      }
      if(mqttClient.connected()){                                                   //wenn Verbindung zu MQTT-Broker erfolgreich hergestellt
        #ifdef DEBUG
          Serial.println("");
          Serial.println("MQTT-Verbindung hergestellt");
        #endif
        //Subscribe-Teil hier


      }
    }
  }

@TO: Bist Du Dir sicher, dass Du die Zertifikate deines MQTT-Servers richtig installiert hast?

Gruß Tommy

DerLehmi: Also bunt zusammenkopieren ist so schon schlecht, da man schon wissen sollte, was wo passiert.

Da Stimme ich dir vollkommen zu, aber das ist nicht so einfach wie es klingt. Das hier ist ja mein erstes Arduino Projekt. Und bis ich versucht habe TLS zu implementieren war ich der Meinung, das ich wüsste, was wo passiert. :/ Aber das es zwei Pubsubclient-Libs gibt höre ich jetzt zum Beispiel wieder zum ersten Mal. Da kommt man ja auch nicht drauf ... Da werde ich mal weiter schauen, ob ich mit der Information mal wieder was finde beim Googeln.

Und was die Zertifikate angeht gehe ich davon aus das die stimmen, den ich habe auch 2 Raspberry Pi's die ebenfalls zum selben Server über das gleiche Zertifikate laufen und bei denen läuft alles.

Also ich weiß jetzt das ich den PubSubClient von knolleary. Aber ansonsten habe ich an sich gestern eigentlich keine fortschritte gemacht :( Hat zufällig noch jemand eine Idee, woran es liegen kann oder bei welchem Befehl ich weiter googeln soll? Ich weiß an sich inzwischen nicht mal mehr wo ich weiter googeln soll. Ich ertrinke in Unwissenheit. Ansonsten sag ich hier Bescheid falls ich auf ne Lösung stolpere.

Bau Deine Verbindungsroutine doch so auf wie von mir vorgeschlagen. An der Verbindung zum Broker hängt es doch....

So. Frohes neues ich hab mich während der Festtage mal mit anderen dingen beschäftigt. Was die Verbindungsroutine angeht, sehe ich die Unterschiede nicht wirklich. Bis auf das Setzen der, bei mir nicht vorhandenen, callback Funktion ist ja alles gleich nur mit weniger Serial prints. Und die Verbindung zum brocker ist nur passiv das Problem. Denn das hatte ja vor TSL Implementierung funktioniert.

DerLehmi: Was mir auffällt: Du übergibst scheinbar nirgends die IP-Adresse des Brokers.

Um nochmal hier drauf zurückzukommen. Auf den Raspis die ebenfalls über MQTT auf ein und denselben Server zugreifen gingen die Zertifikate erst als ich die Server eingetragen hatte und nicht die IP Adresse so wie ich das verstanden habe geht das nicht über Pure IP. Kann der ESP8266 überhaupt von einem DNS Server die IP erfragen? Bzw. macht er das automatisch? Denn mein Sketch funktioniert weder mit IP-Adresse noch mit alias.

Bis auf das Setzen der, bei mir nicht vorhandenen, callback Funktion ist ja alles gleich nur mit weniger Serial prints.

--> Vielleicht braucht er ja eine Callback-Routine? Ich hatte mal das Problem, dass er sich nicht zuverlässig mit dem Broker verbunden hat. Beheben konnte ich das Problem, indem ich die Callback-Routine bei fehlerhaftem Verbindungsversuch erneut zugewiesen habe.

Um nochmal hier drauf zurückzukommen. Auf den Raspis die ebenfalls über MQTT auf ein und denselben Server zugreifen gingen die Zertifikate erst als ich die Server eingetragen hatte und nicht die IP Adresse so wie ich das verstanden habe geht das nicht über Pure IP. Kann der ESP8266 überhaupt von einem DNS Server die IP erfragen? Bzw. macht er das automatisch? Denn mein Sketch funktioniert weder mit IP-Adresse noch mit alias.

-->In diesem Fall kann ich auch nicht wirklich weiterhelfen, mit TLS habe ich noch nicht gearbeitet. Ich hatte bisher Mosquitto auf Raspberry oder einem Netbook laufen lassen und die NodeMCUs per PubSubClient darauf verbinden lassen. Kannst Du denn mit mqttSpy auf den Broker verbinden?

Also das Einfügen einer Callback Funktion hatte keine spürbare Veränderung. Und über MQTT Spy habe ich auch keine Verbindung aufgebaut bekommen. Aber ich habe es heute geschafft aus dem NodeMCU mehr Fehler Informationen herauszukitzeln.

17:13:55.990 -> SDK:2.2.2-dev(38a443e)/Core:2.6.2=20602000/lwIP:STABLE-2_1_2_RELEASE/glue:1.2-16-ge23a07e/BearSSL:89454af
17:13:55.990 -> scandone
17:13:56.133 -> scandone
17:13:56.133 -> state: 0 -> 2 (b0)
17:13:56.133 -> state: 2 -> 3 (0)
17:13:56.503 -> Connecting to WiFi..
17:13:57.010 -> Connecting to WiFi..
17:13:57.143 -> state: 3 -> 0 (4)
17:13:57.143 -> reconnect
17:13:57.244 -> scandone
17:13:57.244 -> state: 0 -> 2 (b0)
17:13:57.244 -> state: 2 -> 3 (0)
17:13:57.244 -> state: 3 -> 5 (10)
17:13:57.244 -> add 0
17:13:57.244 -> aid 1
17:13:57.244 -> cnt
17:13:57.278 ->
17:13:57.278 -> connected with --SSID--, channel 11
17:13:57.278 -> dhcp client start...
17:13:57.278 -> ip:192.168.99.23,mask:255.255.255.0,gw:192.168.99.254
17:13:57.483 -> Connecting to WiFi..
17:13:57.483 -> Connected to the WiFi network
17:13:57.517 -> --MQTT.Server.biz--
17:13:57.517 -> Connecting to MQTT Broker...pm open,type:2 0
17:14:13.465 -> BSSL:connect: Unable to connect TCP socket
17:14:13.465 -> failed with state -2
17:14:13.465 -> Unknown error code.
17:14:15.483 -> Connecting to MQTT Broker...BSSL:connect: Unable to connect TCP socket
17:14:31.145 -> failed with state -2
17:14:31.145 -> Unknown error code.
17:14:33.149 -> Connecting to MQTT Broker...

Ich weiß nur noch nicht genau was "BSSL:connect: Unable to connect TCP socket" zu bedeuten hat. Ich habe es auch geschafft mit einem Ping Script den Server zu erreichen. Ich konnte mir auch die IP Adresse danach ausgeben lassen. Also bekommt der ESP auch über den DNS Server brav die IP raus ohne Probleme und da die Durchschnittszeit für den Ping bei 14.5ms lag sehe ich da auch kein Verbindungsproblem in dem Sinne.

Ich werde den Verdacht nicht los, dass der SecureClient SSL, aber kein TLS spricht.

Zitat: “Die Unterschiede zwischen diesem Protokoll und SSL 3.0 sind nicht dramatisch, aber sie sind groß genug, dass TLS 1.0 und SSL 3.0 nicht zusammenarbeiten.” (Quelle)

Gruß Tommy

Sabeldu: Ich habe es auch geschafft mit einem Ping Script den Server zu erreichen.

Bedenke dabei das Ping über ICMP läuft, das ist ein anderes Protokoll. Sagt also nicht aus das TCP und UDP funktionieren.

Hauptaufgabe von ICMP ist die Übertragung von Statusinformationen und Fehlermeldungen der Protokolle IP, TCP und UDP.

Auch kann es Vorkommen das TCP und UDP funktionieren aber Ping nicht!

Aus einer fehlenden Antwort kann nicht geschlossen werden, dass die Gegenstelle nicht erreichbar wäre, da manche Hosts so konfiguriert sind, dass sie ICMP-Pakete ignorieren und verwerfen;

Gruß Fips