前言
Mosquitto库是一个开源的消息代理项目,用于实现MQTT(Message Queuing Telemetry Transport)协议。MQTT是一种轻量级的通信协议,适用于低带宽、高延迟或不稳定网络环境下的物联网设备通信。Mosquitto库提供了MQTT协议的服务器和客户端实现,可以用于构建物联网应用和设备之间的通信。Mosquitto库支持多种平台和编程语言,并提供了丰富的功能和灵活的配置选项。
一、MQTT简介
MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议;Mosquitto是一个实现了MQTT3.1协议的代理服务器,由MQTT协议创始人之一的Andy Stanford-Clark开发,应用广泛的低功耗传感器,手机、嵌入式计算机、微型控制器等移动设备
二、Mosquitto安装
1.Ubuntu下安装mosquitto
-
wget下载源码包
wget http://mosquitto.org/files/source/mosquitto-1.5.5.tar.gz
2.解压
tar -xzvf mosquitto-1.5.5.tar.gz
3.进入目录,编译下载安装
cd mosquitto-1.5.5/
make
sudo make install
make出现 fatal error: openssl/opensslconf.h: No such file or directory
输入以下命令安装
重新make
若出现错误,看第四点
4.注意事项:
可能出现问题及解决方法
【1】编译找不到openssl/ssl.hsudo apt-get install libssl-dev
【2】编译过程g++命令未找到:sudo apt-get install g++
【3】编译过程找不到ares.hsudo apt-get install libc-ares-dev
【4】编译过程找不到uuid/uuid.hsudo apt-get install uuid-dev
【5】使用过程中找不到libmosquitto.so.1 error while loading shared libraries: libmosquitto.so.1: cannot open shared object file:No such file or directory
【解决方法】——修改libmosquitto.so位置
创建链接sudo ln -s /usr/local/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1
更新动态链接库sudo ldconfig
2.测试mosquitto订阅与发布
1.mosquitto broker配置
查看mosquitto配置文件在mosquitto文件夹下vim mosquitto.conf
mosquitto配置文件默认即可。如有需求可按需修改。
2.启动
mosquitto [-c config file] [ -d | --daemon ] [-p port number] [-v]
参数说明:
-c 后面跟的是启动mosquitto可以调整的参数,比如是否开启基本认证,端口是什么,SSL单向和双向的认证配置等等。
-d 表示MQTT mosquitto将在后台运行。
-p 代表当前的mosquitto服务实例启动以后,其监听端口号,这个配置覆盖[-c config file] 指定的配置文件中的端口。
-v 代码调试模式(verbose)在终端输出更多的信息 。
3.订阅——mosquitto_sub
mosquitto_sub 命令参数说明
1. -c 设定‘clean session’为无效状态,这样一直保持订阅状态,即便是已经失去连接,如果再次连接仍旧能够接收的断开期间发送的消息。
2. -d 打印debug信息
3. -h 指定要连接的域名 默认为localhost
4. -i 指定clientId
5. -I 指定clientId前缀
6. -k keepalive 每隔一段时间,发PING消息通知broker,仍处于连接状态。 默认为60秒。
7. -q 指定希望接收到QoS为什么的消息 默认QoS为0
8. -R 不显示陈旧的消息
9. -t 订阅topic
10. -v 打印消息
11. --will-payload 指定一个消息,该消息当客户端与broker意外断开连接时发出。该参数需要与--will-topic一起使用
12. --will-qos Will的QoS值。该参数需要与--will-topic一起使用
13. --will-retain 指定Will消息被当做一个retain消息(即消息被广播后,该消息被保留起来)。该参数需要与--will-topic一起使用
14. --will-topic 用户发送Will消息的topic
4.发布——mosquitto_pub
mosquitto_pub 命令参数说明
1. -d 打印debug信息
2. -f 将指定文件的内容作为发送消息的内容
3. -h 指定要连接的域名 默认为localhost
4. -i 指定要给哪个clientId的用户发送消息
5. -I 指定给哪个clientId前缀的用户发送消息
6. -m 消息内容
7. -n 发送一个空(null)消息
8. -p 连接端口号
9. -q 指定QoS的值(0,1,2)
10. -t 指定topic
11. -u 指定broker访问用户
12. -P 指定broker访问密码
13. -V 指定MQTT协议版本
14. --will-payload 指定一个消息,该消息当客户端与broker意外断开连接时发出。该参数需要与--will-topic一起使用
15. --will-qos Will的QoS值。该参数需要与--will-topic一起使用
16. --will-retain 指定Will消息被当做一个retain消息(即消息被广播后,该消息被保留起来)。该参数需要与--will-topic一起使用
17. --will-topic 用户发送Will消息的topic
5.关闭
# 查看mosquitto服务进程ID
ps -aux | grep mosquitto
# 杀掉服务进程
# PID 为上一步查找的ID
kill -9 PID
6.测试
第一个终端(订阅)
mosquitto_sub -h localhost -p 1883 -t "test" -v
在打开第二个终端(发布)
mosquitto_pub -h localhost -p 1883 -t "test" -m "hello world"
第一个终端(订阅)
第二个终端(发布)
出现下面错误看注意事项第5点解决
三.mosquitto库函数实现上述订阅与发布
常见API:
1.MQTT初始化
int mosquitto_lib_init(void)
功能:
使用mosquitto库函数前,要先初始化,使用之后就要清除。清除函数;int mosquitto_lib_cleanup()
返回值:MOSQ_ERR_SUCCESS
2.MQTT清除
int mosquitto_lib_cleanup(void)
功能:
使用MQTT之后,清除工作
返回值MOSQ_ERR_SUCCESS
3.新建客户端
struct mosquitto *mosquitto_new(const char * id, bool clean_session, void * obj)
参数
id:如果为NULL,将生成一个随机客户端ID。如果id为NULL,clean_session必须为true。
clean_session:设置为true以指示代理在断开连接时清除所有消息和订阅,设置为false以指示其保留它们,客户端将永远不会在断开连接时丢弃自己的传出消息就是断开后是否保留订阅信息true/false
obj:用户指针,将作为参数传递给指定的任何回调
返回
成功时返回结构mosquitto的指针,失败时返回NULL,询问errno以确定失败的原因:ENOMEM内存不足。EINVAL输入参数无效。
4.释放客户端
void mosquitto_destroy(struct mosquitto * mosq)
功能
释放客户端
参数:mosq: struct mosquitto指针
5.设置账号密码
int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *passworp)
参数
struct mosquitto *mosq :客户端
const char *username : 以字符串形式发送的用户名,或以NULL形式关闭认证。
const char *passworp:以字符串形式发送的密码。 当用户名有效时,设置为NULL,以便只发送一个用户名。
返回值
成功时返回MOSQ_ERR_SUCCESS。
如果输入参数无效,返回MOSQ_ERR_INVAL。
如果发生内存不足的情况,返回MOSQ_ERR_NOMEM。
6.确认连接回调函数
void mosquitto_connect_callback_set(struct mosquitto * mosq, void (*on_connect)(struct mosquitto *mosq, void *obj, int rc) )
功能:连接确认回调函数,当代理发送CONNACK消息以响应连接时,将调用此方法。
参数:
struct mosquitto * mosq:客户端通配符
void (*on_connect):回调函数
struct mosquitto *mosq:客户端数据
void *obj:创建客户端的回调参数(mosquitto_new中提供的用户数据)
int rc:
0-成功
1-连接被拒绝(协议版本不可接受)
2-连接被拒绝(标识符被拒绝)
3-连接被拒绝(经纪人不可用)
4-255-保留供将来使用
7.断开连接回调函数
void mosquitto_disconnect_callback_set( struct mosquitto *mosq,void (*on_disconnect)( struct mosquitto *mosq,void *obj, int rc) );
/*功能:设置断开连接回调函数,当代理收到DISCONNECT命令并断开与客户端的连接,将调用on_disconnect。
参数:mosq: struct mosquitto指针
void (*on_connect)(struct mosquitto *mosq , void *obj, int rc) 回调函数 (参数:
mosq: struct mosquitto指针
obj:mosquitto_new中提供的用户数据
rc:0表示客户端已经调用mosquitto_disconnect,任何其他值,表示断开连接时意外的。)
8.连接MQTT代理/服务器
int mosquitto_connect(struct mosquitto * mosq, const char * host, int port, int keepalive)
功能: 连接到MQTT代理/服务器(主题订阅要在连接服务器之后进行)
参数:
struct mosquitto * mosq:客户端
const char * host:服务器ip
int port:服务器端口号
int keepalive:保持连接的时间间隔, 单位秒
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_ERRNO 如果系统调用返回错误。变量errno包含错误代码
9.断开MQTT代理/服务器
int mosquitto_disconnect( struct mosquitto * mosq )
功能:断开与代理/服务器的连接。
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NO_CONN 如果客户端未连接到代理。
10.网络事件循环处理
int mosquitto_loop_start(struct mosquitto * mosq)
功能:网络事件循环处理函数,通过创建新的线程不断调用mosquitto_loop() 函数处理网络事件,不阻塞
参数:
struct mosquitto * mosq:客户端
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOT_SUPPORTED 如果没有线程支持。
11.发布主题
int mosquitto_publish(struct mosquitto * mosq, int * mid, const char * topic, int payloadlen, const void * payload, int qos, bool retain)
功能:主题发布的函数
参数:
struct mosquitto * mosq:客户端
int * mid:指向int的指针。如果不为NULL,则函数会将其设置为该特定消息的消息ID
const char * topic:要发布的主题,以'\0'结尾的字符串
int payloadlen:主题消息的内容长度
const void * payload:主题消息的内容,指向要发送的数据的指针,如果payloadlen >0,则它必须时有效的存储位置
int qos:整数值0、1、2指示要用于消息的服务质量
bool retain:设置为true以保留消息
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOMEM 如果发生内存不足的情况。
MOSQ_ERR_NO_CONN 如果客户端未连接到代理。
MOSQ_ERR_PROTOCOL 与代理进行通信时是否存在协议错误。
MOSQ_ERR_PAYLOAD_SIZE 如果payloadlen太大。
MOSQ_ERR_MALFORMED_UTF8 如果主题无效,则为UTF-8
MOSQ_ERR_QOS_NOT_SUPPORTED 如果QoS大于代理支持的QoS。
MOSQ_ERR_OVERSIZE_PACKET 如果结果包大于代理支持的包。
12.订阅主题
int mosquitto_subscribe( struct mosquitto * mosq, int * mid, const char * sub, int qos )
/*功能:订阅主题函数
参数:①mosq:有效的mosquitto实例,客户端
②mid: 指向int的指针。如果不为NULL,则函数会将其设置为该特定消息的消息ID。然后可以将其与订阅回调一起使用,以确定何时发送消息。;主题的消息ID
③sub: 主题名称,订阅模式。
④qos : 此订阅请求的服务质量。
返回值:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOMEM 如果发生内存不足的情况。
MOSQ_ERR_NO_CONN 如果客户端未连接到代理。
MOSQ_ERR_MALFORMED_UTF8 如果主题无效,则为UTF-8
MOSQ_ERR_OVERSIZE_PACKET 如果结果包大于代理支持的包。
13.消息回调函数
void mosquitto_message_callback_set( struct mosquitto * mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *) )
/*功能:设置消息回调函数,收到订阅的消息后调用on_message。
参数:①mosq: 有效的mosquitto实例,客户端。
②on_message 回调函数,格式如下:void callback(struct mosquitto * mosq,void * obj,const struct mosquitto_message * message)
回调的参数:
①mosq:进行回调的mosquitto实例
②obj: mosquitto_new中提供的用户数据
③message: 消息数据,回调完成后,库将释放此变量和关联的内存,客户应复制其所需要的任何数据。
struct mosquitto_message{
int mid;//消息序号ID
char *topic; //主题
void *payload; //主题内容 ,MQTT 中有效载荷
int payloadlen; //消息的长度,单位是字节
int qos; //服务质量
bool retain; //是否保留消息
};
14.在无限阻塞循环中为你调用loop()
int mosquitto_loop_forever( struct mosquitto * mosq, int timeout, int max_packets )
/*功能:此函数在无限阻塞循环中为你调用loop(),对于只想在程序中运行MQTT客户端循环的情况,这很有用,如果服务器连接丢失,它将处理重新连接,如果在回调中调用mosqitto_disconnect()它将返回。
参数:①mosq: 有效的mosquitto实例,客户端
②timeout: 超时之前,在select()调用中等待网络活动的最大毫秒数,设置为0以立即返回,设置为负可使用默认值为1000ms。
③max_packets: 该参数当前未使用,应设为为1,以备来兼容
返回值:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOMEM 如果发生内存不足的情况。
MOSQ_ERR_NO_CONN 如果客户端未连接到代理。
MOSQ_ERR_CONN_LOST 如果与代理的连接丢失。
MOSQ_ERR_PROTOCOL 与代理进行通信时是否存在协议错误。
MOSQ_ERR_ERRNO 如果系统调用返回错误。变量errno包含错误代码
15.网络事件阻塞回收结束处理函数
int mosquitto_loop_stop( struct mosquitto * mosq, bool force )
/*功能:网络事件阻塞回收结束处理函数,这是线程客户端接口的一部分。调用一次可停止先前使用mosquitto_loop_start创建的网络线程。该调用将一直阻塞,直到网络线程结束。为了使网络线程结束,您必须事先调用mosquitto_disconnect或将force参数设置为true。
参数:①mosq :有效的mosquitto实例
②force:设置为true强制取消线程。如果为false,则必须已经调用mosquitto_disconnect。
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOT_SUPPORTED 如果没有线程支持。
16.网络事件循环处理函数
int mosquitto_loop_start( struct mosquitto * mosq )
/*功能:网络事件循环处理函数,通过创建新的线程不断调用mosquitto_loop() 函数处理网络事件,不阻塞
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOT_SUPPORTED 如果没有线程支持。
四、使用mosquitto库在树莓派获取ds18b20的温度发布
我在项目使用了cJSON库和iniparser库,这里简单介绍一下
1. Linux下 iniparser库的安装和使用
iniparser介绍
iniparser是一个C语言库,是针对INI文件的开源解析器。ini文件则是一些系统或者软件的配置文件。
ini文件格式
iniparser提供API接口对ini文件进行解析、配置、删除等操作。ini文件基本格式如下所示:
[mqtt_server_addr]
host =iot-06z00c6bu42mu5l.mqtt.iothub.aliyuncs.com
port =1883
[client_id]
id =i5oqviRE56z.ds18b20|securemode=2,signmethod=hmacsha256,timestamp=1673699121744|
[sub_topic]
topic =/sys/i5oqviRE56z/ds18b20/thing/service/property/set
ini文件的最基本组成单元就是key或者叫property,每个key都有一个名称(name)和对应的值(value),例如:
port =1883
“port”就是名称name,“1883”是对应的值;
其中mqtt_server_addr就是组名,组成员有host和port。
下载方式
git clone https://github.com/ndevilla/iniparser.git
下载好后,进入iniparser,在进入src,里面有四个文件,将这四个文件放入自己的项目中即可。
iniparser基本API
int iniparser_getnsec(dictionary * d); //获取dictionary对象的section个数
char * iniparser_getsecname(dictionary * d, int n); //获取dictionary对象的第n个section的名字
void iniparser_dump_ini(dictionary * d, FILE * f); //保存dictionary对象到file
void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); //保存dictionary对象一个section到file
int iniparser_getsecnkeys(dictionary * d, char * s); //获取dictionary对象某个section下的key个数
char ** iniparser_getseckeys(dictionary * d, char * s); //获取dictionary对象某个section下所有的key
char * iniparser_getstring(dictionary * d, const char * key, char * def); //返回dictionary对象的section:key对应的字串值
int iniparser_getint(dictionary * d, const char * key, int notfound); //返回idictionary对象的section:key对应的整形值
double iniparser_getdouble(dictionary * d, const char * key, double notfound); //返回dictionary对象的section:key对应的双浮点值
int iniparser_getboolean(dictionary * d, const char * key, int notfound); //返回dictionary对象的section:key对应的布尔值
int iniparser_set(dictionary * ini, const char * entry, const char * val); //设置dictionary对象的某个section:key的值
void iniparser_unset(dictionary * ini, const char * entry); //删除dictionary对象中某个section:key
int iniparser_find_entry(dictionary * ini, const char * entry) ; //判断dictionary对象中是否存在某个section:key
dictionary * iniparser_load(const char * ininame); //解析dictionary对象并返回(分配内存)dictionary对象
void iniparser_freedict(dictionary * d); //释放dictionary对象(内存)
dictionary中的一些API
unsigned dictionary_hash(const char * key); //计算关键词的hash值
dictionary * dictionary_new(int size); //创建dictionary对象
void dictionary_del(dictionary * vd); //删除dictionary对象
char * dictionary_get(dictionary * d, const char * key, char * def); //获取dictionary对象的key值
int dictionary_set(dictionary * vd, const char * key, const char * val); //设置dictionary对象的key值
void dictionary_unset(dictionary * d, const char * key); //删除dictionary对象的key值
void dictionary_dump(dictionary * d, FILE * out); //保存dictionary对象
2cJSON的安装和使用
cJSON的简介
JSON(JavaScript Object Notation)是一种轻量级的文本数据交换格式,易于让人阅读。同时也易于机器解析和生成。尽管JSON是Javascript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。
JSON 名称/值对
基于方便引用的考虑,我们希望这个JSON解析库是用C语言实现的。同时,为了避免太过复杂的C源码包含关系,希望最好是一个C文件来实现。通过在网络上的查找,发现cJSON是比较符合要求的。cJSON只有一个C文件,一个头文件,包含到项目源码中非常方便,而且其实现效率也是非常高的。
1.JSON指的是JavaScript对象表示法(JavaScript Object Notation)
2.JSON是轻量级的文本数据交换格式
3.JSON独立于语言
4.JSON具有自我描述性,更易理解
5.JSON 使用 JavaScript 语法来描述数据对象,但是 JSON仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言
JSON语法
数据在名称/值对中、数据由逗号分隔、大括号 {} 保存对象、
中括号 [] 保存数组,数组可以包含多个对象
JSON 对象在大括号 {} 中书写:{key1 : value1, key2 : value2, ... keyN : valueN }
对象可以包含多个名称/值对:{ "name":"百度" , "url":"www.baibu.com" }
JSON 数组在中括号[ ] 中书写,数组可包含多个对象:
{
"sites": [
{ "name":"百度" , "url":"www.baidu.com" },
{ "name":"google" , "url":"www.google.com" },
{ "name":"阿里云" , "url":"www.alyun.com" }
]
}
cJSON库下载
git clone https://github.com/DaveGamble/cJSON.git
下载完成,cd进入cJSON文件有cJSON.c cJSON.h文件,把这两个放入你的项目中即可。编译时加上选项-lm因为cJSON源码中用到了math库。或者可以直接链接动态库和静态库.(运行make)
cJSON库函数
cJSON的核心结构体就是一个cJSON
typedef struct cJSON
{
struct cJSON*next,*prev; /* 遍历数组或对象链的前向或后向链表指针*/
struct cJSON *child; /*数组或对象的孩子节点*/
int type; /* key的类型*/
char *valuestring; /*字符串值*/
int valueint; /* 整数值*/
double valuedouble; /* 浮点数值*/
char *string; /* key的名字*/
} cJSON;
说明:
1.cJSON是使用链表来存储数据的,其访问方式很像一棵树。每一个节点可以有兄弟节点,通过next/prev指针来查找,它类似双向链表;每个节点也可以有孩子节点,通过child指针来访问,进入下一层。只有节点是对象或数组时才可以有孩子节点。
2.type是键(key)的类型,一共有种取值,分别是:False,True,null,Number,String,Array,Object。
若是Number类型,则valueint或valuedouble种存储着值,如期望的是int,则访问valueint,如期望的是double,则访问valuedouble,可以得到值。
若是String类型的,则valuestring中存储着值,可以访问valuestring得到值。
3.string中存放的是这个节点的名字,可以理解位key的名称。
CJSON库常用库函数
cJSON *cJSON_CreateObject();
//创建一个json对象,返回一个cJSON结构体类型的指针。
cJSON *cJSON_CreateArray();
//创建一个数组对象,返回一个cJSON结构体类型的指针。
cJSON *cJSON_CreateString(const char *string);
//创建一个字符串对象,传入一个char *类型的字符串,返回一个cJSON结构体类型的指针。
extern cJSON *cJSON_CreateNumber(double num);
// 创建一个数值类型。
void cJSON_AddItemToArray(cJSON *array, cJSON *item);
//向数组对象中添加一个元素,传入参数array为cJSON *结构体类型的指针,为数组对象,item为添加如数字对象中的对象指针。
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
//向json对象中添加一对元素,object为json对象,string为加入一对元素中的name,item为加入一对元素中的value。
char *cJSON_Print(cJSON *item);
//将一个cJSON结构体代表的json对象转换为一个json格式的字符串。
void cJSON_Delete(cJSON *c)
//释放一个cJSON对象占用的内存空间。
为什么使用cJSON库
使用cJSON库而不是直接使用snprintf
或类似的函数来手动构建JSON格式的字符串,主要是出于以下几个方面的考虑:
- 易用性和错误减少:
- 使用cJSON库可以非常直观地通过调用API来构建JSON对象,如添加键值对、数组等。这种方式比手动拼接字符串更加直观,也更容易理解和维护。
- 手动拼接字符串时,容易出错,比如遗漏逗号、引号不匹配、转义字符处理不当等。这些错误在复杂的JSON结构中尤其难以发现。
- 灵活性:
- cJSON库提供了丰富的API来操作JSON数据,包括添加、删除、修改元素等。这使得在需要动态修改JSON数据时,使用cJSON库变得非常方便。
- 相比之下,如果使用
snprintf
手动构建JSON,那么在需要修改数据时,可能需要重写大量的字符串处理代码,这既费时又容易出错。
- 可读性和可维护性:
- 使用cJSON库编写的代码通常比手动拼接字符串的代码更加清晰和易于理解。这有助于提高代码的可读性和可维护性。
- 当其他人(或未来的你)需要修改或扩展代码时,使用cJSON库的代码将更容易理解和修改。
3.在阿里云创建好产品和设备
具体搭建过程参考博客:http://t.csdnimg.cn/gofNS
用记事本记录以下内容
使用MQTT.fx简单的与阿里云连接
-
)打开mqtt.fx,并按照记事本上的user name passwd等填写如下,点击ok,最后点击connect,连接成功之后右上方灰色的圆形会变成绿色。
2).打开监控运维中的在线调试,并设置温度值,点击设置会看到发送指令成功的弹窗
3).使用MQTT.fx对阿里云发布温度,注意发布温度需要格式要求,我们可以先以JSON格式复制,然后粘贴到public的框框内,发布端口需要粘贴topic
4). 拿记事本记住以下内容,后面代码用得到(id是你在阿里云发指令到MQTTX.fx的JSON格式里面的ID号,)
4.在树莓派中获取温度使用MQTT上报到阿里云
使用iniparser配置文件,写一个.ini文件,这样当我们更换设备的时候只需要修改这个.ini文件中的信息即可,然后在代码中使用iniparser库中API。
虽然可以写死在代码里,但代码复用性不高,换一个设备那么就要改一次源码来更改设备配置信息。
流程图:
代码:
mqtt_aliyun_conf_ini
使用记事本里的内容填充,其中${deviceName}换成自己设备的ID,比如我的设备ID就是ds18b20
[mqtt_server_addr]
host =iot-06z00fn5rkd1tm0.mqtt.iothub.aliyuncs.com
port =1883
[user_passwd]
username =ds18b20&k15kfRQ07nu
passwd =c65e0367d172f029568d31c5785c0c1878bab81ecb5e31d6d66092505efc93fe
[client_id]
id =k15kfRQ07nu.ds18b20|securemode=2,signmethod=hmacsha256,timestamp=1721825843363|
[sub_topic]
topic =/sys/k15kfRQ07nu/ds18b20/thing/service/property/set
[pub_topic]
topic =/sys/k15kfRQ07nu/ds18b20/thing/event/property/post
[ali_json]
method =thing.service.property.set
id =2024546537
identifier =CurrentTemperature
version =1.0.0"
[KEEP_ALIVE]
alive =60
[ali_Qos]
Qos =0
mqtt_aliyun_conf.c
/*********************************************************************************
* Copyright: (C) 2024 DengMingXing<857322639@qq.com>
* All rights reserved.
*
* Filename: mqtt_aliyun_conf.c
* Description: This file
*
* Version: 1.0.0(2024年07月24日)
* Author: DengMingXing <857322639@qq.com>
* ChangeLog: 1, Release initial version on "2024年07月24日 21时06分29秒"
*
********************************************************************************/
#include "iniparser.h"
#include "mqtt_aliyun_conf.h"
#include <stdio.h>
#include <string.h>
int get_mqtt_conf(char *ini_path,data_mqtt *mqtt,int type)
{
dictionary *ini=NULL;
const char *hostname;
const char *username;
int port;
const char *passwd;
const char *clientid;
const char *topic;
int Qos;
const char *method;
const char *jsonid;
const char *identifier;
const char *version;
if(!ini_path || !mqtt)
{
printf("invail input parameter in %s\n", __FUNCTION__) ;
return -1 ;
}
ini=iniparser_load(ini_path);
if( ini ==NULL)
{
printf("inipar_load failure\n");
return -1;
}
hostname =iniparser_getstring(ini, "mqtt_server_addr:host", DEFAULT_HOSTNAME);
port =iniparser_getint(ini,"mqtt_server_addr:port",DEFAULT_PORT);
username =iniparser_getstring(ini,"user_passwd:username",DEFAULT_USERNAME);
passwd =iniparser_getstring(ini,"user_passwd:passwd",DEFAULT_PASSWD);
clientid =iniparser_getstring(ini,"client_id:id",DEFAULT_CLIENTID);
identifier =iniparser_getstring(ini,"ali_json:identifier",DEFAULT_IDENTIFIER) ;
Qos =iniparser_getint(ini,"ali_Qos:Qos",DEFAULT_QOS);
if(type == SUB)
topic = iniparser_getstring(ini, "sub_topic:topic", DEFAULT_SUBTOPIC) ;
else if(type == PUB)
{
topic = iniparser_getstring(ini,"pub_topic:topic", DEFAULT_PUBTOPIC) ;
method = iniparser_getstring(ini,"json:method", DEFAULT_METHOD) ;
jsonid = iniparser_getstring(ini,"json:id", DEFAULT_JSONID) ;
version = iniparser_getstring(ini,"json:version", DEFAULT_VERSION) ;
}
mqtt->Qos=Qos;
strncpy(mqtt->hostname, hostname,BUF_SIZE);
mqtt->port = port;
strncpy(mqtt->username, username,BUF_SIZE);
strncpy(mqtt->passwd, passwd,BUF_SIZE);
strncpy(mqtt->clientid, clientid,BUF_SIZE);
strncpy(mqtt->topic, topic,BUF_SIZE);
if(type == PUB)
{
strncpy(mqtt->method, method,BUF_SIZE) ;
strncpy(mqtt->identifier, identifier,BUF_SIZE);
strncpy(mqtt->jsonid, jsonid,BUF_SIZE) ;
strncpy(mqtt->version, version,BUF_SIZE) ;
}
iniparser_freedict(ini);
return 0;
}
mqtt_aliyun_conf.h
把保存在记事本的内容,替换掉宏里的内容
/********************************************************************************
* Copyright: (C) 2024 DengMingXing<857322639@qq.com>
* All rights reserved.
*
* Filename: mqtt_aliyun_conf.h
* Description: This file
*
* Version: 1.0.0(2024年07月24日)
* Author: DengMingXing <857322639@qq.com>
* ChangeLog: 1, Release initial version on "2024年07月24日 21时12分22秒"
*
********************************************************************************/
#ifndef MQTT_ALIYUN_CONF_H
#define MQTT_ALIYUN_CONF_H
#define BUF_SIZE 512
#define DEFAULT_CLIENTID "k15kfRQ07nu.ds18b20|securemode=2,signmethod=hmacsha256,timestamp=1721825843363|"
#define DEFAULT_USERNAME "ds18b20&k15kfRQ07nu"
#define DEFAULT_PASSWD "c65e0367d172f029568d31c5785c0c1878bab81ecb5e31d6d66092505efc93fe"
#define DEFAULT_HOSTNAME "iot-06z00fn5rkd1tm0.mqtt.iothub.aliyuncs.com"
#define DEFAULT_PORT 1883
#define DEFAULT_SUBTOPIC "/sys/k15kfRQ07nu/ds18b20/thing/service/property/set"
#define DEFAULT_PUBTOPIC "/sys/k15kfRQ07nu/ds18b20/thing/event/property/post"
#define DEFAULT_QOS 0
#define DEFAULT_METHOD "thing.service.property.set"
#define DEFAULT_JSONID "2024546537"
#define DEFAULT_IDENTIFIER "CurrentTemperature"
#define DEFAULT_VERSION "1.0.0"
#define KEEP_ALIVE 60
enum{
SUB,
PUB
};
typedef struct data_mqtt
{
char hostname[BUF_SIZE] ;
int port ;
char username[BUF_SIZE] ;
char passwd[BUF_SIZE] ;
char clientid[BUF_SIZE] ;
char topic[BUF_SIZE] ;
int Qos;
char method[BUF_SIZE] ;
char jsonid[BUF_SIZE] ;
char identifier[BUF_SIZE] ;
char version[BUF_SIZE] ;
}data_mqtt;
int get_mqtt_conf(char *ini_path,data_mqtt *mqtt,int type);
#endif
ds18b20_get_temp.c
/*********************************************************************************
* Copyright: (C) 2024 DengMingXing<857322639@qq.com>
* All rights reserved.
*
* Filename: ds18b20_get_temp.c
* Description: This file ds18b20_get_temp.c
*
* Version: 1.0.0(2024年03月07日)
* Author: DengMingXing <857322639@qq.com>
* ChangeLog: 1, Release initial version on "2024年03月07日 22时50分52秒"
*
********************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "ds18b20_get_temp.h"
#include "logger.h"
int get_temp(float *temp)
{
int fd = -1;
DIR *dirp = NULL;
struct dirent *direntp;
char w1_path[64] = "/sys/bus/w1/devices/";
char chip[32] = {0};
int count = 0;
char buf[128];
char *p =NULL;
dirp = opendir(w1_path);
if( !dirp)
{
log_error("opendir folder %s failure:%s\n",w1_path,strerror(errno));
return -1;
}
while( NULL !=(direntp = readdir(dirp)) )
{ if( strstr(direntp->d_name, "28-") )
{
strncpy(chip, direntp->d_name, sizeof(chip));
count = 1;
}
}
closedir(dirp);
if( !count )
{
log_error("Can not find ds18b20 chipset\n");
return -2;
}
strncat(w1_path, chip, 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 )
{
log_error("Open file failure: %s\n",strerror(errno));
return -3;
}
memset(buf, 0, sizeof(buf));
if( read(fd, buf, sizeof(buf)) < 0 )
{
log_error("read data from fd =: %d failure%s\n", fd, strerror(errno));
return -4;
}
// printf("buf:%s", buf);
p=strstr(buf,"t=");
if( !p )
{
log_error("Can not find t= string\n");
return -5;
}
p += 2;
// printf("p = %s\n", p);
*temp = atof(p)/1000;
close(fd);
return 0;
}
ds18b20_get_temp.h
/********************************************************************************
* Copyright: (C) 2024 DengMingXing<857322639@qq.com>
* All rights reserved.
*
* Filename: ds18b20_get_temp.h
* Description: This file ds18b20_get_temp.h
*
* Version: 1.0.0(2024年03月07日)
* Author: DengMingXing <857322639@qq.com>
* ChangeLog: 1, Release initial version on "2024年03月07日 23时13分08秒"
*
********************************************************************************/
#ifndef _DIS18B20_GET_TEMP_h_
#define _DIS18B20_GET_TEMP_h_
int get_temp(float *temp);
#endif
发布端:mqtt_pub.c
/*********************************************************************************
* Copyright: (C) 2024 DengMingXing<857322639@qq.com>
* All rights reserved.
*
* Filename: mqtt_pub.c
* Description: This file
*
* Version: 1.0.0(2024年07月24日)
* Author: DengMingXing <857322639@qq.com>
* ChangeLog: 1, Release initial version on "2024年07月24日 21时30分45秒"
*
********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <libgen.h>
#include <getopt.h>
#include <string.h>
#include <mosquitto.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <time.h>
#include "ds18b20_get_temp.h"
#include "mqtt_aliyun_conf.h"
#include "cJSON.h"
#include "dictionary.h"
#include "iniparser.h"
#define PROG_VERSION "1.0.0"
#define INI_PATH "./mqtt_aliyun_conf.ini"
static int g_stop=0;
/* 调用信号 */
void sig_handler(int sig_num)
{
if(sig_num == SIGUSR1)
g_stop = 1;
}
int get_time(char *tim);
void connect_callback(struct mosquitto *mosq,void *obj,int rc);
static inline void print_usage(char *progname);
int main(int argc,char **argv)
{
int rv;
int port=1883;
char *hostname=NULL;
char *username=NULL;
char *clientid=NULL;
char *topic=NULL;
char *passwd=NULL;
char *program_name=basename(argv[0]);
int daemon_run=0;
int opt = -1;
int log_fd;
data_mqtt mqtt;
struct mosquitto *mosq = NULL;
struct option options[] =
{
{"daemon",no_argument,NULL,'d'},
{"topic", required_argument,NULL,'t'},
{"hostname", required_argument,NULL,'H'},
{"clientid", required_argument, NULL, 'i'},
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{"username",required_argument,NULL,'u'},
{"passwd",required_argument,NULL,'P'},
{NULL,0,NULL,0}
};
while((opt = getopt_long(argc,argv,"dhp:t:i:u:P:H:",options,NULL)) != -1)
{
switch(opt)
{
case 't':
topic = optarg;
break;
case 'i':
clientid = optarg;
break;
case 'H':
hostname = optarg;
break;
case 'u':
username = optarg;
break;
case 'P':
passwd = optarg;
break;
case 'd':
daemon_run = 1;
break;
case 'p':
port = atoi(optarg);
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
break;
}
}
/* 创建日志 */
if(daemon_run)
{
printf("program %s running in backgrund\n", program_name);
if( (log_fd = open("client.log", O_CREAT|O_RDWR, 0666)) < 0)
{
printf("open() failed:%s\n", strerror(errno)) ;
return -2;
}
dup2(log_fd, STDOUT_FILENO);
dup2(log_fd, STDERR_FILENO);
daemon(1,1);
}
/* 安装信号 */
signal(SIGUSR1,sig_handler);
/* 载入配置文件 */
memset(&mqtt,0,sizeof(mqtt));
rv=get_mqtt_conf(INI_PATH,&mqtt,PUB);
/* MQTT 初始化 */
mosquitto_lib_init();
/* 创建MQTT 客户端 */
mosq = mosquitto_new(mqtt.clientid,true,(void *)&mqtt);
if(!mosq)
{
printf("mosquitto_new() failed: %s\n",strerror(errno));
goto cleanup;
return -1;
}
/* 设置账号密码 */
if(mosquitto_username_pw_set(mosq,mqtt.username,mqtt.passwd) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_username_pw_set failed: %s\n",strerror(errno));
goto cleanup;
}
/* 设置回调函数 */
mosquitto_connect_callback_set(mosq,connect_callback);
while(!g_stop)
{
/* 连接MQTT服务器,ip,端口,时间 */
if(mosquitto_connect(mosq, mqtt.hostname,mqtt.port,KEEP_ALIVE) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_connect() failed: %s\n",strerror(errno));
goto cleanup;
}
printf("connect successful\n");
/*无阻塞 断线连接 */
mosquitto_loop_forever(mosq,-1,1);
sleep(3);
}
/* 释放客户端,清除 */
cleanup:
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
/* 获取时间 */
int get_time(char *tim)
{
time_t t;
struct tm *p;
time(&t);
p = gmtime(&t);
snprintf(tim, 32, "%04d-%02d-%02d %02d:%02d:%02d", 1900+p->tm_year,1+p->tm_mon, p->tm_mday, (p->tm_hour + 8), p->tm_min, p->tm_sec);
return 0 ;
}
/* 回调函数*/
void connect_callback(struct mosquitto *mosq,void *obj,int rc)
{
float tem = 0.00000000;
char tim[64];
char *msg;
cJSON * root = cJSON_CreateObject();
cJSON * item = cJSON_CreateObject();
memset(root,0,sizeof(root));
memset(item,0,sizeof(item));
memset(tim,0,sizeof(tim));
if(get_temp(&tem)<0)
{
printf("get_temperature failed:%s\n",strerror(errno));
return ;
}
if(get_time(tim)<0)
{
printf("get_time failured:%s\n",strerror(errno));
return ;
}
if(!obj)
{
printf("invalid_argument in %s\n",__FUNCTION__);
return;
}
struct data_mqtt *mqtt = (data_mqtt *)obj;
cJSON_AddItemToObject(root, "method", cJSON_CreateString(mqtt->method));
cJSON_AddItemToObject(root, "id", cJSON_CreateString(mqtt->jsonid));
cJSON_AddItemToObject(root, "params",item);
cJSON_AddItemToObject(root,"time",cJSON_CreateString(tim));
cJSON_AddItemToObject(item, "CurrentTemperature", cJSON_CreateNumber(tem));
cJSON_AddItemToObject(root, "version", cJSON_CreateString(mqtt->version));
msg=cJSON_Print(root);
printf("%s\n",msg);
if(!rc)
{
if(mosquitto_publish(mosq,NULL,mqtt->topic,strlen(msg),msg,mqtt->Qos,NULL) != MOSQ_ERR_SUCCESS)
{
printf("mosquitto_publish failed: %s\n",strerror(errno));
return;
}
}
printf("mqtt_publish successful!\n");
mosquitto_disconnect(mosq);
}
/* 帮助信息 */
void print_usage(char *progname)
{
printf("%s usage:\n",progname);
printf("Example: %s -h ${brokeraddress} -p ${brokerport} -i ${clientid} -u ${username} -p ${password} -t${topic} -h ${help} -d ${daemon}\n",progname);
printf("-h(--host): sepcify hostname.\n");
printf("-p(--port): sepcify port.\n");
printf("-h(--Help): print this help information.\n");
printf("-d(--daemon): set program running on background.\n");
printf("-i(--clientid): sepcify the clientid.\n");
printf("-u(--username): sepcify username of the client.\n");
printf("-P(--passwd): sepcify password of the username.\n");
printf("-t(--topic): sepcify topic of the client.\n");
printf("-d(--daemon): running in backgrund.\n");
}
日志系统:logger.c
/*********************************************************************************
* Copyright: (C) 2024 LiYi<1751425323@qq.com>
* All rights reserved.
*
* Filename: logger.c
* Description: This file logger.c
*
* Version: 1.0.0(20/04/24)
* Author: LiYi <1751425323@qq.com>
* ChangeLog: 1, Release initial version on "20/04/24 14:14:43"
*
********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <pthread.h>
#include "logger.h"
typedef void (*log_LockFn)(void *udata, int lock);
static struct {
char file[32]; /* logger file name */
FILE *fp; /* logger file pointer */
long size; /* logger file max size */
int level; /* logger level */
log_LockFn lockfn; /* lock function */
void *udata; /* lock data */
} L;
static const char *level_names[] = {
"ERROR",
"WARN",
"INFO",
"DEBUG",
"TRACE"
};
static const char *level_colors[] = {
"\x1b[31m",
"\x1b[33m",
"\x1b[32m",
"\x1b[36m",
"\x1b[94m"
};
static inline void time_to_str(char *buf)
{
struct timeval tv;
struct tm *tm;
int len;
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
len = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d ",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec);
buf[len] = '\0';
}
static void mutex_lock(void *udata, int lock)
{
int err;
pthread_mutex_t *l = (pthread_mutex_t *) udata;
if (lock)
{
if ( (err = pthread_mutex_lock(l)) != 0 )
log_error("Unable to lock log lock: %s", strerror(err));
}
else
{
if ( (err = pthread_mutex_unlock(l)) != 0 )
log_error("Unable to unlock log lock: %s", strerror(err));
}
}
int log_open(char *fname, int level, int size, int lock)
{
FILE *fp;
L.level = level;
L.size = size*1024;
if( !fname || !strcmp(fname, "console") || !strcmp(fname, "stderr") )
{
strcpy(L.file, "console");
L.fp = stderr;
L.size = 0; /* console don't need rollback */
}
else
{
if ( !(fp = fopen(fname, "a+")) )
{
fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno));
return -2;
}
L.fp = fp;
strncpy(L.file, fname, sizeof(L.file));
}
if( lock )
{
static pthread_mutex_t log_lock;
pthread_mutex_init(&log_lock, NULL);
L.udata = (void *)&log_lock;
L.lockfn = mutex_lock;
}
fprintf(L.fp, "\n");
log_info("logger system(%s) start: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
LOG_VERSION, L.file, level_names[level], size);
return 0;
}
void log_close(void)
{
if( L.fp && L.fp!=stderr )
fclose(L.fp);
if (L.udata )
pthread_mutex_destroy( L.udata);
}
static void log_rollback(void)
{
char cmd[128]={0};
long fsize;
/* don't need rollback */
if(L.size <= 0 )
return ;
fsize = ftell(L.fp);
if( fsize < L.size )
return ;
/* backup current log file */
snprintf(cmd, sizeof(cmd), "cp %s %s.bak", L.file, L.file);
system(cmd);
/* rollback file */
fseek(L.fp, 0, SEEK_SET);
truncate(L.file, 0);
fprintf(L.fp, "\n");
log_info("logger system(%s) rollback: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
LOG_VERSION, L.file, level_names[L.level], L.size/1024);
return ;
}
void _log_write(int level, const char *file, int line, const char *fmt, ...)
{
va_list args;
char time_string[100];
if ( !L.fp || level>L.level )
return;
/* Acquire lock */
if ( L.lockfn )
L.lockfn(L.udata, 1);
log_rollback();
/* check and rollback file */
time_to_str(time_string);
/* Log to stderr */
if ( L.fp == stderr )
{
fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ",
time_string, level_colors[level], level_names[level], file, line);
}
else /* Log to file */
{
fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line);
}
va_start(args, fmt);
vfprintf(L.fp, fmt, args);
va_end(args);
fflush(L.fp);
/* Release lock */
if ( L.lockfn )
L.lockfn(L.udata, 0);
}
#define LINELEN 81
#define CHARS_PER_LINE 16
static char *print_char =
" "
" "
" !\"#$%&'()*+,-./"
"0123456789:;<=>?"
"@ABCDEFGHIJKLMNO"
"PQRSTUVWXYZ[\\]^_"
"`abcdefghijklmno"
"pqrstuvwxyz{|}~ "
" "
" "
" ???????????????"
"????????????????"
"????????????????"
"????????????????"
"????????????????"
"????????????????";
void log_dump(int level, const char *prompt, char *buf, size_t len)
{
int rc;
int idx;
char prn[LINELEN];
char lit[CHARS_PER_LINE + 2];
char hc[4];
short line_done = 1;
if (!L.fp || level>L.level)
return;
if( prompt )
_log_write(level, __FILE__, __LINE__, "%s", prompt);
rc = len;
idx = 0;
lit[CHARS_PER_LINE] = '\0';
while (rc > 0)
{
if (line_done)
snprintf(prn, LINELEN, "%08X: ", idx);
do
{
unsigned char c = buf[idx];
snprintf(hc, 4, "%02X ", c);
strncat(prn, hc, LINELEN);
lit[idx % CHARS_PER_LINE] = print_char[c];
}
while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
line_done = (idx % CHARS_PER_LINE) == 0;
if (line_done)
{
if (L.fp)
fprintf(L.fp, "%s %s\n", prn, lit);
}
}
if (!line_done)
{
int ldx = idx % CHARS_PER_LINE;
lit[ldx++] = print_char[(int)buf[idx]];
lit[ldx] = '\0';
while ((++idx % CHARS_PER_LINE) != 0)
strncat(prn, " ", sizeof(prn)-strlen(prn));
if (L.fp)
fprintf(L.fp, "%s %s\n", prn, lit);
}
}
logger.h
/********************************************************************************
* Copyright: (C) 2024 LiYi<1751425323@qq.com>
* All rights reserved.
*
* Filename: logger.h
* Description: This file logger.h
*
* Version: 1.0.0(20/04/24)
* Author: LiYi <1751425323@qq.com>
* ChangeLog: 1, Release initial version on "20/04/24 14:14:02"
*
********************************************************************************/
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <stdio.h>
#include <stdarg.h>
#define LOG_VERSION "v0.1"
/* log level */
enum {
LOG_LEVEL_ERROR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG,
LOG_LEVEL_TRACE,
LOG_LEVEL_MAX
};
enum {
LOG_LOCK_DISABLE, /* disable lock */
LOG_LOCK_ENABLE, /* enable lock */
};
#define ROLLBACK_NONE 0
/* description: Initial the logger system
* arguments :
* $fname: logger file name, NULL/"console"/"stderr" will log to console
* $level: logger level above;
* $size : logger file max size in KiB
* $lock : thread lock enable or not
* return : <0: Failed ==0: Sucessfully
*/
#define THREAD_LOCK_NONE 0
#define THREAD_LOCK_EN 1
int log_open(char *fname, int level, int size, int lock);
/* description: Terminate the logger system */
void log_close(void);
/* description: log message into log file. Don't call this function directly. */
void _log_write(int level, const char *file, int line, const char *fmt, ...);
/* description: dump a buffer in hex to logger file */
void log_dump(int level, const char *prompt, char *buf, size_t len);
/* function: log message into logger file with different log level */
#define log_trace(...) _log_write(LOG_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) _log_write(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) _log_write(LOG_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) _log_write(LOG_LEVEL_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) _log_write(LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#endif
makefile
CC = gcc
all:
gcc *.c -o mqtt_pub -lmosquitto
clean:
rm -rf mqtt_pub
运行mqtt_pub
本篇博客参考了http://t.csdnimg.cn/y5R19