A Minimalist MQTT Interface EasyMqttClient

Click on the “Embedded Application Research Institute” above, and select “Top/Star Official Account

Useful Resources Delivered First Hand!

Source | Embedded Application Research Institute

Compiled & Formatted | Embedded Application Research Institute

In the development of IoT-related applications, MQTT is used to some extent. The following open-source project is an interface I encapsulated based on the mqttclient project by the great Jiejie:

https://github.com/Yangyuanxin/EasyMqttClient

A Minimalist MQTT Interface EasyMqttClient

The mqttclient project by Jiejie:

https://github.com/jiejieTop/mqttclient

A Minimalist MQTT Interface EasyMqttClient

Before encapsulation, I used a memory leak tool to locate and troubleshoot issues with mqtt_release and SALOF_LOG. Fortunately, the mqtt_release scenario is rarely used, but it is still necessary to mention:

  • Bug1 (Memory Leak in platform_thread)

The function platform_thread_destroy in the mqttclient/platform/linux/platform_thread.c file does not free the memory allocated for thread in the thread encapsulation function, which causes a memory leak when mqttclient calls the mqtt_release function.

  • Bug2 (To be resolved – can be fixed by disabling macro definitions)

The mqttclient/common/log module has a memory leak, which can be resolved by disabling MQTT_LOG_IS_SALOF in the mqtt_config.h file.

  • Bug3 (To be resolved – can be temporarily ignored)

When executing mqtt_release, there is a chance of encountering a core dump, but the mqtt_release scenario is not commonly seen in general product development unless there is a specific need. For details, see the Issues I raised:

https://github.com/jiejieTop/mqttclient/issues/60

Aside from that, using mqttclient is quite pleasant, and many open-source projects and actual products have utilized this interface, making it very stable.

Here are the 7 APIs of my encapsulated interface, which are very simple! They are:

//MQTT Initialization
EasyMqttClient_t *EasyMqttInit(EasyMqttAttr_t *Attr);
//MQTT De-initialization
int EasyMqttUnInit(EasyMqttClient_t *Client);
//MQTT Connection
int EasyMqttConnect(EasyMqttClient_t *Client);
//MQTT Disconnection
int EasyMqttDisConnect(EasyMqttClient_t *Client);
//MQTT Topic Subscription
int EasyMqttSubscribe(EasyMqttClient_t *Client, const char *Topic, enum EasyMqttQos_t Qos, 
                    void (*Cb)(const char *Topic,char* Data,unsigned short Len));
//MQTT Unsubscription
int EasyMqttUnsubscribe(EasyMqttClient_t *Client, const char *Topic);
//MQTT Topic Publishing
int EasyMqttPublish(EasyMqttClient_t *Client, const char *Topic, enum EasyMqttQos_t Qos, char *Data, unsigned short Len);

The EasyMqttInit function encapsulates these trivial processes, such as setting the URL and port number, into a structure EasyMqttAttr:

typedef struct EasyMqttAttr
{
    char *Url;
    char *Port;
    char *ClientId;
    char *Username;
    char *Password;
}EasyMqttAttr_t;

//......................
mqtt_set_host;
mqtt_set_port;
mqtt_set_client_id;
mqtt_set_user_name;
mqtt_set_password;
mqtt_set_clean_session;
//When calling, it's very simple
//1.
//2.Define a structure variable
example: 
EasyMqttClient_t *Client = NULL;
EasyMqttAttr_t Attr = 
{
   .Url      = "192.168.4.248",
   .Port     = "30157",
   .ClientId = "EasyMqttMqtt",
   .Username = "EasyMqtt",
   .Password = "123456"
};
//3.Call the EasyMqttInit function
Client = EasyMqttInit(Client, &Attr);
//to do

//Implement your MQTT connection, subscription, distribution logic

//to do end

Additionally, it implements separate handling of callback functions for different subscribed Topics, making the development logic clearer and easier to debug and troubleshoot. This mechanism is based on an array of structures, as shown below:

struct TopicHandler_t
{
    //Topic
    const char *Topic;
    //Callback function corresponding to the Topic
    void (*CallBack)(const char *Topic,char* Data,unsigned short Len);
};
//Structure array table, maximum support for handling Topics is MAX_TOPIC, which defaults to 64
struct TopicHandler_t Table[MAX_TOPIC];

When calling the EasyMqttSubscribe Topic subscription function to subscribe to a Topic, this Topic and its callback will be added to this table. When mqttclient receives different Topics, it will look up the table to call the corresponding callback function for different Topics. The specific logic is as follows:

//Topic callback trigger
static void TopicHandlerCallBack(void* client, message_data_t* Msg)
{
    (void)client;
    int Index = 0;
    char *Topic = Msg->topic_name;
    unsigned short Len = Msg->message->payloadlen;
    char *Data  = (char *)Msg->message->payload;
    //Locking
    pthread_mutex_lock(&Mutex);
    //When receiving different Topics, find the corresponding callback function based on the Topic and call it
    for(Index = 0; Index < sizeof(Table)/sizeof(Table[0]); Index++)
    {
        if(0 == strcmp(Msg->topic_name,Table[Index].Topic))
        {
            Table[Index].CallBack(Topic,Data,Len);
            break;
        }
    }
    //Unlocking
    pthread_mutex_unlock(&Mutex);
}

//EasyMqttSubscribe Topic subscription function
int EasyMqttSubscribe(EasyMqttClient_t *Client, const char *Topic, enum EasyMqttQos_t Qos, 
                    void (*Cb)(const char *Topic, char* Data, unsigned short Len))
{
    if(Index > MAX_TOPIC-1)
    {
        printf("Exceeds the maximum number of topics set:%d!\n", Index);
        return -1;
    }
    Table[Index].Topic = Topic;
    Table[Index].CallBack = Cb;
    Index++;
    return mqtt_subscribe(Client, Topic, (mqtt_qos_t)Qos, TopicHandlerCallBack);
}

For specific usage methods, please refer to the EasyMqtt.c file’s EasyMqttTest function. Currently, this project has only been tested on Linux projects; further testing will be conducted in different RTOS environments. Continuous attention is welcomed, and PRs are also welcome to make embedded MQTT application development simpler together.

Using this project in a Linux environment:

  • 1. Clone this project
git clone https://github.com/Yangyuanxin/EasyMqttClient.git
  • 2. Modify the cross-compilation toolchain (default is gcc)

If you want to run it on an embedded platform, you need to modify the Makefile as follows:

CROSS_COMPILE = 

Otherwise, it will compile in the default gcc environment.

  • 3. Compile
make
  • 4. Execute
./a.out

Other environments: to be tested.

Previous Highlights

Hands-on open-source tutorials based on HarmonyOS projects

Sharing some highly-rated open-source projects related to embedded systems on GitHub

Open Source: AliOS_Things_Developer_Kit revival plan

An amazing open-source interpretation project [Linux Kernel Unveiling], definitely don’t miss it!

If you find this article helpful, please click <span>[Looking]</span> and share it, it is also a support for me.

Leave a Comment