Arduino example with server signed certificate

Hi,
I am new to HiveMQ.
I can to connect my device to my basic cluster using the MQTT.fx client.
I have select the port 8883 and the option "CA server signed certificate " on the “SSL/TLS settings”
Now I am trying to connect to the broker using my arduino ethernet device and the PubSubClient library.
I can connect to the public demo broker (port 1883) but I can’t connect to my Hostname (port 8883)
I want an arduino example for this SSL/TLS connection. I have searched for hours but I can’t find an example for connection to HiveMQ with “CA server signed certificate”.
Could someone help me with this issue by upoloading an example for arduino ethernet or esp8266 device?

Hey Chris,

I just happen to have ran my first experiments with a D1 Mini (ESP8266) and a HiveMQ Cloud Basic cluster yesterday. I got it working using the Adafruit MQTT Library.
Here’s a snippet:

#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "--YOUR-SSID--"
#define WLAN_PASS       "--YOUR-KEY--"

/************************* MQTT Setup *********************************/

#define MQTT_SERVER      "<your-id>.s1.eu.hivemq.cloud"
#define MQTT_SERVERPORT  8883
#define MQTT_USERNAME    "username"
#define MQTT_PASSWORD         "password"

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClientSecure client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_PASSWORD);

// This is the sha-1 fingerprint of the current certificate for <your-id>.s1.eu.hivemq.cloud (It's a wildcard certificate, so should be the same for all deployments) I'm not sure at this point if this will change once the letsencrypt certificate is updated
static const char *fingerprint PROGMEM = "EA 59 CA BD 80 01 01 E8 12 58 B6 4E A3 59 2B 5A 6E 60 BC F5";

/****************************** Feeds ***************************************/

Adafruit_MQTT_Publish sensor = Adafruit_MQTT_Publish(&mqtt, MQTT_USERNAME "/sensors/moisture/test1");

/*************************** Sketch Code ************************************/

// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();

int rawSensorValue = 0;
int normalizedValue = 0;

void setup() {
  Serial.begin(76800);
}

void wakeupCallback() { }

void loop() {
  // Connect to WiFi access point.
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: "); Serial.println(WiFi.localIP());
  client.setFingerprint(fingerprint);

  
  // mqtt.subscribe(&onoffbutton);

  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here
  Adafruit_MQTT_Subscribe *subscription;
  /*while ((subscription = mqtt.readSubscription(5000))) {
    if (subscription == &onoffbutton) {
      Serial.print(F("Got onoff: "));
      Serial.println((char *)onoffbutton.lastread);
    } else if (subscription == &errors) {
      Serial.print(F("ERROR: "));
      Serial.println((char *)errors.lastread);
    } else if (subscription == &throttle) {
      Serial.println((char *)throttle.lastread);
    }
  }*/

  rawSensorValue = analogRead(A0);
  // Max dryness = 850, max wetness = 450, normalize to 0 - 100 range
  normalizedValue = (850 - min(rawSensorValue, 850)) / 4;
  
  // Now we can publish stuff!
  Serial.print(F("\nSending moisture, raw: "));
  Serial.print(rawSensorValue);
  Serial.print(", normalized: ");
  Serial.println(normalizedValue);
  
  Serial.print(F("Publishing... "));
  if (! sensor.publish(normalizedValue)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }
 mqtt.disconnect();
 Serial.println("Sleeping...");
 // TODO: deepsleep doesn't work for me(?) fix this somehow?
 //ESP.deepSleep(60e6);

 // Timed Light Sleep instead for now
 //extern os_timer_t *timer_list;
 //timer_list = nullptr;  // stop (but don't disable) the 4 OS timers
 WiFi.mode(WIFI_OFF);
 wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
 wifi_fpm_set_wakeup_cb(wakeupCallback);
 wifi_fpm_open();
 wifi_fpm_do_sleep(60E6);
 delay(60e3 + 1);
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
    retries--;
    if (retries == 0) {
      // basically die and wait for WDT to reset me
      while (1);
    }
  }
  Serial.println("MQTT Connected!");
}

I commented out and removed some of the references to subscriptions because my application doesn’t need to subscribe / read any messages.

It’s adapted from Adafruit_MQTT_Library/adafruitio_errors_esp8266.ino at master · adafruit/Adafruit_MQTT_Library · GitHub

Note that this example uses the certificate fingerprint, which is going to change when the certificate is renewed. Optimally, you’d use the root CA from the TLS certificate to verify the certificate even when the certificate changes.

Hi Simon,
thank you very much :grinning:
Your code works fine!

I have modified the library example of the PubSubClient library using the WiFiClientSecure.
This is the final code for the PubSubClient that works fine too!

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.

const char* ssid = “-WIFI SSID-”;
const char* password = “-WIFI PASSWORD”;
const char* mqtt_server = “.s1.eu.hivemq.cloud”;

static const char *fingerprint PROGMEM = “EA 59 CA BD 80 01 01 E8 12 58 B6 4E A3 59 2B 5A 6E 60 BC F5”;

//WiFiClient espClient;
WiFiClientSecure espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;

//==========================================
void setup_wifi() {

delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);

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

randomSeed(micros());

Serial.println("");
Serial.println(“WiFi connected”);
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}

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

// Switch on the LED if an 1 was received as first character
if ((char)payload[0] == ‘1’) {
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is active low on the ESP-01)
} else {
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
}

}
//=====================================
void reconnect() {
// Loop until we’re reconnected
while (!client.connected()) {
Serial.print(“Attempting MQTT connection…”);
// Create a random client ID
String clientId = “ESP8266Client-”;
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(), “username”, “password”)) {
//if (client.connect(clientId.c_str())) {
Serial.println(“connected”);
// Once connected, publish an announcement…
client.publish(“outTopic”, “hello world”);
// … and resubscribe
client.subscribe(“inTopic”);
} else {
Serial.print(“failed, rc=”);
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}

//================================================
void setup() {
delay(500);
Serial.begin(9600);
delay(500);
setup_wifi();

pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
espClient.setFingerprint(fingerprint);
// Setting insecure disables the fingerprint verification.
//espClient.setInsecure();

client.setServer(mqtt_server, 8883);
client.setCallback(callback);

}

void loop() {

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

unsigned long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
++value;
snprintf (msg, MSG_BUFFER_SIZE, “hello world #%ld”, value);
Serial.print("Publish message: ");
Serial.println(msg);
client.publish(“outTopic”, msg);
}
}

1 Like

FYI as i mentioned initially the fingerprint has changed by now and my device has stopped working due to this reason, so i’m including a small snippet here for complete-ness how i updated my code to work by verifying the root CA instead of using the fingerprint directly (this should practically work forever from now on)

// Define the root CA PEM cert (this is the new ISRG Root X1)
static const char *root_ca PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";
  // Remove this:
  // client.setFingerprint(fingerprint);
  X509List cert(root_ca);
  client.setTrustAnchors(&cert);

Hi,
my device has stopped working too.
How to create the root_ca file for my broker url?
The above not working with ESP8266 and pubsubclient library

Hi,
Check this out!

Basically:
“openssl s_client -showcerts -verify 5 -connect your_id.s1.eu.hivemq.cloud:8883” and the last cert from the chain.

Sorry for the late reply, missed the message somehow. I also updated my code to use the root CA

Here’s a minimal snippet of what you have to change

This uses the X1 root CA from letsencrypt. This should work for any hivemq cloud cluster and also work after the root cert change in letsencrypt.


static const char *root_ca PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";

// in loop()
// Instead of client.setFingerPrint();
  X509List cert(root_ca);
  client.setTrustAnchors(&cert);

Hi Simon,

I have tried the same code but could not succeed.

FYI, I also changed the fingerprint keys (copied from console.hivemq.cloud, under SHA-1 Fingerprint).

Below are the steps I followed

  1. Copied your code
  2. Edit userrname, password, Fingerprint, mqtt_server, wifi cred, topic.
  3. Upload to ESP8266

OUTPUT
01:53:05.117 → Connection failed
01:53:05.117 → Retrying MQTT connection in 5 seconds…
01:53:10.731 → Connection failed
01:53:10.731 → Retrying MQTT connection in 5 seconds…

Kindly please help me in solving this issue.

Thanks in advance.

Hi Aman,

on the description of this video tutorial: Virtuino MQTT + HiveMQ - YouTube
you can find an example code for secure connection to ESP32, that I have tested and works fine.
Regards!