stm32裸机实现mqtt遇到的问题和需要注意的地方
Posted Enoch0423
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了stm32裸机实现mqtt遇到的问题和需要注意的地方相关的知识,希望对你有一定的参考价值。
stm32实现mqtt的发布和订阅遇到的问题和需要注意的地方
本文中使用的是正点原子的stm32f407系列,使用的是KEIL+stm32cubemx的构建方式,其中硬件驱动和lwip可以通过stm32cubemx直接配置进来,由此可以减少在底层驱动上消耗时间,可以更加专注于应用层的开发。
1.首先要让stm32单片机连上网,同时要清楚自己的网线另一头是接在什么地方的,如果是路由器上那么他是支持DHCP的方式来动态分配分配IP地址的,但是如果是交换机那就要看交换机是否具有DHCP的功能,一般情况下交换机是没有DHCP功能的,那就只能使用静态IP的分配的方式,同时自己分配的网段要和你的主机一致,不然也是连不上网的。
同时还需要注意的是stm32f407有三个网络相关的引脚被复用了 他们分别是:
PG14 ETH_TXD1
PG13 ETH_TXD0
PG11 ETH_TX_EN
还有ETH_RESET引脚需要在以太网初始化时用到
PD3 ETH_RESET
ETH_RESET要在ethernetif.c文件下面的low_level_init()中使用到,如下面的代码所示
static void low_level_init(struct netif *netif)
{
HAL_StatusTypeDef hal_eth_init_status;
/* Init ETH */
uint8_t MACAddr[6] ;
heth.Instance = ETH;
heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
heth.Init.Speed = ETH_SPEED_100M;
heth.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
heth.Init.PhyAddress = LAN8720A_PHY_ADDRESS;
MACAddr[0] = 0x02;
MACAddr[1] = 0x00;
MACAddr[2] = 0x00;
MACAddr[3] = 0x00;
MACAddr[4] = 0x00;
MACAddr[5] = 0x00;
heth.Init.MACAddr = &MACAddr[0];
heth.Init.RxMode = ETH_RXPOLLING_MODE;
heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
/* USER CODE BEGIN MACADDRESS */
HAL_GPIO_WritePin(ETH_RESET_GPIO_Port,ETH_RESET_Pin,GPIO_PIN_RESET);
HAL_Delay(55);
HAL_GPIO_WritePin(ETH_RESET_GPIO_Port,ETH_RESET_Pin,GPIO_PIN_SET);
/* USER CODE END MACADDRESS */
*另外还有在while循环中一定要在250ms内调用一次MX_LWIP_Process()函数同时轮询调用 ethernetif_set_link(netif_default)函数,参数为extern struct netif netif_default;需要在main()函数之前定义好。
2.成功连上网后初始化mqtt有关变量,注意mqtt_info一定是全局变量,否则在后面的API函数中不能够作为参数进行传递。
mqtt_client_t* lwip_mqtt;
struct mqtt_connect_client_info_t mqtt_info =
{
"9999",
NULL,
NULL,
60,
NULL,
NULL,
0,
0
};
然后在所有驱动初始化完成之后调用下面的函数,给lwip_mqtt动态分配空间
lwip_mqtt = mqtt_client_new();
然后连接mqtt服务器
uint8_t mqtt_init(mqtt_client_t *client)
{
err_t err;
ip_addr_t my_ip_addr;
//memset(&mqtt_info,0,sizeof(mqtt_info));
ip4_addr_set_u32(&my_ip_addr, ipaddr_addr("XXX.XXX.XXX.XXX"));
//mqtt_set_inpub_callback(lwip_mqtt,mqtt_incoming_publish_cb,mqtt_incoming_data_cb,&mqtt_info);
/**********************************************************************************************************
mqtt_client_connect函数
arg1:mqtt句柄,arg2:mqtt服务器地址 arg3:端口号,arg4:连接回调函数,arg5:引入参数arg arg6:mqtt_info句柄
************************************************************************************************************/
err = mqtt_client_connect(client,(const ip_addr_t*) &my_ip_addr,XXXX,mqtt_connect_cb,&mqtt_info,&mqtt_info);
if(err != ERR_OK)
{
printf("mqtt connect err,ecode %d\\r\\n",err);
return -1;
}
return 0;
}
这里要注意:
1.原来使用IP4_ADDR()调试的时候发现得到的IP地址不正确,使用ip4_addr_set_u32()才是正确的
2.mqtt_set_inpub_callback一定要在mqtt_connect_cb()回调函数之后再使用,否则订阅的主题有发布的数据也不能够触发mqtt_incoming_data_cb()回调函数,怀疑是MQTT连接之前设置回调函数失败。
3.还有就是MEMP_NUM_SYS_TIMEOUT这个定义需要再加上2,就像下图所示,因为要将MQTT的超时时间算在里面。
然后是mqtt_connect_cb()回调函数
void mqtt_connect_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
LWIP_UNUSED_ARG(client);//消除警告
//struct mqtt_connect_client_info_t* client_info = (struct mqtt_connect_client_info_t*)arg;
mqtt_set_inpub_callback(lwip_mqtt,mqtt_incoming_publish_cb,mqtt_incoming_data_cb,arg);
if(status == MQTT_CONNECT_ACCEPTED)
{
printf("mqtt 连接成功\\r\\n");
/*订阅或者不订阅topic函数 参数1:client句柄,参数2:topic,参数3:qos,参数4:回调函数。参数5:是否订阅
1:sub 0:unsub
*/
mqtt_sub_unsub(client,"test123",0,mqtt_sub_request_cb,&mqtt_info,1);
}
}
LWIP_UNUSED_ARG()是为了消除参数未使用的警告。
下面这个回调函数是用来处理订阅和取消订阅的请求的
void mqtt_sub_request_cb(void *arg, err_t err)
{
printf("订阅成功\\r\\n");
printf("errcode:%d\\r\\n",err);
}
下面是mqtt_incoming_data_cb()回调函数,这个是用来处理订阅主题中所订阅到的信息是什么。
void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
printf("接受到的数据为:%s\\r\\n",data);
}
下面的回调函数是用来处理哪个主题发布的信息
void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
{
printf("Incoming publish at topic \\" %s \\" with total length %u\\r\\n", topic, (unsigned int)tot_len);
}
最后是实现定时发布的功能,一秒发布一次数据
u32_t timer;
u32_t current_timer;
char payload[20] = "hello everyone!";
timer = HAL_GetTick();
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
MX_LWIP_Process();
ethernetif_set_link(netif_default);
current_timer = HAL_GetTick();
if(current_timer-timer > 1000)
{
timer = current_timer;
/****************************************************************************************
MQTT 发布函数
arg1:mqtt句柄 arg2:发布主题 arg3:发布内容 arg4:发布内容大小 arg5:qos消息质量 arg6:医嘱消息
arg7:MQTT发布回调函数 arg8:引入参数。
***************************************************************************************/
mqtt_publish(lwip_mqtt,"test456",payload,strlen(payload),0,0,mqtt_pub_request_cb,&mqtt_info);
}
}
另外MQTT_OUTPUT_RINGBUF_SIZE是MQTT发布消息txbuffer的大小,MQTT_VAR_HEADER_BUFFER_LEN是MQTT订阅主题接收消息的rxbuffer的大小
MQTT_OUTPUT_RINGBUF_SIZE默认是256byte,MQTT_VAR_HEADER_BUFFER_LEN默认是128byte,如果要发送大一点的数据空间就不够了,可以修改大小但是不能改的过于大,自己尝试过1024byte就会出错,这个问题的原因是的opt.h中的MEM_SIZE不够了,stm32cubemx默认初始生成的是1600byte,我们可以对他进行修改,这样就能够满足大数据量的主题发布和接收了
以上是关于stm32裸机实现mqtt遇到的问题和需要注意的地方的主要内容,如果未能解决你的问题,请参考以下文章
9-STM32物联网开发WIFI+GPRS基础篇(STM32+SIM800实现MQTT远程通信控制)
8-STM32物联网开发WIFI+GPRS基础篇(STM32+GPRS(AT指令)实现MQTT远程通信控制)