raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.24k stars 837 forks source link

No MQTT transmission after some time #1725

Open HarterHorst opened 3 weeks ago

HarterHorst commented 3 weeks ago

System Info:

Pico SDK Version : 1.5.1
lwIP Release     : 2.2.0d
Version: 7.95.49 (2271bb6 CY) CRC: b7a28ef3 Date: Mon 2021-11-29 22:50:27 PST Ucode Ver: 1043.2162 FWID 01-c51d9400
cyw43 loaded ok, mac d8:3a:dd:2e:36:38
API: 12.2
Data: RaspberryPi.PicoW
Compiler: 1.29.4
ClmImport: 1.47.1
Customization: v5 22/06/24

Hi all,

I am working on a larger project with the Pico and I'd like to use MQTT. I experienced some problems with the LwIP MQTT function using the PICO W in no_os mode. I have written a simple script that pushes a MQTT message every minute as a kind of alive packet. This works as intended, but after some time the transmission stops. I've turned on the debug functions to get an idea, but I can't find the cause. All parameters seem to be fine and the statistics show no memory shortage or other resource related problems.

I have observed the following behaviour.

  1. The message "tcp_output: nothing to send (00000000)" appears every second, so I have 60 of these messages in the log between the various MQTT messages. After some time the number of messages reduces to just one message between the MQTT messages. Compare line 1816 and following with line 2014.
  2. The mqtt callback function returns the following error: "mqtt_pub_request_cb: Publish result: -3 which indicates a timeout problem due to the documentation. see line 4253.
  3. The IP stack seems fine as I can ping the device.

Any ideas what is wrong here or any ideas how to get more information from the debug system? code and lwipopts.h are below. minicom log is attached.

minicom.cap.zip

regards Andi

#define LWIP_PLATFORM_ASSERT(message) custom_assert_handler(message)

#define MQTT_BROKER_IP_ADDR "192.168.0.3"
#define MQTT_TOPIC_ALIVE "home/IoT/rp2040/alive"
#define MQTT_BROKER_PORT 1883
#define MQTT_CLIENT_ID "RP2040-mmWave"

#define TimeBetweenAlivePackets 1000*60*1           // Every minute

#include <stdio.h>
#include <string.h>
#include <time.h>

// Pico Standard libraries
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"

// lwip libraries
#include "lwip/apps/mqtt.h"
#include "lwip/timeouts.h"
#include "lwip/init.h"
#include "lwip/stats.h"

// my own functions
#include "networkinfo.h"
#include "showversions.h"

const char *WIFI_SSID = "<REMOVED>";
const char *WIFI_PASSWORD = "<REMOVED>";

ip_addr_t mqtt_ip;

struct mqtt_connect_client_info_t mqtt_client_info;
char AliveMessage[] = "Alive";

err_t err;
uint8_t DeviceStatus;

struct repeating_timer timer_system_alive;

void init_wifi() {
    struct netif *netif = &cyw43_state.netif[0];
    ip4_addr_t ipaddr, netmask, gw;
    IP4_ADDR(&ipaddr, 192, 168, 0, 38);
    IP4_ADDR(&netmask, 255, 255, 255, 0);
    IP4_ADDR(&gw, 192, 168, 0, 1);
    netif_set_addr(netif, &ipaddr, &netmask, &gw);

    cyw43_arch_enable_sta_mode();
    if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 10000)) {
        printf("Failed to connect to WiFi\n");
        return;
    }

    printf("Connected to WiFi\n");
}

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);
}

void custom_assert_handler(const char *message) {
    printf("Assertion failed: %s\n", message);
    abort();  // Stop execution, or you can define any other appropriate action
}

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, "print_payload") == 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: Ignoring payload...\n");
    }
  } else {
    /* Handle fragmented payload, store in buffer, write to file or whatever */
  }
}

static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status) {
    const struct mqtt_connect_client_info_t* client_info = (const struct mqtt_connect_client_info_t*)arg;
    printf("MQTT cb : Client \"%s\" : status %d.\n", client_info->client_id, (int)status);

    err_t err;
    if(status == MQTT_CONNECT_ACCEPTED) {
        printf("mqtt_connection_cb: Successfully connected\n");
        mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);

        /*
        err = mqtt_subscribe(client, MQTT_TOPIC_ALIVE, 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);

        // example_do_connect(client);
    }  
}

static void mqtt_pub_request_cb(void *arg, err_t result)
{
  if(result != ERR_OK) {
    printf("mqtt_pub_request_cb: Publish result: %d\n", result);
  } else {
    printf("mqtt_pub_request_cb: Callback OK\n");
  }
  printf ("-------------------------------------------------------\n");
}

static void mqtt_connect(mqtt_client_t *mqtt_client) {
    cyw43_arch_lwip_begin();

    err = mqtt_client_connect(
        mqtt_client, 
        &mqtt_ip,
        MQTT_BROKER_PORT,
        mqtt_connection_cb,
        &mqtt_client_info,
        &mqtt_client_info);

    cyw43_arch_lwip_end();

    printf ("MQTT Connect Code: %i\n",err);
    printf ("MQTT Port %i\n",MQTT_PORT);

    u8_t constatus = mqtt_client_is_connected (mqtt_client);
    printf ("MQTT Connection Status: %i\n",constatus);
}

bool timer_cb_SendAlivePacket(struct repeating_timer *t) {

    mqtt_client_t *mqtt_client = (mqtt_client_t *)t->user_data;
    u8_t qos = 0;
    u8_t retain = 0;
    printf ("-------------------------------------------------------\n");

    int tcpip_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
    switch (tcpip_status) {
        case CYW43_LINK_DOWN:
            printf("TCP/IP link status: DOWN\n");
            break;
        case CYW43_LINK_JOIN:
            printf("TCP/IP link status: JOIN\n");
            break;
        case CYW43_LINK_NOIP:
            printf("TCP/IP link status: NOIP\n");
            break;
        case CYW43_LINK_UP:
            printf("TCP/IP link status: UP\n");
            break;
        case CYW43_LINK_FAIL:
            printf("TCP/IP link status: FAIL\n");
            break;
        case CYW43_LINK_NONET:
            printf("TCP/IP link status: NONET\n");
            break;
        case CYW43_LINK_BADAUTH:
            printf("TCP/IP link status: BADAUTH\n");
            break;
        default:
            printf("TCP/IP link status: UNKNOWN (%d)\n", tcpip_status);
            break;
    }

    int status = cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA);
    switch (status) {
        case CYW43_LINK_DOWN:
            printf("WiFi link status: DOWN\n");
            break;
        case CYW43_LINK_JOIN:
            printf("WiFi link status: JOIN\n");
            break;
        case CYW43_LINK_NOIP:
            printf("WiFi link status: NOIP\n");
            break;
        case CYW43_LINK_UP:
            printf("WiFi link status: UP\n");
            break;
        case CYW43_LINK_FAIL:
            printf("WiFi link status: FAIL\n");
            break;
        case CYW43_LINK_NONET:
            printf("WiFi link status: NONET\n");
            break;
        case CYW43_LINK_BADAUTH:
            printf("WiFi link status: BADAUTH\n");
            break;
        default:
            printf("WiFi link status: UNKNOWN (%d)\n", status);
            break;
    }

    stats_display();

    while (mqtt_client_is_connected(mqtt_client)!=1) {
        printf ("Reconnecting to mqtt broker...\n");
        mqtt_connect(mqtt_client);
        busy_wait_ms(4000);
    }

    cyw43_arch_lwip_begin();

    err = mqtt_publish(mqtt_client, MQTT_TOPIC_ALIVE, AliveMessage, strlen(AliveMessage), qos, retain, mqtt_pub_request_cb, 0);

    cyw43_arch_lwip_end();

    if (err != ERR_OK) {
        printf("Publish err: %d\n", err);
    } else {printf ("**** MQTT Message has been sent. ****\n");}
    //MEM_STATS_DISPLAY();
    printf ("-------------------------------------------------------\n");

    return true;
}

int main() {
    stdio_init_all();

    show_software_versions();

    printf ("Starting WLAN initialization ...\n");
    if (cyw43_arch_init_with_country(CYW43_COUNTRY_GERMANY)) {
        printf("failed to initialise\n");
        return 1;
    }
    cyw43_arch_enable_sta_mode();
    cyw43_wifi_pm(&cyw43_state, CYW43_DEFAULT_PM & ~0xf);

    init_wifi();

    uint8_t LinkStatus = cyw43_tcpip_link_status (&cyw43_state, CYW43_ITF_STA);
    printf ("   - Link Status: %i\n",LinkStatus);

    print_ip_information();

    memset(&mqtt_client_info, 0, sizeof(mqtt_client_info));
    mqtt_client_info.client_id = MQTT_CLIENT_ID;
    mqtt_client_info.keep_alive = 0; // 15 is default

    mqtt_client_info.will_topic = NULL;
    mqtt_client_info.will_msg = NULL;
    mqtt_client_info.will_qos = 0;
    mqtt_client_info.will_retain = 0;

    IP4_ADDR(&mqtt_ip, 192, 168, 0, 3);

    mqtt_client_t *mqtt_client = mqtt_client_new();
    mqtt_connect(mqtt_client);

    if (!add_repeating_timer_ms (TimeBetweenAlivePackets, timer_cb_SendAlivePacket, mqtt_client, &timer_system_alive)) {
        printf ("Error creating timer.\n");
    }

    while (true) {
        cyw43_arch_poll();
        sys_check_timeouts();
    }
    return 0;
}

| lwipopts.h

#define _LWIPOPTS_EXAMPLE_COMMONH_H

// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)

// allow override in some examples
#ifndef NO_SYS
#define NO_SYS                      1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET                 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC             1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC             0
#endif
#define MEM_ALIGNMENT               4
#define MEM_SIZE                    16000
#define MEMP_NUM_TCP_SEG            32
#define MEMP_NUM_ARP_QUEUE          10
#define MEMP_NUM_SYS_TIMEOUT        (LWIP_NUM_SYS_TIMEOUT_INTERNAL+4) 
#define PBUF_POOL_SIZE              24
#define LWIP_ARP                    1
#define LWIP_ETHERNET               1
#define LWIP_ICMP                   1
#define LWIP_RAW                    1
#define TCP_WND                     (8 * TCP_MSS)
#define TCP_MSS                     1460
#define TCP_SND_BUF                 (8 * TCP_MSS)
#define TCP_SND_QUEUELEN            ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK  1
#define LWIP_NETIF_LINK_CALLBACK    1
#define LWIP_NETIF_HOSTNAME         1
#define LWIP_NETCONN                0
#define MEM_STATS                   1
#define SYS_STATS                   0
#define MEMP_STATS                  1
#define LINK_STATS                  0
// #define ETH_PAD_SIZE                2
#define LWIP_CHKSUM_ALGORITHM       3
#define LWIP_DHCP                   0
#define LWIP_IPV4                   1
#define LWIP_TCP                    1
#define LWIP_UDP                    1
#define LWIP_DNS                    1
#define LWIP_TCP_KEEPALIVE          1
#define LWIP_NETIF_TX_SINGLE_PBUF   1
#define DHCP_DOES_ARP_CHECK         0
#define LWIP_DHCP_DOES_ACD_CHECK    0
#define LWIP_NOASSERT               0

#define LWIP_TCPIP_CORE_LOCKING     1

//#ifndef NDEBUG
#define LWIP_DEBUG                  //1
#define LWIP_STATS                  1
#define LWIP_STATS_DISPLAY          1
//#endif

#define LWIP_DBG_MIN_LEVEL          LWIP_DBG_LEVEL_ALL
#define LWIP_DBG_TYPES_ON           (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH)

#define ETHARP_DEBUG                LWIP_DBG_OFF
#define NETIF_DEBUG                 LWIP_DBG_ON
#define PBUF_DEBUG                  LWIP_DBG_OFF
#define API_LIB_DEBUG               LWIP_DBG_OFF
#define API_MSG_DEBUG               LWIP_DBG_OFF
#define SOCKETS_DEBUG               LWIP_DBG_OFF
#define ICMP_DEBUG                  LWIP_DBG_OFF
#define INET_DEBUG                  LWIP_DBG_OFF
#define IP_DEBUG                    LWIP_DBG_OFF
#define IP_REASS_DEBUG              LWIP_DBG_OFF
#define RAW_DEBUG                   LWIP_DBG_ON
#define MEM_DEBUG                   LWIP_DBG_ON
#define MEMP_DEBUG                  LWIP_DBG_ON
#define SYS_DEBUG                   LWIP_DBG_ON
#define TCP_DEBUG                   LWIP_DBG_ON
#define TCP_INPUT_DEBUG             LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG            LWIP_DBG_ON
#define TCP_RTO_DEBUG               LWIP_DBG_OFF
#define TCP_CWND_DEBUG              LWIP_DBG_OFF
#define TCP_WND_DEBUG               LWIP_DBG_OFF
#define TCP_FR_DEBUG                LWIP_DBG_OFF
#define TCP_QLEN_DEBUG              LWIP_DBG_OFF
#define TCP_RST_DEBUG               LWIP_DBG_OFF
#define UDP_DEBUG                   LWIP_DBG_OFF
#define TCPIP_DEBUG                 LWIP_DBG_ON
#define PPP_DEBUG                   LWIP_DBG_OFF
#define SLIP_DEBUG                  LWIP_DBG_OFF
#define DHCP_DEBUG                  LWIP_DBG_OFF

#define MQTT_DEBUG                  LWIP_DBG_ON

#endif /* __LWIPOPTS_H__ */