【MQTT】 基于mosquitto实现本机发布订阅温度 linux

一、MQTT简介

MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

MQTT特性

MQTT协议工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:

(1)使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。

这一点很类似于XMPP,但是MQTT的信息冗余远小于XMPP,,因为XMPP使用XML格式文本来传递数据。

(2)对负载内容屏蔽的消息传输。

(3)使用TCP/IP提供网络连接。

主流的MQTT是基于TCP连接进行数据推送的,但是同样有基于UDP的版本,叫做MQTT-SN。这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了。

(4)有三种消息发布服务质量:

“至多一次”,消息发布完全依赖底层TCP/IP网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。这一种方式主要普通APP的推送,倘若你的智能设备在消息推送时未联网,推送过去没收到,再次联网也就收不到了。

“至少一次”,确保消息到达,但消息重复可能会发生。

“只有一次”,确保消息到达一次。在一些要求比较严格的计费系统中,可以使用此级别。在计费系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送,确保用户收到且只会收到一次。

(5)小型传输,开销很小(固定长度的头部是2字节),协议交换最小化,以降低网络流量。

这就是为什么在介绍里说它非常适合“在物联网领域,传感器与服务器的通信,信息的收集”,要知道嵌入式设备的运算能力和带宽都相对薄弱,使用这种协议来传递消息再适合不过了。

(6)使用Last Will和Testament特性通知有关各方客户端异常中断的机制。

Last Will:即遗言机制,用于通知同一主题下的其他设备发送遗言的设备已经断开了连接。

Testament:遗嘱机制,功能类似于Last Will。

MQTT协议原理

MQTT协议实现方式

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)

(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

网络传输与应用消息

MQTT会构建底层网络传输:它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的、基于字节流的双向传输。

当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关连。

MQTT客户端

一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以:

(1)发布其他客户端可能会订阅的信息;

(2)订阅其它客户端发布的消息;

(3)退订或删除应用程序的消息;

(4)断开与服务器连接。

MQTT服务器

MQTT服务器以称为"消息代理"(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以:

(1)接受来自客户的网络连接;

(2)接受客户发布的应用信息;

(3)处理来自客户端的订阅和退订请求;

(4)向订阅的客户转发应用程序消息。

MQTT协议中的订阅、主题、会话

一、订阅(Subscription)

订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。

二、会话(Session)

每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。

三、主题名(Topic Name)

连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。

四、主题筛选器(Topic Filter)

一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。

五、负载(Payload)

消息订阅者所具体接收的内容。

MQTT协议中的方法

MQTT协议中定义了一些方法(也被称为动作),来于表示对确定资源所进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。主要方法有:

(1)Connect。等待与服务器建立连接。

(2)Disconnect。等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。

(3)Subscribe。等待完成订阅。

(4)UnSubscribe。等待服务器取消客户端的一个或多个topics订阅。

(5)Publish。MQTT客户端发送消息请求,发送完成后返回应用程序线程。

MQTT协议数据包结构

 在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成。MQTT数据包结构如下:

(1)固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。

(2)可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。

(3)消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。

参考博客:

https://blog.csdn.net/qq_28877125/article/details/78325003

二、安装MQTT

1.mosquitto简介

Mosquitto是用C语言实现MQTT协议的Broker。是一款实现了消息推送协议 MQTT v3.1 的开源消息代理软件,提供轻量级的,支持可发布/可订阅的的消息推送模式,使设备对设备之间的短消息通信变得简单,比如现在应用广泛的低功耗传感器,手机、嵌入式计算机、微型控制器等移动设备。一个典型的应用案例就是 Andy Stanford-ClarkMosquitto(MQTT协议创始人之一)在家中实现的远程监控和自动化。

Mosquitto官网

Mosquitto源码

2.安装mosquitto库

apt-get命令安装:

# 安装mosquitto
sudo apt-get install mosquitto
# 安装客户端
sudo apt-get install mosquitto-clients
# 安装设备端
sudo apt-get install mosquitto-dev
#查询mosquitto是否正确运行
mosquitto -p 1883   //mosquitto默认时候用1883端口

本机终端测试MQTT

打开一个终端,订阅主题
mosquitto_sub -t mqtt

【-h】指定要连接的MQTT服务器 
【-t】订阅主题,此处为mqtt 
【-v】打印更多的调试信息

再打开一个终端,发布主题
mosquitto_pub -h localhost -t mqtt -m "hello world"

【-h】指定要连接的MQTT服务器 
【-t】向指定主题推送消息 
【-m】指定消息内容

其他处理

查看运行状态 systemctl status mosquitto

开启/关闭systemctl start/stop mosquitto

查看进程ps -aux | grep mosquitto

查看1883端口占用netstat -apn | grep 1883

查看lsof -i:1883

关闭进程kill -9 pid

3.常用MQTT库函数

1. MQTT 初始化

函数原型:

int mosquitto_lib_init(void)
//功能:使用mosquitto库函数前,要先初始化,使用之后就要清除。清除函数;int mosquitto_lib_cleanup();
//返回值:MOSQ_ERR_SUCCESS 总是

2. MQTT 清除

函数原型:

int mosquitto_lib_cleanup(void)
//功能:使用完mosquitto函数之后,要做清除工作。
//返回值: MOSQ_ERR_SUCCESS 总是

3. 新建客户端

函数原型:

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

/*功能:创建一个新的mosquitto客户端实例,新建客户端。

参数:①id :用作客户端ID的字符串。如果为NULL,将生成一个随机客户端ID。如果id为NULL,clean_session必须为true。

②clean_session:设置为true以指示代理在断开连接时清除所有消息和订阅,设置为false以指示其保留它们,客户端将永远不会在断开连接时丢弃自己的传出消息。调用mosquitto_connect或mosquitto_reconnect将导致重新发送消息。使mosquitto_reinitialise将客户端重置为其原始状态。如果id参数为NULL,则必须将其设置为true。
简言之:就是断开后是否保留订阅信息true/false

③obj: 用户指针,将作为参数传递给指定的任何回调,(回调参数)

返回:成功时返回结构mosquitto的指针,失败时返回NULL,询问errno以确定失败的原因:
ENOMEM内存不足。
EINVAL输入参数无效。

4. 释放客户端

函数原型:

void mosquitto_destroy( struct mosquitto * mosq )

/*功能:释放客户端

参数:mosq: struct mosquitto指针

5. 确认连接回调函数

函数原型:

void mosquitto_connect_callback_set(struct mosquitto * mosq, void (*on_connect)(struct mosquitto *mosq, void *obj, int rc) )

/*功能:连接确认回调函数,当代理发送CONNACK消息以响应连接时,将调用此方法。

参数:mosq: struct mosquitto指针
void (*on_connect)(struct mosquitto *mosq , void *obj, int rc)  回调函数 (参数:
mosq: struct mosquitto指针
obj:mosquitto_new中提供的用户数据
rc: 连接响应的返回码,其中有:
0-成功
1-连接被拒绝(协议版本不可接受)
2-连接被拒绝(标识符被拒绝)
3-连接被拒绝(经纪人不可用)
4-255-保留供将来使用

6. 断开连接回调函数

函数原型:

void mosquitto_disconnect_callback_set( struct mosquitto *mosq,void (*on_disconnect)( struct mosquitto *mosq,void *obj, int rc) );

/*功能:断开连接回调函数,当代理收到DISCONNECT命令并断开与客户端的连接,将调用此方法。
参数:mosq: struct mosquitto指针
void (*on_connect)(struct mosquitto *mosq , void *obj, int rc)  回调函数 (参数:
mosq: struct mosquitto指针
obj:mosquitto_new中提供的用户数据
rc:0表示客户端已经调用mosquitto_disconnect,任何其他值,表示断开连接时意外的。)

7. 连接MQTT代理/服务器

函数原型:

int mosquitto_connect( struct mosquitto * mosq, const char * host, int port, int keepalive )

/*功能: 连接到MQTT代理/服务器(主题订阅要在连接服务器之后进行)
参数:①mosq : 有效的mosquitto实例,mosquitto_new()返回的mosq.
②host : 服务器ip地址
③port:服务器的端口号
④keepalive:保持连接的时间间隔, 单位秒。如果在这段时间内没有其他消息交换,则代理应该将PING消息发送到客户端的秒数。
返回:MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_ERRNO 如果系统调用返回错误。变量errno包含错误代码

8. 断开MQTT代理/服务器

函数原型:

int mosquitto_disconnect( struct mosquitto * mosq )

/*功能:断开与代理/服务器的连接。
返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NO_CONN 如果客户端未连接到代理。

9. 发布主题

函数原型:

int mosquitto_publish( struct mosquitto * mosq, int * mid, const char * topic, int payloadlen, const void * payload, int qos, bool retain )

/*功能:主题发布的函数
参数:①mosq:有效的mosquitto实例,客户端
②mid:指向int的指针。如果不为NULL,则函数会将其设置为该特定消息的消息ID。然后可以将其与发布回调一起使用,以确定何时发送消息。请注意,尽管MQTT协议不对QoS = 0的消息使用消息ID,但libmosquitto为其分配了消息ID,以便可以使用此参数对其进行跟踪。
③topic:要发布的主题,以null结尾的字符串
④payloadlen:有效负载的大小(字节),有效值在0到268,435,455之间;主题消息的内容长度
⑤payload: 主题消息的内容,指向要发送的数据的指针,如果payloadlen >0,则它必须时有效的存储位置。
⑥qos:整数值0、1、2指示要用于消息的服务质量。
⑦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 如果结果包大于代理支持的包。

10. 订阅主题

函数原型:

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 如果结果包大于代理支持的包。

11. 消息回调函数

函数原型:

void mosquitto_message_callback_set( struct mosquitto * mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_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; //是否保留消息
};

12. MQTT循环调用/断开重新连接

函数原型:

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包含错误代码

13. :网络事件阻塞回收结束处理

函数原型:

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 如果没有线程支持。

14. 网络事件循环处理

函数原型:

int mosquitto_loop_start( struct mosquitto * mosq )
/*功能:网络事件循环处理函数,通过创建新的线程不断调用mosquitto_loop() 函数处理网络事件,不阻塞

返回:
MOSQ_ERR_SUCCESS 成功。
MOSQ_ERR_INVAL 如果输入参数无效。
MOSQ_ERR_NOT_SUPPORTED 如果没有线程支持。

15. 配置用户名/密码

函数原型:

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。

16.循环接受订阅发代理的内容

函数原型:

int mosquitto_loop(struct mosquitto *mosq,int timeout,int max_packets)

参数
    struct mosquitto *mosq :客户端
    int timeout:超时之前,在select()调用中等待网络活动的最大毫秒数,设置为0以立即返回,设置为负可使用默认值为1000ms。
    int 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包含错误代码 

mosquitto库的API:https://mosquitto.org/api/files/mosquitto-h.html

基于DS18B20在本机实现温度的订阅和发布实例

发布端

/*********************************************************************************
 *      Copyright:  (C) 2023 Yangpeng
 *                  All rights reserved.
 *
 *       Filename:  get_temperature.c
 *    Description:  This file get temperature
 *                 
 *        Version:  1.0.0(2023年01月05日)
 *         Author:  Yangpeng <1023769078@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年01月05日 16时39分48秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netdb.h>
#include <stdlib.h>
#include <fcntl.h>
#include <libgen.h>
#include <netinet/in.h>
#include <mosquitto.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <mosquitto.h>

#include "get_temperature.h"

#define BUF_SIZE    1024
#define ALIVE_Time 60
#define hostname "localhost"

int g_stop=0;

void printf_usage(char *program);
void sig_handler(int SIG_NUM);
int get_time(char *tim);

int main(int argc,char **argv)
{
    int             daemon_run=0;
    int             port;
    int             opt;
    char               *topic = NULL;
    char            tim[32];
    float           temper;
    char         buf[512];
    char         tem[32];
    char         *user="Yangpeng";
    struct mosquitto    *mosq = NULL;
    int         mid;

    char *program = basename(argv[0]);
    
    struct option long_options[] =
    {
        {"port",required_argument, NULL, 'p'},
        {"help",no_argument, NULL,'h'},
        {NULL, 0, NULL, 0}
    };
    while ((opt = getopt_long(argc, argv, "p:h", long_options, NULL)) != -1)
    {
         switch (opt)
        {
             case 'p':
                port = atoi(optarg);
                break;
             case 'h':
                printf_usage(argv[0]);
                break;
            default:
                break;
        }
    }
    if(!port)
    {
        printf_usage(program) ;
        return 0 ;
    }

    //安装信号
    signal(SIGUSR1, sig_handler);
    while(!g_stop)
    {
        if(get_temperature(&temper)<0)
        {
            printf("get_temperature() failed\n") ;
            return -1;
        }
        sprintf(tem,"  temperature:%5.0f",temper);
        get_time(tim);
        memset(buf,0,sizeof(buf));
        snprintf(buf,sizeof(buf),"%s%s%s",user,tim,tem);
        printf("%s\n",buf);


        mosquitto_lib_init();
        mosq = mosquitto_new("pub_test", true, NULL) ;
        if(mosq == NULL)
        {
            printf("New sub_test error!\n");
            mosquitto_lib_cleanup();
            return -1;
        }
        printf("Create mosquitto sucessfully!\n");
        
        if(mosquitto_connect(mosq,hostname,port,ALIVE_Time)!= MOSQ_ERR_SUCCESS)
        {
            printf("Mosq_Connect() failed: %s\n", strerror(errno) );
            mosquitto_destroy(mosq);
            mosquitto_lib_cleanup();
            return -2;
        }
        printf("Connect %s:%d Sucessfully!\n", hostname, port);
        int loop = mosquitto_loop_start(mosq);
        if(loop != MOSQ_ERR_SUCCESS)
        {
            printf("mosquitto loop error\n");
            mosquitto_destroy(mosq) ;
            mosquitto_lib_cleanup() ;
            return -3;
        }   
        if( mosquitto_publish(mosq,&mid,"topic",strlen(buf),buf,0,0) != MOSQ_ERR_SUCCESS ) 
        {
            printf("Mosq_Publish() error: %s\n", strerror(errno));
            mosquitto_destroy(mosq) ;
            mosquitto_lib_cleanup() ;
            return -4;
        }
        else
             printf("Publish information of temperature Ok!\n") ;
        sleep(5);
    }

}

void printf_usage(char *program)
{
    printf("使用方法:%s【选项】 \n", program);
    printf("\n传入参数\n");
    printf(" -p[port ] 指定连接的端口号\n");
    printf(" -h[help ] 打印帮助信息\n");
    printf("\n例如: %s -b -p 8900\n", program);
    return;
}


void sig_handler(int SIG_NUM)
{
    if(SIG_NUM == SIGUSR1)
    g_stop = 1 ;
}

int get_time(char *tim)
{
    time_t time_val,time_mk;
    struct tm *time_gm,*time_local;
    time(&time_val);
    time_gm=gmtime(&time_val);                      
    sprintf(tim,"%04d/%02d/%02d  %02d:%02d:%02d",time_gm->tm_year+1900,time_gm->tm_mon+1,time_gm->tm_mday,time_gm->tm_hour,time_gm->tm_min,time_gm->tm_sec);
    return 0;
}

订阅端

/*********************************************************************************
 *      Copyright:  (C) 2022 Yangpeng
 *                  All rights reserved.
 *
 *       Filename:  mosquitto_sub.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2023年01月05日)
 *         Author:  Yangpeng <1023769078@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年01月05日 16时39分48秒"
 *                 
 ********************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mosquitto.h"

#define HOST "localhost"
#define PORT  1883
#define KEEP_ALIVE 60
#define MSG_MAX_SIZE  512

static int running =1;

void my_connect_callback(struct mosquitto *mosq, void *obj, int rc)
{
    printf("Call the function: on_connect\n");
    if(rc)
    {
        printf("on_connect error!\n");
        exit(1);
    }
    else
    {
        if(mosquitto_subscribe(mosq, NULL, "topic", 2))
        {
            printf("Set the topic error!\n");
            exit(1);
        }
    }
}

void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc)
{
    printf("Call the function: my_disconnect_callback\n");
    running = 0;
}

void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
    printf("Call the function: on_subscribe\n");
}

void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
    printf("Call the function: on_message\n");
    printf("Recieve a message of %s : %s\n", (char *)msg->topic, (char *)msg->payload);
    if(0 == strcmp(msg->payload, "quit"))
    {
        mosquitto_disconnect(mosq);
    }
}

int main()
{
    int ret;
    struct mosquitto *mosq;

    ret = mosquitto_lib_init();
    if(ret)
    {
        printf("Init lib error!\n");
        return -1;
    }

    mosq =  mosquitto_new("sub_test", true, NULL);
    if(mosq == NULL)
    {
        printf("New sub_test error!\n");
        mosquitto_lib_cleanup();
        return -1;
    }
    printf("creat a sub_er success!\n");

    mosquitto_connect_callback_set(mosq, my_connect_callback);
    mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);
    mosquitto_subscribe_callback_set(mosq, my_subscribe_callback);
    mosquitto_message_callback_set(mosq, my_message_callback);
    
    ret = mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE);
    if(ret)
    {
                printf("Connect server error!\n");
                mosquitto_destroy(mosq);
                mosquitto_lib_cleanup();
                return -1;
    }
    printf("Start!\n");
    while(running)
    {
        mosquitto_loop(mosq, -1, 1);
    }
    mosquitto_destroy(mosq);
    mosquitto_lib_cleanup();
    printf("End!\n");
}

ds18b20获取温度模块

get_temperature.c
/*********************************************************************************
 *      Copyright:  (C) 2023 Yangpeng
 *                  All rights reserved.
 *
 *       Filename:  get_temperature.c
 *    Description:  This file get temperature
 *                 
 *        Version:  1.0.0(2023年01月05日)
 *         Author:  Yangpeng <1023769078@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年01月05日 16时39分48秒"
 *                 
 ********************************************************************************/

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#include"get_temperature.h"


int get_temperature(float *temp)
{
    char             path[64]="/sys/bus/w1/devices/";
    char             buf[128];
        char             chip[32];
        DIR             *dirp=NULL;
        struct dirent   *direntp=NULL;
        int              fd=-1;
        char            *ptr=NULL;
        int              found=0;

    if(NULL==(dirp=opendir(path)))
    {
            printf("opendir failure:%s\n",strerror(errno));
            return -1;
    }

    memset(chip,0,sizeof(chip));
    while((direntp=readdir(dirp))!=NULL)
    {
            if(strstr(direntp->d_name,"28-"))
            {
                strcpy(chip,direntp->d_name);
                    found=1; 
            }
    }
     
    closedir(dirp);

    if(!found)
    {
            printf("readdir failure\n");
            return -2;
        }
  

                             
    strncat(path,chip,sizeof(path)-strlen(path));
    strncat(path,"/w1_slave",sizeof(path)-strlen(path));

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

    memset(buf,0,sizeof(buf));
    if(read(fd,buf,sizeof(buf))<0)
    {
            printf("read file failure:%s\n",strerror(errno));
            return -4;
    } 

    ptr=strstr(buf,"t=");
    ptr+=2;
 
    if(!ptr)
    {
            printf("error:can not ptr\n");
            return -5;
    } 

    *temp=atof(ptr)/1000;
    printf("temprature:%f\n",*temp);
    close(fd);

    return 0;
}
get_temperature.h
/********************************************************************************
 *      Copyright:  (C) 2023 Yangpeng
 *                  All rights reserved.
 *
 *       Filename:  get_temperature.h
 *    Description:  This file 
 *
 *        Version:  1.0.0(2023年01月05日)
 *         Author:  Yangpeng <1023769078@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年01月05日 17时02分31秒"
 *                 
 ********************************************************************************/

#ifndef  _GET_TEMPERATURE_H_
#define  _GET_TEMPERATURE_H_

int get_temperature(float *temp);

#endif

makefile

all:
    gcc mosquitto_sub.c -o mosquitto_sub -l mosquitto
    gcc mosquitto_pub.c get_temperature.c -o mosquitto_pub -l mosquitto    
    

clean:
    rm mosquitto_pub mosquitto_sub

runsub:
    ./mosquitto_sub -p 1883 

runpub:
    ./mosquitto_pub -p 1883 

运行截图

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MQTT是一种轻量级的通讯协议,适用于物联网设备之间的通信。Mosquitto是一款开源的MQTT代理服务器,它提供了可靠稳定的MQTT消息传递服务。为了满足用户的需求,开发人员推出了一款名为MQTT测试工具的软件,它可以用来测试Mosquitto代理服务器的性能和功能。 MQTT测试工具可以支持多种MQTT协议,包括QoS0、QoS1和QoS2。此外,它还支持通过清除session或发送遗嘱消息来测试Mosquitto的断开连接机制。MQTT测试工具可以达到每秒数千条消息的处理速度,可帮助用户提高Mosquitto代理服务器的性能和功能。 通过使用MQTT测试工具可以实现较低的错误率和较高的消息交付率,从而提高设备之间的通信效率和数据传输的质量。此外,它还支持各种操作系统,如Windows、Linux和MacOS等。 总之,MQTT测试工具是一款非常实用的软件,适用于测试MQTT消息传递的各种参数和功能,帮助提高Mosquitto的性能和功能,让设备之间的通信更加高效。 ### 回答2: MQTTMosquitto)是一种轻量级的通信协议,用于基于订阅/发布模型的物联网设备之间的交互。针对MQTT协议,我们需要使用MQTT测试工具,这样我们才能测试MQTT的各种功能。 Mosquitto是一种流行的MQTT实现。这个工具是一个开源项目,它可以运行在多个平台上,从而使我们能够轻松地测试MQTT应用程序。Mosquitto提供了可用的CLI(命令行界面),为开发者们提供了良好的操作性。它还提供了一些基本测试,包括测试客户端的连接效果和性能。此外,它提供了一些高级测试,如消息订阅的特征、多连接质量、最大qos的处理和消息重传等。 Mosquitto支持基于TLS的安全通讯协议,自动重连功能,多SPS(会话)管理以及客户端订阅策略等等。Mosquitto是一个功能强大的MQTT测试工具,为MQTT开发人员提供了一个好的平台,能够测试他们的MQTT应用程序,并在需要时进行调整。 总之,MQTT测试工具Mosquitto是一款非常好的工具,它能够提高MQTT应用程序的可靠性和性能,同时为MQTT开发人员提供了一个好的平台,使他们能够更有效地测试和调整他们的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值