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遇到的问题和需要注意的地方的主要内容,如果未能解决你的问题,请参考以下文章

STM32WB55 或 STM32F10 上的裸机编程

9-STM32物联网开发WIFI+GPRS基础篇(STM32+SIM800实现MQTT远程通信控制)

STM32F1 - 在裸机上使用主 SPI

8-STM32物联网开发WIFI+GPRS基础篇(STM32+GPRS(AT指令)实现MQTT远程通信控制)

STM32F103C8T6+ESP-01S+MQTT服务器实现数据上传和接收

STM32F405 裸机 SPI 从机 - MISO 数据有时会混乱