STM32+LWIP protocol stack implements MQTT protocol and mounts it to EMQ_X_CLOUD platform

Posted by flipis on Tue, 15 Feb 2022 09:18:52 +0100

introduction

In the previous articles, I mainly used LWIP protocol stack to implement httpd server and some applications. In this article, we will implement another MQTT protocol which is widely used in the Internet of things. I won't talk about the definition of MQTT protocol. I mainly remember that MQTT protocol is a subscription / publishing protocol.

Cloud platform selection

In order to mount our devices to the cloud platform using MQTT protocol, we need to select the appropriate cloud platform in the first step. Alibaba cloud and Tencent cloud can both be used. Here I choose EMQ X Cloud platform , I saw this cloud platform on Zhihu. Now newcomers try it for 14 days for free.

After entering the control panel, we can see the specific operation interface and enter the monitoring option, where we can see our client connection.

For the specific use of this platform, I have found two main ways to connect to the client.

1. In the client connection guide, download the MQTT X tool


In this software, you can connect to our MQTT server.

2. Conduct online debugging on EMQ X Cloud platform


This is the debugging of the web version.

Both methods can be used as client connections. It can be used to test whether the cloud platform settings are correct and whether normal subscription / publishing can be carried out.

Code writing

Mqtt protocol stack is included in LWIP protocol stack. We can use it directly. The specific code is in mqtt C file, which is a relatively complete set of mqtt library. We only need to write the C file of mqtt client.
LWIP official gave a simple example, we follow Official course Write an mqtt client file named mqtt_client.c.
I wrote with freeRTOS operating system. Those without operating system can see the article of this big man Stm32h743zi + LwIP + mqtt (no OS)

#include <stdio.h>
#include <string.h>
#include "mqtt_client.h"
#include "mqtt.h"
#include "cmsis_os.h"

#define ipaddr_mqtt "120.79.215.239"
#define mqtt_usrname "897923957"
#define mqtt_password "XXXXX" / / because of EMQ_ The cloud platform must have an account password, so you should enter the password corresponding to the cloud platform in the

osThreadId_t mqttpub_TaskHandle;
//mqtt_client_t *client;

const osThreadAttr_t defaultTask_attribute = {
  .name = "defaultTask",
  .priority = (osPriority_t) osPriorityNormal,
  .stack_size = 128 * 4
};


void example_do_connect(mqtt_client_t *client);


/* Called when publish is complete either with sucess or failure */
static void mqtt_pub_request_cb(void *arg, err_t result)
{
  if(result != ERR_OK) {
    printf("Publish result: %d\n", result);
  }
}

void example_publish(mqtt_client_t *client, void *arg)
{
  const char *pub_payload= "PubSubHubLubJub";
  err_t err;
  u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
  u8_t retain = 0; /* No don't retain such crappy payload... */
  err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
  if(err != ERR_OK) {
    printf("Publish err: %d\n", err);
  }
}

static int inpub_id;
static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
{
  printf("Incoming publish at topic %s with total length %u\n", topic, (unsigned int)tot_len);

  /* Decode topic string into a user defined reference */
  if(strcmp(topic, "subtopic") == 0) {
    inpub_id = 0;
  } else if(topic[0] == 'A') {
    /* All topics starting with 'A' might be handled at the same way */
    inpub_id = 1;
  } else {
    /* For all other topics */
    inpub_id = 2;
  }
}

static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
  printf("Incoming publish payload with length %d, flags %u\n", len, (unsigned int)flags);

  if(flags & MQTT_DATA_FLAG_LAST) {
    /* Last fragment of payload received (or whole part if payload fits receive buffer
       See MQTT_VAR_HEADER_BUFFER_LEN)  */

    /* Call function or do action depending on reference, in this case inpub_id */
    if(inpub_id == 0) {
      /* Don't trust the publisher, check zero termination */
     // if(data[len-1] == 0) {
        printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
     // }
    } else if(inpub_id == 1) {
      /* Call an 'A' function... */
    } else {
      //printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
      printf("mqtt_incoming_data_cb: Ignoring payload...\n");
    }
  } else {
    /* Handle fragmented payload, store in buffer, write to file or whatever */
  }
}

static void mqtt_sub_request_cb(void *arg, err_t result)
{
  /* Just print the result code here for simplicity, 
     normal behaviour would be to take some action if subscribe fails like 
     notifying user, retry subscribe or disconnect from server */
  printf("Subscribe result: %d\n", result);
}

static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
    err_t err;
    if(status == MQTT_CONNECT_ACCEPTED) {
        printf("mqtt_connection_cb: Successfully connected\n");
        
        /* Setup callback for incoming publish requests */
        mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
        
        /* Subscribe to a topic named "subtopic" with QoS level 1, call mqtt_sub_request_cb with result */ 
        err = mqtt_subscribe(client, "subtopic", 1, mqtt_sub_request_cb, arg);

        if(err != ERR_OK) {
            printf("mqtt_subscribe return: %d\n", err);
        }
    } else {
        printf("mqtt_connection_cb: Disconnected, reason: %d\n", status);
        
        /* Its more nice to be connected, so try to reconnect */
        example_do_connect(client);
    }  
}

void example_do_connect(mqtt_client_t *client)
{
    struct mqtt_connect_client_info_t ci;
    ip_addr_t  ip_mqtt;
    err_t err;

    /* Setup an empty client info structure */
    memset(&ci, 0, sizeof(ci));
    
    /* Minimal amount of information required is client identifier, so set it here */ 
    ci.client_id = "lwip_test";

    ci.client_user = mqtt_usrname;
    ci.client_pass = mqtt_password;
    
    /* Initiate client and connect to server, if this fails immediately an error code is returned
        otherwise mqtt_connection_cb will be called with connection result after attempting 
        to establish a connection with the server. 
        For now MQTT version 3.1.1 is always used */

    /*    
    err = ipaddr_aton(ipaddr_mqtt,&ip_mqtt);
    if(err == 0)
        printf("cp could not be converted to addr!!!!!!\n");
    */
    IP4_ADDR(&ip_mqtt, 120, 79, 215, 239); //Fill in the ip address of your cloud platform here

    err = mqtt_client_connect(client, &ip_mqtt, MQTT_PORT, mqtt_connection_cb, 0, &ci);
		printf("mqtt_connect return %d\n", err);
    
    /* For now just print the result code if something goes wrong*/
    if(err != ERR_OK) {
        printf("mqtt_connect return %d\n", err);
    }
}

/*
void mqtt_pub_thread(void *argument)
{
    while (1)
    {
        example_publish(client, (void *)mqtt_pub_request_cb); 
        osDelay(1000);
    }
}
*/

  //example_do_connect(&static_client);
void mqtt_init(void)
{
    mqtt_client_t *client = mqtt_client_new();

    if(client != NULL) {
        example_do_connect(client);
    }
    //mqttpub_TaskHandle = osThreadNew(mqtt_pub_thread, NULL, &defaultTask_attribute);

    
    //example_publish(client, (void *)mqtt_pub_request_cb); // Why is this arg parameter assigned

    //mqtt_disconnect(client);

}

This is just a simple example to test the connection to the server. Call mqtt during initialization_ The init () function is OK.

experimental result

After calling the initialization function, we can see that the connection has been established and LwIP can be seen on the cloud platform_ Test is our STM32 MCU.

Messages published by other clients can also be received normally.

Note that client publishing needs to be published under the same theme. I use the theme of subtopic here.

So far, the experiment has been finished.

Pit record

In this experiment, there are mainly two stepping pits.

1,Assertion “sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty” failed at line 216 in src/core/timers.c error

Refer to this guy's article
resolvent
Change the CUBEMX option a little larger, or directly search the macro definition in the code and change it yourself. The default seems to be 3. I changed it to 13

2. The program crashes for no reason

It is found that the problem in the previous article is the same. The lwip stack is set too small in lwipopts H medium to large.

summary

The MQTT protocol has been done over the past few days. It is relatively simple. If it is used in the future, it will be further studied. This is at most a demo for testing. The next task is to upgrade the OTA. The STM32 wired network is basically over. If it's helpful, please praise it.

Topics: IoT stm32 MQTT