基于MQTT的移植

本文介绍了如何搭建EMQ MQTT服务器,包括下载源码、启动服务器及验证运行状态。接着,通过comnet调试工具测试了MQTT连接,并展示了在下位机上使用MQTT源码进行订阅和发布的实现过程,涉及TCP连接、数据打包及发送。此外,还讨论了数据打包时的大端模式问题。
摘要由CSDN通过智能技术生成

MQTT协议

第1步:搭建MQTT服务器,选择的是EMQ服务器,下载ErlangMqtt_win7_v2.3.9.zip服务器源码包,解压后如下图所示:第这里插入图片描述
第2步,按下win+R键输入cmd进入命令终端控制台,之后进入解压后的bin目录下,输入emqttd console命令会出现Erlang窗口。
在这里插入图片描述
第3步:输入emqttd.cmd start命令启动,启动后输入emqttd_ctl status命令查看服务器是否属于运行状态。如若停止服务器可以输入emqttd stop 命令。
在这里插入图片描述
第4步,进入浏览器,输入http://本地ip:18083进入EMQ服务器登陆界面。
输入用户名:admin 密码:public;进入到EMQ服务器界面。到这一步服务器就搭建完成。

接下来就利用客户端测试程序验证EMQ 服务器是否搭建成功。
第1步:下载通信猫调试工具comnet.zip,进行安装。
里插入图片描述
选择网络下的MQTT协议。输入服务器的IP和port:1883,因为MQTT基于tcp通信,服务器配置文件中tcp传输所默认的端口为1883.

第2步:点击启用,可以选择订阅/发布,进行数据传输。


	下面进行MQTT客户端在下位机上运行,而EMQ服务器在pc机上运行,能够达到下位机与上位机能够正确的通信。

第1步,下载MQTT的源码包MQTTPacket,将目录下的src下的所有文件以及samples下的transport.c和transport.h放入工程目录下。
在这里插入图片描述
samples目录下:
在这里插入图片描述

第2步,根据源码提供的接口函数完成在下位机的订阅和发布的测试代码。

点击提取MQTT资料
提取码:y8w2


通过在transport.c中,完成对移植过程中一些测试代码的编写,包括与tcp进行连接、订阅测试、发布测试、检查与服务器保持连接的任务函数。


void pingTask(void)
{
	unsigned char buf[10] = {0};
	int bufSize = sizeof(buf);
	int len = 0;
	while(1)
	{
		taskDelay(30000);
		len = MQTTSerialize_pingreq(buf, bufSize);
		transport_sendPacketBuffer(mysock, buf, bufSize);
	}
}

#if 0
int transport_open(char *addr,int port)
{
	int *sock = &mysock;
	struct sockaddr_in serv_addr;
	static struct timeval tv;
	int timeout = 1000;

	*sock = socket(AF_INET, SOCK_STREAM, 0);
	if(*sock < 0)
	{
		printf("socket failed.\n");
	}

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	serv_addr.sin_addr.s_addr = inet_addr(addr);

	if(connect(*sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
	{
		printf("connect failed.\n");
		return -1;
	}

	tv.tv_sec = 10;
	tv.tv_usec = 0;
	setsockopt(mysock, SOL_SOCKET,SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));

	return mysock;
	
}

#else
void TcpConnect(const char *host, int port)
{
	struct sockaddr_in serv_addr;

	mysock = socket(AF_INET, SOCK_STREAM, 0);
	if(mysock < 0)
	{
		printf("socket failed.\n");
	}

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	serv_addr.sin_addr.s_addr = inet_addr(host);

	int res = connect(mysock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
	if(res < 0)
	{
		printf("%s,Tcp connected failed.\n",__FUNCTION__);
	}
	else
	{
		printf("%s,Tcp connected success.\n",__FUNCTION__);
	}
	
}
#endif

void subscribeTest()
{
	int taskId = 0;
	char *host = "192.168.1.8";
	int port = 1883;
#if 0
	mysock = transport_open(host, port);
#else
	TcpConnect(host, port);
#endif

	//初始化连接参数
	unsigned char buf[256] = {0};
	int bufSize = sizeof(buf);
	MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
	data.clientID.cstring = "zj";    //客户ID
	data.keepAliveInterval = 30;     //心跳数,保持存活时间
	data.cleansession = 1;			 //清除会话
	data.username.cstring = "admin"; //用户名
	data.password.cstring = "public";//用户密码
	data.MQTTVersion = 4;			 //MQTT版本号

	//打包connect请求数据(报文)
	int len = MQTTSerialize_connect(buf, bufSize, &data);

	//发送请求报文
	int rc = transport_sendPacketBuffer(mysock, (unsigned char *)buf, len);
	if(rc == len)
	{
		printf("c->s send CONNECT success.\n");
		//printf("len = %d,bufSize = %d\n",len,bufSize);
	}
	else
	{
		printf("c->s send CONNECT failed.\n");
		exit(-1);
	}

	//等待服务器的CONNACK,等待服务器接到请求后,返回的请求应答数据
	if(MQTTPacket_read(buf, bufSize, transport_getdata) == CONNACK)
	{
		unsigned char sessionPresent,connack_rc;
		if(MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, bufSize) != 1 || connack_rc != 0)
		{
			printf("Unable to connect,return code %d\n",connack_rc);
		}
		else
		{
			printf("Able to connect.\n");
		}
	}

	//创建ping任务,30秒ping一次
	taskId = taskSpawn("pingtask", 200, 0, 0x30000, (FUNCPTR)pingTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

	//订阅主题
	MQTTString topicString = MQTTString_initializer;
	int msgId = 1;                  //消息ID 
	int req_qos = 0;				//消息质量
	topicString.cstring = "hello";  //消息主题

	//打包subscribe数据
	len = MQTTSerialize_subscribe(buf, bufSize, 0, msgId, 1, &topicString, &req_qos);

	//发送subscribe数据
	rc = transport_sendPacketBuffer(mysock, (unsigned char *)buf, len);
	while(1)
	{
		len = transport_getdatanb(&mysock, buf, bufSize);
		if(buf[0] > 0)
		{
			if((buf[0] & 0xF0) == 0x30)
			{
				//解析publish数据
				unsigned char dup; //重发标志
				int qos;           //服务质量等级
				unsigned char retained; //保留标志
				unsigned short msgid;
				int payloadlen_in;
				unsigned char *payload_in; //指向payload

				MQTTString recvTopic;
				MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &recvTopic, &payload_in, &payloadlen_in, buf, bufSize);
				printf("arrived message %.*s\n",payloadlen_in,payload_in);
			}
		}	
	}
}


void publishTest()
{
	char *host = "192.168.1.8";
	int port = 1883;
	mysock = transport_open(host,port);
	unsigned char buf[256] = {0};
	int bufSize = sizeof(buf);
	MQTTPacket_connectData data = MQTTPacket_connectData_initializer;

	data.clientID.cstring = "jz";  
	data.keepAliveInterval = 30;    
	data.username.cstring = "admin"; 
	data.password.cstring = "public";

	//打包connect请求数据(报文)
	int len = MQTTSerialize_connect(buf, bufSize, &data);

	//发送请求报文
	int rc = transport_sendPacketBuffer(mysock, (unsigned char *)buf, len);
	if(rc == len)
	{
		printf("c->s send CONNECT success.\n");
		//printf("len = %d,bufSize = %d\n",len,bufSize);
	}
	else
	{
		printf("c->s send CONNECT failed.\n");
		exit(-1);
	}
	
	//等待服务器的CONNACK,等待服务器接到请求后,返回的请求应答数据
	if(MQTTPacket_read(buf, bufSize, transport_getdata) == CONNACK)
	{
		unsigned char sessionPresent,connack_rc;
		if(MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, bufSize) != 1 || connack_rc != 0)
		{
			printf("Unable to connect,return code %d\n",connack_rc);
		}
		else
		{
			printf("Able to connect.\n");
		}
	}
	int taskId = 0;
	//创建ping任务,30秒ping一次
	taskId = taskSpawn("pingtask", 200, 0, 0x30000, (FUNCPTR)pingTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

	MQTTString topicString = MQTTString_initializer;
	topicString.cstring = "topic";
	char *payload = "work";
	while(1)
	{
		taskDelay(4000);

		len = MQTTSerialize_publish(buf, bufSize, 0 , 0, 0, 0, topicString, (unsigned char *)payload, strlen(payload));
		transport_sendPacketBuffer(mysock, buf, len);
		memset(buf, 0, len);
		printf("publish send work\n");
	}
}

个人总结:
在打包数据时,需考虑大端还是小端模式,在移植时tcp已经能够连接,但一直无法与服务器连接,最终通过利用网络调试助手获取能够连接服务器的connect报文和不能连接的connect报文,将两者数据进行比较,发现在打包数据时,是要用到大端模式,所以在MQTTPacket.h中需要将union这个共用体中的第二个结构体注释,使用第一个结构体:大端模式.

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值