MQTT Client Using Paho Cpp

With the IoT revolution, software has become a key component. Developing software products that are: lightweight, efficient, and scalable requires the proper tools such as MQTT and C++.

This post describes how to install libraries, configure the CMake and build a simple C++ client that can post a literal text message to a cloud MQTT broker.

Why?

So what is MQTT, and why choose C++?

MQTT [1] is a publishing/subscribe messaging protocol that is lightweight and efficient, scalable, reliable, secure, and well-supported [2], and ideal for connecting remote devices with a small code footprint and minimal network bandwidth.

C++ is a general-purpose programming, which is the third most popular language[3] among the most energy-efficient programs [4] with a large set of libraries from the standard library and Boost and wide hardware support for high performance such as the Cuda library or the Intel Library

For further information regarding MQTT check the MQTT essentials

Tutorial

For this tutorial, we use C++ 17, CMake, and the Paho MQTT CPP Client. https://ports.macports.org/port/paho.mqtt.cpp/. Is assumed some C++ and CMake experience. The complete code can be found at https://gitlab.com/hurtadosanti/MqttClient.

Install the Libraries

This requires three steps on an ubuntu 22.04 machine: 1) Install the build libraries, 2) Install Paho for C and 3) Install Paho for C++.

NOTE: In a Mac, MacPorts provides the Paho MQTT CPP packet

  1. Install the compilation essentials

    sudo apt install build-essential cmake git pkg-config gcc g++ libssl-dev
    
  2. Clone build and install the Eclipse Paho C Client Library for the MQTT Protocol.

    git clone https://github.com/eclipse/paho.mqtt.c.git
    cd paho.mqtt.c
    cmake -Bbuild -H. -DPAHO_ENABLE_TESTING=OFF -DPAHO_BUILD_STATIC=ON -DPAHO_WITH_SSL=ON -DPAHO_HIGH_PERFORMANCE=ON
    sudo cmake --build build/ --target install
    sudo ldconfig
    
  3. Clone, build and install the Eclipse Paho MQTT C++ Client Library.

    cd ~/software
    git clone https://github.com/eclipse/paho.mqtt.cpp
    cd paho.mqtt.cpp
    cmake -Bbuild -H. -DPAHO_BUILD_STATIC=ON -DPAHO_BUILD_DOCUMENTATION=FALSE -DPAHO_BUILD_SAMPLES=FALSE
    sudo cmake --build build/ --target install
    sudo ldconfig
    

Configure CMake

The required Libraries are Threads and the PahoMqtt.

set(CMAKE_CXX_STANDARD 17)
find_package(Threads REQUIRED)
find_package(PahoMqttCpp REQUIRED)

Create a HiveMQ Cloud account

Create and get the credentials from https://console.hivemq.cloud

Write the Client

To write the client we use the Paho Async Client.

First, we write the callback to handle the messages

class LiteralCallback : public virtual mqtt::callback,
                        public virtual mqtt::iaction_listener {
private:
    mqtt::async_client &client;
    const std::string topic;
public:
    LiteralCallback(mqtt::async_client &client, std::string &topic) : client(client), topic(topic) {
    }

    void connected(const mqtt::string &string) override {
        callback::connected(string);
        mqtt::subscribe_options options;
        mqtt::properties properties;
        client.subscribe(topic, 0, options, properties);
        std::cout << "Subscribed to topic"<<topic<< std::endl;
    };

    void message_arrived(mqtt::const_message_ptr message) override {
        callback::message_arrived(message);
        std::cout << "message arrived on topic: " << message->get_topic() << ", and message: " << message->get_payload()
                  << std::endl;

    };

    void on_failure(const mqtt::token &asyncActionToken) override {
        std::cout << "on failure" << std::endl;
        if (asyncActionToken.get_message_id() != 0)
            std::cout << " for token: [" << asyncActionToken.get_message_id() << "]" << std::endl;
        std::cout << std::endl;

    }

    void on_success(const mqtt::token &asyncActionToken) override {
        std::cout << "on success" << std::endl;
    }


    ~LiteralCallback() override = default;
};

Afterward, write the client and pass the cloud credentials through program arguments.

int main(int argc, char *argv[]) {

    const std::string clientId("cloud_client_2");
    std::string topicName("test");
    const int qos = 1;

    std::string username;
    std::string password;
    std::string serverAddress;

    if (argc > 3) {
        username = argv[1];
        password = argv[2];
        serverAddress = argv[3];
    } else {
        std::cerr << "Broker Settings should be set" << std::endl;
        return 1;
    }

    try {

        mqtt::async_client cli(serverAddress, clientId);
        mqtt::connect_options connectOptions(username, password);
        mqtt::ssl_options sslOptions;
        connectOptions.set_ssl(sslOptions);

        LiteralCallback cb(cli, topicName);
        cli.set_callback(cb);

        auto connection = cli.connect(connectOptions, nullptr, cb);
        connection->wait();

        mqtt::topic topic(cli, topicName, qos);

        auto publishToken = topic.publish("Cloud Client Testing");
        std::cout<<"Message send"<<std::endl;
        publishToken->wait();
        std::cout<<"Message received"<<std::endl;
        cli.disconnect()->wait();
    }
    catch (const mqtt::exception &exc) {
        std::cerr << exc << std::endl;
        return 2;
    }

    return 0;
}

Compile and Build

To compile, create a build folder for the binaries and run the cmake task.

   mkdir build && cd build
   cmake ../

When successful build the binary using make.

   make

Run a test

Finally, execute the binary mqtt_client passing the three program arguments in the correct order.

mqtt_client <username> <password> ssl://<url>

Now you should see the following:

on success
on success
Subscribed to topictest
Message send
message arrived on topic: test, and message: Cloud Client Testing
Message received

References