工具:正点原子的STM32F4探索者开发板。
准备:STLINK连接电脑和开发板。然后开发板上电,用一根网线连接开发板和路由器,电脑也连接到路由器上(有线或无线均可)。 然后用USB转miniUSB线连接电脑和开发板的串口1(注意接插件到串口1中间有个插针,需要用短路帽连接),然后打开串口调试助手,115200波特率,一直监视着就行。我用的sscom v5.13.1,打开网口调试助手,我也用的sscom,端口5001,一直监听着就行。
工程:用的实验6,然后把main中的TCP和UDP代码注释掉。
1.CUBE设置
2.KEIL设置
3.新建my_mqtt.c
#include "lwip/apps/mqtt.h"
#include "lwip/netif.h"
#include "lwip/dhcp.h"
#include "string.h"
#include "my_mqtt.h"
void MqttConStatus_Handle(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
void MqttSubPub_Handle(void *arg, err_t err);
void MqttRecTopic_Handle(void *arg, const char *topic, u32_t tot_len);
void MqttRecData_Handle(void *arg, const u8_t *data, u16_t len, u8_t flags);
char ClientId[] = "LwIPMqttId-1";
char ClientUser[] = "user1";
char ClientPass[] = "Aa123456789";
char topic[] = "TopicTest";//发布主题
char payload[] = "This is a test Info!";//发布主题的消息
char SubscribeTopic[] = "test1";//需要订阅的主题
//结构体参数分别是 客户端ID 用户名 用户密码 心跳时间 遗愿主题 遗愿消息 遗愿消息等级 有无遗愿
struct mqtt_connect_client_info_t MqttClientMess = {ClientId,ClientUser,ClientPass,60,NULL,NULL,0,0};
mqtt_client_t* MqttClient = NULL;
int MQTT_connect_flag = 0;//0表示未连接,1表示已连接
void MyMQTT_Test(void)
{
//static int connect_flag=0;
int aa;
char temp_data[100];
static int num_temp=0;
if(MqttClient == NULL){
MqttClient = mqtt_client_new();
}
if(MQTT_connect_flag ==0){
//准备MQTT服务器地址
ip4_addr_t MqttServer;
IP4_ADDR(&MqttServer,120,24,185,66);
//ip_addr_t server_ip;
//ip4_addr_set_u32(&server_ip, ipaddr_addr("120.76.100.197"));
//新建一个客户端连接到服务器需要的信息块,以及填充相应的信息
num_temp++;
sprintf(temp_data,"LwIPMqttId-%d",num_temp);
MqttClientMess.client_id = temp_data;
//连接到服务器方法 参数分别是:客户端 服务器地址 服务器端口 连接状态变化回调 回调的参数 客户端信息
aa = mqtt_client_connect(MqttClient,&MqttServer,11021,MqttConStatus_Handle,NULL,&MqttClientMess);
printf("MQTT开始连接:%d", aa);
if(aa == 0){
//注册订阅信息后 收到数据的回调 参数分别是:客户端 订阅主题的回调 数据回调 回调的参数
//mqtt_set_inpub_callback(MqttClient,mipcb,midcb,NULL);
//订阅主题 参数分别是:客户端 订阅的主题 主题的消息等级 订阅结果回调 回调参数
//mqtt_subscribe(MqttClient,SubscribeTopic,0,cb,"subscribe");
}
}
else{
if(mqtt_client_is_connected(MqttClient)){
//发布消息主体 参数:客户端 发布的主题 主题的消息 消息的长度 消息等级 服务器是否保留 发布结果回调 回调参数
printf("MQTT已连接");
mqtt_publish(MqttClient,topic,payload,strlen(payload),0,0,MqttSubPub_Handle,"publish");
}
else{
printf("MQTT未连接");
}
}
}
//客户端连接到服务状态 当状态发生变化时会进入这个回调
void MqttConStatus_Handle(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
//源码中所有状态的例举
printf("MQTT状态改变:%d",status);
switch((uint16_t)status)
{
case MQTT_CONNECT_ACCEPTED:
MQTT_connect_flag =1;
//注册订阅信息后 收到数据的回调 参数分别是:客户端 订阅主题的回调 数据回调 回调的参数
mqtt_set_inpub_callback(MqttClient,MqttRecTopic_Handle,MqttRecData_Handle,NULL);
//订阅主题 参数分别是:客户端 订阅的主题 主题的消息等级 订阅结果回调 回调参数
mqtt_subscribe(MqttClient,SubscribeTopic,0,MqttSubPub_Handle,"subscribe");
//mqtt_sub_unsub(client,"test123",0,mipcb,&Ci,1);
break; //已连接
case MQTT_CONNECT_REFUSED_PROTOCOL_VERSION:;break;
case MQTT_CONNECT_REFUSED_IDENTIFIER:;break;
case MQTT_CONNECT_REFUSED_SERVER:;break;
case MQTT_CONNECT_REFUSED_USERNAME_PASS:;break;
case MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_:;break;
case MQTT_CONNECT_DISCONNECTED:
MQTT_connect_flag =0;
break; //连接断开
case MQTT_CONNECT_TIMEOUT:
MQTT_connect_flag =0;
;break; //连接超时
default:break;
}
}
//订阅或者发布时的结果回调 arg是注册时给的参数 err是状态 通LwIP的状态
void MqttSubPub_Handle(void *arg, err_t err)
{
char* p = arg;
if(strcmp(p,"publish") == 0){
printf("发布反馈%d",err);
return;
}
else if(strcmp(p,"subscribe") == 0){
printf("订阅反馈%d",err);
return;
}
}
//订阅主题后 客户端收到服务器中包含的主题信息
void MqttRecTopic_Handle(void *arg, const char *topic, u32_t tot_len)
{
//Led2Flash();
printf("收到订阅的消息主题:%s",topic);
return;
}
//订阅主题后 客户端收到服务器中包含的数据信息
void MqttRecData_Handle(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
printf("收到订阅的消息数据:%s",data);
return;
}
void ethernetif_notify_conn_changed(struct netif *netif)
{
//printf("网络连接状态发生改变");
if(netif_is_link_up(netif)){
printf("网络连接状态发生改变:刚刚连接上");
netif_set_up(netif);
dhcp_start(netif);
}
else{
printf("网络连接状态发生改变:刚刚断开了");
}
}
4.新建my_mqtt.h
#ifndef _MYMQTT_H_
#define _MYMQTT_H_
#include "stdio.h"
void MyMQTT_Test(void);
#endif
5.main主循环添加
6.效果演示:
从以上代码可以看出来,定时器每5s检查MQTT连接状态,如果连接正常则发布消息“This is a test Info!”, 而我们用通信猫也是每隔5s发送一次sssss。从下图可以看到单片机端也是收到消息的。
并且,此代码还包含热插拔功能,系统启动时没插网线,后面插上可以自动恢复, 同样,正常连接状态下拔插也能恢复。
7.发送字节数修改
测试单片机接收通信猫(另一个MQTT客户端)发送的一个1000字节的数据,单片机收到后发布同样的数据,结果通信猫只收到了126字节的数据。
同时,单片机串口发出警告:Assertion "client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN" failed at line 683 in ../Middlewares/Third_Party/LwIP/src/apps/mqtt/mqtt.c
实测默认情况下,最多只能发送126字节的数据.
观察mqtt_opts.h文件:
#define MQTT_OUTPUT_RINGBUF_SIZE 256 //这表示发送的最大字节数
#define MQTT_VAR_HEADER_BUFFER_LEN 128 //这表示接收的最大字节数,
均修改为256后正常,实际发送230以内都还算正常。
都修改为512后单片机能接收,但发布报错-3(ERR_TIMEOUT)