1.代码实现

1.主函数代码段

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include "mosquitto.h"
#include "cJSON.h"
#include "dictionary.h"
#include "iniparser.h"
#include "mqtt_config.h"

#define LEN_T           64
#define LEN_ID          20
#define PATH            "./Mqtt.ini"
#define KEEPALIVE       60

void connect_callback(struct mosquitto *mosq, void *obj, int rc);
int get_tm(char* localt, int size);
int get_dev(char *id,int len,int sn);
int get_temperature(float *temp);

int main(int argc,char **argv)
{
        int                                     rv = 0;
        struct mosquitto        *mosq = NULL;
        void                            *obj=NULL;
        mqtt_user_t                     mqtt;
    memset(&mqtt, 0, sizeof(mqtt));
    get_config(PATH, &mqtt);

    rv = mosquitto_lib_init();
    if( rv < 0 )
    {
            printf("mosquitto init failed:%s\n",strerror(errno));
            goto Cleanup;
    }

    mosq = mosquitto_new(mqtt.clientid, true, (void *)&mqtt);
    if( !mosq )
    {
            printf("create new mosq failed:%s\n",strerror(errno));
            goto Cleanup;
    }

    rv = mosquitto_username_pw_set(mosq, mqtt.username, mqtt.password);
    if( rv != MOSQ_ERR_SUCCESS )
    {
            printf("set username and password failed:%s\n",strerror(errno));
            goto Cleanup;
    }
    printf("create mosquitto successfully");

    mosquitto_connect_callback_set(mosq, connect_callback);

    while(1)
    {
            rv = mosquitto_connect(mosq, mqtt.brokeraddress, mqtt.brokerport, KEEPALIVE);
            if( rv != MOSQ_ERR_SUCCESS )
            {
                    printf("connect to broker failed:%s\n",strerror(errno));
                    goto Cleanup;
            }
            printf("connect to broker ok\n");
            mosquitto_loop_forever(mosq,-1,1);
            sleep(5);
    }
    Cleanup:
        mosquitto_lib_cleanup();
        mosquitto_destroy(mosq);
            return 0;
}
/*  Description:Get id of the device */
int get_dev(char *id,int len,int sn)
{
        int                     ret;
    if( !id || len<LEN_ID )
    {
            printf("Invalid input arugments\n");
            return -1;
    }

    ret=snprintf(id,len,"%05d",sn);
    if(ret<0 || ret>=len)
    {
            return -1;
    }
    return 0;
}
/*  Description:Get current time */
int get_tm(char* localt, int size)
{
        time_t          seconds;
        struct tm   *local;
        int                     ret;
        time(&seconds);

    local=localtime(&seconds);
    if(local==NULL)
    {
            return -2;
    }

    ret=snprintf(localt,size,"%d/%d/%d-%d:%d:%d",local->tm_year+1900,local->tm_mon+1,local->tm_mday,local->tm_hour,local->tm_min,local->tm_sec);

    if(ret<0||ret>=64)
    {
            return -1;
    }
    return 0;
}
/* Description:get temperature */
int get_temperature(float *temp)
{
        int                             fd=-1;
        char                            buf[128];
        char                            *ptr=NULL;
        DIR                             *dirp=NULL;
        struct dirent                           *direntp=NULL;
        char                            w1_path[64]="/sys/bus/w1/devices/";
        char                            chip_sn[32];
        int                             found=0;
        dirp=opendir(w1_path);
    if(!dirp)
    {
            printf("Open folder %s failure:%s\n",w1_path,strerror(errno));
            return -1;
    }

    while(NULL!=(direntp=readdir(dirp)))
    {
            if(strstr(direntp->d_name,"28-"))
            {
                    strncpy(chip_sn,direntp->d_name,sizeof(chip_sn));
                    found=1;
            }
    }

    closedir(dirp);

    if(!found)
    {
            printf("Cannot find ds18b20 chipset\n");
            return -2;
    }

    strncat(w1_path,chip_sn,sizeof(w1_path)-strlen(w1_path));
    strncat(w1_path,"/w1_slave",sizeof(w1_path)-strlen(w1_path));

    if((fd=open(w1_path,O_RDONLY))<0)
    {
            printf("Open file failure:%s\n",strerror(errno));
            goto cleanup;
            return -3;
    }

    memset(buf,0,sizeof(buf));
    if(read(fd,buf,sizeof(buf))<0)
    {
            printf("Read data from fd[%d] failure:%s\n",fd,strerror(errno));
            goto cleanup;
            return -4;
    }

    ptr=strstr(buf,"t=");
    if(!ptr)
    {
            printf("Cannot find t= string\n");
            goto cleanup;
            return -5;
    }

    ptr +=2;
    *temp=atof(ptr)/1000;
    cleanup:
        if(fd)
                close(fd);
        if(!dirp)
                closedir(dirp);
                return 0;
}    
void connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
        float           temp;
        char            local_t[LEN_T];
        int                     size=sizeof(local_t);
        char            id[LEN_ID];
        int                     sn=1;
        cJSON           *root;
        cJSON           *item;
        mqtt_user_t     *mqtt;
        char            *msg;
        printf("connection successful cJSON cal packaging\n");

    if( get_temperature(&temp) < 0 )
    {
            printf("get temperature failed:%s\n",strerror(errno));
    }

    if( get_tm(local_t,size) < 0 )
    {
            printf("get time failed:%s\n",strerror(errno));
    }

    if( get_dev(id, LEN_ID, sn) < 0 )
    {
            printf("get device id failed:%s\n",strerror(errno));
    }

    mqtt = (mqtt_user_t *)obj;

    root = cJSON_CreateObject();
    item = cJSON_CreateObject();

    cJSON_AddItemToObject(root, "method", cJSON_CreateString(mqtt->method));
    cJSON_AddItemToObject(root, "version", cJSON_CreateString(mqtt->version));
    cJSON_AddItemToObject(item, "CurrentTemperature", cJSON_CreateNumber(temp));
    cJSON_AddItemToObject(root, "params", item);
    cJSON_AddItemToObject(root, "time", cJSON_CreateString(local_t));
    cJSON_AddItemToObject(root, "device id", cJSON_CreateString(id));

    msg = cJSON_Print(root);
    printf("%s\n",msg);

    if( !rc )
    {
            if( mosquitto_publish(mosq, NULL, mqtt->topic, strlen(msg), msg, mqtt->Qos, true) != MOSQ_ERR_SUCCESS )
            {
                    printf("publish topic failed:%s\n",strerror(errno));
            }
    }
    mosquitto_disconnect(mosq);
    return ;
}

2.mqtt_config.c(读取配置文件数据代码段)

int     get_config(char *path,mqtt_user_t *mqtt)
{
        dictionary              *ini = NULL;
        const char              *brokeraddress;
        int                             brokerport;
        int                             id;
        const char              *username;
        const char              *password;
        const char              *clientid;
        const char              *topic;
        int                             Qos;
        const char              *method;
    const char              *time;
    const char              *jsonid;
    const char              *identifier;
    const char              *version;

    if( !path || !mqtt )
    {
            printf("invalid_argument:%s\n",__func__);
            return -1;
    }

    ini = iniparser_load(path);
    if( !ini )
    {
            printf("iniparser_load failure\n");
            return -2;
    }

    brokeraddress = iniparser_getstring(ini, "server:hostname", DEFAULT_BROKER_ADDRESS);
    brokerport = iniparser_getint(ini, "server:port", DEFAULT_BROKER_PORT);
    username = iniparser_getstring(ini, "user_name_pwd:username", DEFAULT_USERNAME);
    password = iniparser_getstring(ini, "user_name_pwd:pwd", DEFAULT_PASSWORD);
    clientid = iniparser_getstring(ini, "clientid:clientid", DEFAULT_CLIENTID);
    identifier = iniparser_getstring(ini, "json:identifier", DEFAULT_JSONIDTF);
    topic = iniparser_getstring(ini, "pub_topic:topic", DEFAULT_PUB_TOPIC);
    method = iniparser_getstring(ini, "json:method", DEFAULT_METHOD);
    Qos = iniparser_getint(ini, "Qos:Qos", DEFAULT_Qos);
    //id = iniparser_getstring(ini, "json:id", DEFAULT_ID);
    version = iniparser_getstring(ini, "json:version", DEFAULT_VERSION);

    strncpy(mqtt->brokeraddress, brokeraddress, SIZE);
    mqtt->brokerport = brokerport;
    strncpy(mqtt->username, username, SIZE);
    strncpy(mqtt->password, password, SIZE);
    strncpy(mqtt->clientid, clientid, SIZE);
    strncpy(mqtt->identifier, identifier, SIZE);
    strncpy(mqtt->topic, topic, SIZE);
    strncpy(mqtt->method, method, SIZE);
    strncpy(mqtt->version, version, SIZE);
    mqtt->Qos = Qos;
    iniparser_freedict(ini);

    return 0;
    }

3.配置文件代码段

[server]
hostname    =   iot-06z00htt4df99dk.mqtt.iothub.aliyuncs.com
port        =   1883

[user_name_pwd]
username        =      
pwd                     =     
[clientid]
clientid                        =      
[pub_topic]
topic           =       

[json]
method          =       thing.event.property.post
id                      =       11
identifier      =       Temperature
version         =       1.0.0

[keepalive]
keepalive       =       60

[Qos]
Qos                     =       0

4.mqtt_config.h

#ifndef         _MQTT_CONFIG_H_
#define         _MQTT_CONFIG_H_

#define SIZE    1024
#define DEFAULT_BROKER_ADDRESS          "iot-06z00htt4df99dk.mqtt.iothub.aliyuncs.com"
#define DEFAULT_BROKER_PORT                     1883
#define DEFAULT_USERNAME                        "temp&k16b1aH4UjO"
#define DEFAULT_PASSWORD                        "924000ba040582d759c3d1b41f209745b6d1b74bea10120ccdffd846794fc8c1"
#define DEFAULT_CLIENTID                        "k16b1aH4UjO.temp|securemode=2,signmethod=hmacsha256,timestamp=1714134288886|"
#define DEFAULT_METHOD                          "thing.event.property.post"
#define DEFAULT_VERSION                         "1.0.0"
#define DEFAULT_Qos                                     0
#define DEFAULT_JSONIDTF                        "CurrentTemperature"
#define  DEFAULT_PUB_TOPIC                      "/sys/k16b1aH4UjO/temp/thing/event/property/post"

typedef struct mqtt_user_s
{
        char            brokeraddress[SIZE];
        int                     brokerport;
        char            username[SIZE];
        char            password[SIZE];
        char            clientid[SIZE];
        char            identifier[SIZE];
        char            topic[SIZE];
        char            method[SIZE];
        char            version[SIZE];
        int                     Qos;
//      int                     id;
}mqtt_user_t;

extern int get_config(char *path,mqtt_user_t *mqtt);

#endif

2.mosquitto库函数使用

1.int mosquitto_lib_init(void)

function:

Must be called before any other mosquitto functions.This function is not thread safe.

returns:

MOSQ_ERR_SUCCESS on success.
MOSQ_ERR_UNKNOWN on Windows, if sockets couldn’t be initialized.

2.struct mosquitto *mosquitto_new(char *id, bool clean_session, (void *)obj)

function:

Create a new mosquitto client instance

parrameters:

id String to use as the client id. If NULL, a random client id will be generated. If id is NULL, clean_session must be true.
clean_session set to true to instruct the broker to clean all messages and subscriptions on disconnect, false to instruct it to keep them. See the man page mqtt(7) for more details. Note that a client will never discard its own outgoing messages on disconnect. Calling mosquitto_connect or mosquitto_reconnect will cause the messages to be resent. Use mosquitto_reinitialise to reset a client to its original state. Must be set to true if the id parameter is NULL.
obj A user pointer that will be passed as an argument to any callbacks that are specified.

returns:

Pointer to a struct mosquitto on success. NULL on failure. Interrogate errno to determine the cause for the failure:

  • ENOMEM on out of memory.
  • EINVAL on invalid input parameters.

3.int mosquitto_username_pw_set(mosquitto *mosq,char *username,char *password)

function:

Configure username and password for a mosquitto instance. By default, no username or password will be sent. For v3.1 and v3.1.1 clients, if username is NULL, the password argument is ignored.

This is must be called before calling mosquitto_connect.

parameters:

mosq a valid mosquitto instance.
username the username to send as a string, or NULL to disable authentication.
password the password to send as a string. Set to NULL when username is valid in order to send just a username.

returns:

MOSQ_ERR_SUCCESS on success.
MOSQ_ERR_INVAL if the input parameters were invalid.
MOSQ_ERR_NOMEM if an out of memory condition occurred.

4.int mosquitto_connect(mosquitto *mosq,char *host,int port,int keepalive)

function:

Connect to an MQTT broker.

It is valid to use this function for clients using all MQTT protocol versions.

parrameters:

mosq a valid mosquitto instance.
host the hostname or ip address of the broker to connect to.
port the network port to connect to. Usually 1883.
keepalive the number of seconds after which the broker should send a PING message to the client if no other messages have been exchanged in that time.

returns:

MOSQ_ERR_SUCCESS on success.
MOSQ_ERR_INVAL if the input parameters were invalid, which could be any of:
  • mosq == NULL
  • host == NULL
  • port < 0
  • keepalive < 5
MOSQ_ERR_ERRNO if a system call returned an error. The variable errno contains the error code, even on Windows. Use strerror_r() where available or FormatMessage() on Windows.

5.int mosquitto_loop_start(mosquitto *mosq)

function:

This is part of the threaded client interface. Call this once to start a new thread to process network traffic. This provides an alternative to repeatedly calling mosquitto_loop yourself.

returns:

MOSQ_ERR_SUCCESS on success.
MOSQ_ERR_INVAL if the input parameters were invalid.
MOSQ_ERR_NOT_SUPPORTED if thread support is not available.

6.int mosquitto_loop_forever(mosquitto *mosq,int time,int max_packects)

function:

This function call loop() for you in an infinite blocking loop. It is useful for the case where you only want to run the MQTT client loop in your program.

It handles reconnecting in case server connection is lost. If you call mosquitto_disconnect() in a callback it will return.

parameters:

mosq a valid mosquitto instance.
timeout Maximum number of milliseconds to wait for network activity in the select() call before timing out. Set to 0 for instant return. Set negative to use the default of 1000ms.
max_packets this parameter is currently unused and should be set to 1 for future compatibility.

returns:

MOSQ_ERR_SUCCESS on success.
MOSQ_ERR_INVAL if the input parameters were invalid.
MOSQ_ERR_NOMEM if an out of memory condition occurred.
MOSQ_ERR_NO_CONN if the client isn’t connected to a broker.
MOSQ_ERR_CONN_LOST if the connection to the broker was lost.
MOSQ_ERR_PROTOCOL if there is a protocol error communicating with the broker.
MOSQ_ERR_ERRNO if a system call returned an error. The variable errno contains the error code, even on Windows. Use strerror_r() where available or FormatMessage() on Windows.

7.int mosquitto_disconnect(mosquitto *mosq)

function:

Disconnect from the broker.

It is valid to use this function for clients using all MQTT protocol versions.

returns:

MOSQ_ERR_SUCCESS on success
MOSQ_ERR_INVAL if the input parameters were invalid.
MOSQ_ERR_NO_CONN if the client isn’t connected to a broker.

8.int mosquitto_lib_cleanup(void)

function:

Call to free resources associated with the library.

returns:

MOSQ_ERR_SUCCESS always

void mosquitto_destroy(struct mosquitto *mosq)

function:

Use to free memory associated with a mosquitto client instance.

parameter:

mosq:a struct mosquitto pointer to free

3.cJSON库函数的使用

·cJSON

cJSON 是一个轻量级的 JSON 解析器/生成器库,用于 C 语言。它提供了一组简单而有效的 API,用于解析 JSON 数据、生成 JSON 数据以及操作 JSON 数据结构。该库的设计目标是简单、轻量级和易于使用,适用于嵌入式系统和其他资源受限的环境。

·特点

以下是 cJSON 库的一些主要特点:

  1. 轻量级:cJSON 库的代码库非常小巧,只包含了少量的源文件,因此很容易嵌入到你的项目中而不会增加太多的额外开销。
  2. 简单易用:cJSON 提供了一组简单直观的 API,使得解析和生成 JSON 数据变得非常容易。它的 API 设计类似于 DOM 树的操作,让开发者可以轻松地遍历和修改 JSON 数据结构。
  3. 跨平台性:cJSON 可以在各种不同的平台上运行,包括 Windows、Linux、macOS 等。它不依赖于任何特定的操作系统或硬件平台。
  4. 支持标准的 JSON 数据格式:cJSON 库支持 JSON 数据的标准格式,包括对象、数组、字符串、数字、布尔值和 null 值等。它可以正确地解析和生成符合 JSON 规范的数据。
  5. 开放源代码:cJSON 是开放源代码的,你可以免费获取其源代码,并根据需要进行修改和定制。
    总的来说,cJSON 是一个简单、轻量级、易于使用的 JSON 解析器/生成器库,非常适合在 C 语言项目中处理 JSON 数据。

1.void cJSON_AddItemToArray(cJSON *array, cJSON*item)

​ 函数的作用是将一个 cJSON 对象添加到 cJSON 数组中。让我解释一下它的工作原理:

  1. 参数cJSON_AddItemToArray 函数接受两个参数,第一个参数是目标 cJSON 数组,第二个参数是要添加到数组中的 cJSON 对象。
  2. 工作原理:当你调用 cJSON_AddItemToArray 函数时,它会将第二个参数(即要添加的 cJSON 对象)添加到第一个参数(即目标 cJSON 数组)的末尾。这样,目标数组就包含了新添加的 cJSON 对象。
  3. 数组扩展:如果目标数组在调用 cJSON_AddItemToArray 函数时还不存在,这个函数会创建一个新的 cJSON 数组,并将要添加的 cJSON 对象作为第一个元素添加进去。
  4. 内存管理:需要注意的是,cJSON_AddItemToArray 函数并不会复制 cJSON 对象本身,而只是将对该对象的引用添加到数组中。这意味着,如果你后续修改了原始 cJSON 对象,那么数组中对应的元素也会相应地发生变化。

综上所述,cJSON_AddItemToArray 函数的工作原理很简单,它负责将 cJSON 对象添加到 cJSON 数组中,并确保 cJSON 数组能够正确地管理和存储这些对象。

2.cJSON *cJSON_CreateObject()

创建的 cJSON 对象是一个 JSON 对象,它可以存储很多键值对数据。JSON 对象是一种键值对的集合,每个键值对由一个键(字符串)和一个值(可以是任何有效的 JSON 数据类型)组成。

因此,你可以使用 cJSON_AddItemToObject() 函数将键值对添加到 cJSON_CreateObject() 创建的对象中,以便存储更多的值。每个键都必须是唯一的,但值可以是任何有效的 JSON 数据类型,包括字符串、数字、数组、对象等。

例如,你可以创建一个 cJSON 对象来表示一个人的信息,然后使用 cJSON_AddItemToObject() 函数将名字、年龄、邮箱等信息作为键值对添加到这个 cJSON 对象中,以便将这些信息转换为 JSON 格式。

3.cJSON *cJSON_CreateString(const char *string)

它接受一个参数 string,这个参数是一个以 null 结尾的 C 字符串,表示要转换的字符串内容。然后,它会返回一个新的 cJSON 字符串对象,其中包含了相同的字符串内容。

这个函数的工作原理很简单:它会创建一个 cJSON 字符串对象,并将传入的 C 字符串内容复制到这个 cJSON 对象中。这个 cJSON 对象可以作为 JSON 数据结构的一部分,例如,可以将它添加到 cJSON 对象或 cJSON 数组中,然后使用 cJSON 库提供的函数将整个 JSON 数据结构转换为字符串输出。

例如,假设你有一个 C 字符串 char *name = "Tom",你可以使用 cJSON_CreateString(name) 来创建一个 cJSON 字符串对象,表示 "Tom" 这个字符串。然后,你可以将这个 cJSON 字符串对象添加到 cJSON 对象中,并最终将整个 cJSON 对象转换为 JSON 字符串输出。

4.void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);

cJSON_AddItemToObject() 是 cJSON 库中用于向 cJSON 对象中添加键值对数据的函数之一。它的作用是向指定的 cJSON 对象中添加一个键值对,其中键是一个字符串,值是一个 cJSON 对象。

它接受三个参数:

  • object: 要添加键值对的 cJSON 对象。
  • string: 键的字符串表示,即要添加的键名。
  • item: 要添加的值,是一个 cJSON 对象,可以是任何 cJSON 支持的数据类型,如 cJSON 数组、cJSON 对象、cJSON 字符串等。

这个函数的工作原理是将键值对添加到指定的 cJSON 对象中。它会在 cJSON 对象的内部数据结构中创建一个新的键值对节点,其中键是 string 参数表示的字符串,值是 item 参数表示的 cJSON 对象。

举个例子,假设你有一个 cJSON 对象 cJSON *person = cJSON_CreateObject(),你可以使用 cJSON_AddItemToObject(person, "name", cJSON_CreateString("Tom")) 将一个名为 "name",值为 "Tom" 的键值对添加到这个 cJSON 对象中。然后,你可以继续使用类似的方式向这个 cJSON 对象中添加更多的键值对数据。

5.void cJSON_Delete(cJSON *c);

是 cJSON 库中用于删除 cJSON 对象的函数。它的作用是释放由 cJSON 库动态分配的内存,并将传入的 cJSON 对象以及其所有子对象都销毁。

它接受一个参数 c,这个参数是一个指向 cJSON 对象的指针,表示要删除的 cJSON 对象。

这个函数的工作原理是递归地遍历 cJSON 对象的内部数据结构,并释放每个节点的内存。如果一个 cJSON 对象包含子对象(比如 cJSON 数组或 cJSON 对象),那么这个函数会继续递归地删除这些子对象,直到整个 cJSON 对象及其所有子对象都被销毁。

使用 cJSON_Delete() 函数是非常重要的,特别是在你不再需要一个 cJSON 对象时。这可以确保释放 cJSON 库所分配的内存,防止内存泄漏。通常,在使用 cJSON 库创建的 cJSON 对象不再需要时,都应该调用 cJSON_Delete() 函数来销毁它们。

4.配置文件函数

1.dictionary * iniparser_load(const char * ininame)

function:

加载ini文件,将数据存于dictionary结构中

returns:

返回一个dictionary结构体指针

2.char *iniparser_getstring(const dictionary * ini, const char * key, const char * def)

iniparser_getstring 函数用于从配置文件中获取指定键的字符串值。其参数包括:

  • ini:指向已加载的配置文件的 dictionary 结构体的指针。
  • 第二个参数是一个字符串,表示要获取的键。它的格式通常是 "section:key",其中 section 是区段名称,key 是键名。
  • 第三个参数是当键不存在时返回的默认字符串值。

这个函数返回的是键对应的字符串值。如果键不存在,则返回指定的默认字符串值。

例如,iniparser_getstring(ini, "server:hostname", DEFAULT_BROKER_ADDRESS) 的作用是从配置文件中查找名为 "hostname" 的键,位于名为 "server" 的区段中,如果找到则返回其字符串值,如果找不到则返回 DEFAULT_BROKER_ADDRESS 所指定的默认字符串值。

3.void iniparser_freedict(dictionary * ini)

function:

release the dictionary structure