Opta MQTT over port 8883 using a TLS cert for encryption

Can someone point me to a library to handle an MQTT connection with TLS encryption using WiFi?
(Specifically for the OPTA)

I'm using the code below for a broker that uses a simple username and password over port 1883, However I need to connect to a broker over port 8883 which uses a cert for TLS encryption.

#include <WiFi.h>
#include <PubSubClient.h>

// Replace these with your WiFi network credentials
const char* ssid = "ssid";
const char* password = "password";

// Create an instance of the WiFiClient and PubSubClient
WiFiClient wifiClient;
PubSubClient client(wifiClient);

unsigned long previousMillis = 0;
const long interval = 2000; // Publish interval in milliseconds

void setup() {
  Serial.begin(115200);
  delay(10);
  connectToWiFi();
}

void loop() {
  if (!client.connected()) {
    connectToMQTT();
  }

  client.loop();
  digitalWrite(LED_D1, HIGH);
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    int wifiSignalStrength = WiFi.RSSI();
    char signalStrengthString[4];
    itoa(wifiSignalStrength, signalStrengthString, 10);

    client.publish("opta_1", signalStrengthString);
    Serial.print("ssi: ");
    Serial.println(signalStrengthString);
    digitalWrite(LED_D2, HIGH);
    delay(100);
    digitalWrite(LED_D2, LOW);  
  }
}

void connectToWiFi() {
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    digitalWrite(LED_D0, LOW);
  }

  Serial.println("");
  Serial.println("WiFi connected");
  digitalWrite(LED_D0, HIGH);
}

void connectToMQTT() {
  // Replace with the MQTT broker's domain or IP address and port
  const char* mqtt_server = "mqtt broker ip addr";
  const int mqtt_port = 1883;
  const char* mqtt_username = "mqtt username";
  const char* mqtt_password = "mqtt password";
  // Generate a unique client ID 
  String clientId = "Arduino_OPTA";
  client.setServer(mqtt_server, mqtt_port); 

  while (!client.connected()) {
    digitalWrite(LED_D1, LOW);

    // Close the TCP connection before trying to reconnect
    wifiClient.stop();
    delay(1000);  // Optional delay

    if (WiFi.status() != WL_CONNECTED) {
      connectToWiFi();
    }

    Serial.print("Attempting MQTT connection with ID ");
    Serial.print(clientId);
    Serial.print("...");

    // Use the unique client ID
    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      switch (client.state()) {
        case -4:
          Serial.println(" : Connection timeout");
          break;
        case -3:
          Serial.println(" : Connection lost");
          break;
        case -2:
          Serial.println(" : Connect failed");
          break;
        case -1:
          Serial.println(" : Disconnect failed");
          break;
        case 1:
          Serial.println(" : Invalid protocol");
          break;
        case 2:
          Serial.println(" : Invalid client ID");
          break;
        case 3:
          Serial.println(" : Server unavailable");
          break;
        case 4:
          Serial.println(" : Bad credentials");
          break;
        case 5:
          Serial.println(" : Not authorized");
          break;
      }
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}



1 Like

Exact same problem. Seems the OPTA core is missing the WiFiClientSecure library and dependencies that makes this work with the ESP32/ESP8266 platforms. That library enables in-line declaration of a security certificate, example: Getting Started with MQTT on Arduino Using NodeMCU ESP8266. I'm part way through putting together all the libraries manually in the hope i can achieve the required outcome via brute force, but if anyone has a cleaner (i.e. less work) approach, I'm watching with interest. @cmb672 please post if you manage to find a solution to this, thanks.

1 Like

@ dwholmesphd Will do!

At first I thought I was just missing the proper library or something simple. It now appears TLS with the mbed platform is not trivial. To be honest I've got a bit of buyers remorse for purchasing over $1800 worth of Optas based on market hype.

Without an easily accessible secure MQTT library, there's no way this comes close to an "Industrial" IIoT solution, especially at the current price point.

Nevertheless I'm continuing to develop on the Opta as an IIoT Edge device with an intermediary NodeRed broker to handle the secure MQTT traffic.

Hopefully someone with much more expertise than I will chime in with a suitable TLS library for the Opta.

2 Likes

@cmb672, may be making some progress. After some digging in the mbed_opta core, i've found it does include the libraries WiFiSSLClient and MbedSSLClient. Within the MbedSSLClient there is a setRootCA() private function, but I haven't yet worked out how to expose it to a sketch to set the CA. Will keep working, but let me know if you uncover anything in the meantime. My C++ is pretty rough.

I was over thinking it. Looks like the WiFiSSLClient does the requesting for you. I've successfully connected to HiveMQ over port 8883 with the following code:

#include <WiFi.h>
#include <WiFiSSLClient.h>
#include <PubSubClient.h>
#include "arduino_secrets.h"


#include "arduino_secrets.h"

//WiFi
const char *ssid = SECRET_SSID;      // SSID / nome da rede WiFi que deseja se conectar
const char *password = SECRET_PASS;  // Senha da rede WiFi que deseja se conectar

// MQTT Broker
const char *mqtt_broker = "<broker path, eg HiveMQ etc>";  // broker address
const char *topic = "<topic>/<topic sub>";                                         // define topic
const char *mqtt_username = "<username>";                               // username for authentication
const char *mqtt_password = "<password>";                                // password for authentication
const int mqtt_port = 8883;                                                       // port of MQTT over TCP

WiFiSSLClient espClient;
PubSubClient client(espClient);

void connectToMQTT() {
  //connect to MQTT broker
  while (!client.connected()) {
    String clientId = "OptaClient-";  // Create a random client ID
    clientId += String(random(0xffff), HEX);

    Serial.print("The client connects to the mqtt broker\n");
    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {
      Serial.println("MQTT broker connected");
    } else {
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
}


void reconnect() {
  //mqttClient.setBufferSize(255);
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    String clientId = "OptaClient-";  // Create a random client ID
    clientId += String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {
      Serial.println("MQTT broker connected");
      //client.subscribe(mqttTopic);
    } else {
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
}

void setup() {
  // Set software serial baud to 9600;
  Serial.begin(9600);
  // connecting to a WiFi network
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  //connecting to a mqtt broker

  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);

  connectToMQTT();

  // publish and subscribe
  client.publish(topic, "hello broker from arduino opta");  // publish to the topic
  client.subscribe(topic);                                  // subscribe from the topic
}

void callback(char *topic, byte *payload, unsigned int length) {
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  Serial.println("-----------------------");
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

Will try with the Arduino Cloud and a few other brokers next, but so far looks to be the solution.

3 Likes

Has anyone managed to evolve the libraries to add the certifiers generated on the server?

Hello,
Does this work on esp8266?
Where can I get the 'WiFiSSLClient.h' file?
Which libraries must be installed and what does the code look like to run on an ESP8266?
This certificate thing to connect to the mqtt broker is complicated for me to understand, can you help me?
Do you have other suggestions for free mqtt brokers?

em portuguĂŞs do Brasil:
Olá,
Isso funciona em esp8266?
Onde consigo o arquivo 'WiFiSSLClient.h'?
Quais bibliotecas devem ser instaladas e como fica o cĂłdigo para rodar em um ESP8266?
Esta coisa de certificado para conectar a broker mqtt está complicado para Eu entender, pode me ajudar?
Tem outras sugestões de mqtt brokers gratuitos?

I've had success with ESP32, with <WiFiClientSecure.h>, where the relevant methods are

    void setCACert(const char *rootCA);
    void setCertificate(const char *client_ca);
    void setPrivateKey (const char *private_key);

which expect the PEM-format text that start with

-----BEGIN CERTIFICATE-----

and

-----BEGIN RSA PRIVATE KEY-----

It looks a little different with ESP8266: the same-name file is effectively an alias for

#include "WiFiClientSecureBearSSL.h"

and in that file, the equivalent methods for WiFiClientSecure are different

    // Install certificates of trusted CAs or specific site
    void setTrustAnchors(const X509List *ta) { _ctx->setTrustAnchors(ta); }
    // In cases when NTP is not used, app must set a time manually to check cert validity
    void setX509Time(time_t now) { _ctx->setX509Time(now); }
    // Install a client certificate for this connection, in case the server requires it (i.e. MQTT)
    void setClientRSACert(const X509List *cert, const PrivateKey *sk) { _ctx->setClientRSACert(cert, sk); }
    void setClientECCert(const X509List *cert, const PrivateKey *sk,
                         unsigned allowed_usages, unsigned cert_issuer_key_type) {
      _ctx->setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
    }

There are different methods for client certs, depending on whether it is RSA (as in the example PRIVATE KEY above) or Elliptic-Curve. Instead of plain char * C-string, there are distinct types

In case PubSubClient does not work, try the newer (and still beta) ArduinoMqttClient, available in a same-name library by Arduino.

1 Like

@dwholmesphd I have tried the code you provided with my credentials, but am unable to connect to my hivemq cloud server with my arduino opta. Were there any other steps you had to take prior loading the script such as uploading root certificates?

Hi @bmwilk33, in the end, i followed this tutorial for connecting to AWS IoT Core - https://docs.arduino.cc/tutorials/opta/getting-started-with-aws-iot-core/. If you set up the security certificates following this (creating on hivemq assuming they have a similar certificate generation process), then i'd think it would work. You can also copy the sketch from the Arduino IDE to the Arduino PLC IDE if you establish the right libraries to add in the manual library definition. On AWS at least, you also need to manually add a security policies for each type of MQTT access you want (connect, receive, publish, subscribe), just using * for policy action doesn't seem to work on AWS at least. Let me know if you can get it to work but I've managed to get really reliable results across multiple devices connected to AWS, so hopefully hivemq is similar.

One of my problems is that the greengrass application is inside a server that does not belong to aws. And opta does not send anything because it does not recognize it.

Hi, in the end were you able to get the opta working with hivemq cloud? and also I am having issues connecting to the aws iot core any suggestions on troubleshooting?

I moved to AWS, so haven't looked at hivemq again. Main tips in my previous post for connecting to AWS.