MQTT stops publishing after a while

Hi all,

Below is the code I am running on a RP2040 Nano Connect.

A brief description of my code:

void loop() - If pin A3 is HIGH, pin 8 and pin 6 will switch HIGH. If pin A3 goes LOW, a delay timer(using millis) is activated to switch pin 8 LOW and successively activate another delay timer(using millis) to switch pin 6 LOW. The above can be considered as a critical task, hence should be executed regardless of Wifi and/or MQTT broker connection status.

void tele() - Non critical task, responsible for Wifi & MQTT connection and publishing values of pin A3, 6 & 8.

To achieve the expected outcome, I used the Scheduler library(Scheduler.h) which requires delay() & yield() to switch between tasks.

The issue I am having, it publishes MQTT messages for 30-40mins on average after which it stops publishing until a reboot.

I tried digging the internet for causes/solutions but could not find any regarding this problem. I am suspecting it might be related to the global structure of the code.

Has anyone encountered similar issue while publishing MQTT messages?

Actually, I am open to all suggestions even if it requires changing the entire structure of the code. Improvement regarding the overall efficiency of the code will also be appreciated.

Looking forward to the community's input.
Thank you. :smiley:

#include <Scheduler.h> 
#include <SPI.h>
#include <WiFiNINA.h>
#include <ArduinoMqttClient.h>
#include "arduino_secrets.h"

const int PSON = A3; // Voltage detection
const int SSR = 8; // Solid State Relay control
const int FAN = 6; // Fan control

unsigned long ssrTimer = 0; // SSR timer
unsigned long fanTimer = 0; // fan timer

int psState = 0; // PSON status
int ssState = 0; // SSR status
int fnState = 0; // FAN status

//Enter data in secret tab(arduino_secrets.h)
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

const char broker[] = "10.0.0.10";
int        port     = 1883;
const char topic[]  = "RP2040Nano/PSon";
const char topic2[]  = "RP2040Nano/SSR";
const char topic3[]  = "RP2040Nano/FAN";

void setup() {
  Serial.begin(9600);
    
  pinMode(PSON, INPUT);   // analog pin A3 as input
  pinMode(SSR, OUTPUT);   // digital pin 8 as output
  pinMode(FAN, OUTPUT);   // digital pin 6 as output

  digitalWrite(SSR, LOW);
  digitalWrite(FAN, LOW);

  // attempt to connect to WiFi network:
  Serial.print("Attempting to connect to WPA SSID: ");
  Serial.println(ssid);
  if (WiFi.begin(ssid, pass) != WL_CONNECTED) {
    Serial.println("You're not connected to the network");
    Serial.println();
  }
  else {  
  Serial.println("You're connected to the network");
  Serial.println();
  }

  // You can provide a username and password for authentication
  mqttClient.setUsernamePassword(SECRET_MQTT_USER, SECRET_MQTT_PASS);

  Serial.print("Attempting to connect to the MQTT broker: ");
  Serial.println(broker);
  if (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());
  }
  else {
    Serial.println("You're connected to the MQTT broker!");
    Serial.println();
  }

  Scheduler.startLoop(tele);
}

void loop() {

  psState = analogRead(PSON); // read analog input
  ssState = digitalRead(SSR); // read digital output
  fnState = digitalRead(FAN); // read digital output

  if (psState > 50)
    ssrTimer = 100; // delay SSR for 10 seconds
    psOFF(); // run psOFF 
  if (ssState == 1)
    fanTimer = 100; // delay Fan for 10 seconds
    fnOFF(); // run psOFF
  delay(100);
}

void psOFF()
{
  static unsigned long sspreviousTime;
  unsigned long sscurrentTime = millis(); // return time since board active

  if (sscurrentTime - sspreviousTime < 100) // move forward if 100 ms has elapsed
    return;

  sspreviousTime = sscurrentTime;

  if (!ssrTimer) {
    digitalWrite(SSR, LOW);   // if timer is done or still 0, set digital output LOW
  }
  else {
    digitalWrite(SSR, HIGH);  // set digital output HIGH
    digitalWrite(FAN, HIGH);  // set ditigal output HIGH
    ssrTimer--;
  }
}

void fnOFF()
{
  static unsigned long fnpreviousTime;
  unsigned long fncurrentTime = millis(); // return time since board active

  if (fncurrentTime - fnpreviousTime < 100) // move forward if 100 ms has elapsed
    return;

  fnpreviousTime = fncurrentTime;

  if (!fanTimer) {
    digitalWrite(FAN, LOW);   // if timer is done or still 0, set digital output LOW
  }
  else {
    digitalWrite(FAN, HIGH);  // set digital output HIGH
    fanTimer--;
  }
}

void tele(){
  if (WiFi.status() != WL_CONNECTED) {
    // Wifi disconnected, connect
    connectWiFi();
  }

  // call poll() regularly to allow the library to send MQTT and prevent disconnection by the broker
  mqttClient.poll();
  
  static unsigned long previousMillis;
  unsigned long currentMillis = millis();

   if (currentMillis - previousMillis >= 5000) {
    // save the last time a message was sent
    previousMillis = currentMillis;
  
    if (!mqttClient.connected()) {
      // MQTT client is disconnected, connect
      connectMQTT();
    }

    // send message, the Print interface can be used to set the message contents
    mqttClient.beginMessage(topic);
    mqttClient.print(psState);
    mqttClient.endMessage();

    mqttClient.beginMessage(topic2);
    mqttClient.print(ssState);
    mqttClient.endMessage();

    mqttClient.beginMessage(topic3);
    mqttClient.print(fnState);
    mqttClient.endMessage();   
  }
  yield(); 
}

void connectWiFi() {
  WiFi.end();
  Serial.print("Attempting to connect to SSID: ");
  Serial.print(ssid);
  Serial.println();
  
  while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
    // failed, retry
    Serial.print(".");
    delay(5000);
  }
  Serial.println();

  Serial.println("You're connected to the network");
  Serial.println();
}

void connectMQTT() {
  mqttClient.stop();
  Serial.print("Attempting to MQTT broker: ");
  Serial.print(broker);
  Serial.println();
  
  while (!mqttClient.connect(broker, port)) {
    // failed, retry
    Serial.print(".");
    delay(5000);
  }
  Serial.println();

  Serial.println("You're connected to the MQTT broker");
  Serial.println();
}

Why do you need to use the Scheduler library ?

I need the code to be responsive to pin A3's state (HIGH/LOW). Wifi and MQTT are not critical, if it cannot connect - try to reconnect and if it is connected - publish.

I have the content of void loop() operating on an Arduino Nano perfectly. I am planning
to swap the Nano for a RP2040 Nano Connect and wish to add MQTT. The code above is a result of merging the official example of Wi-Fi and MQTT connections with my already functioning code. I am aware that is not the right way to code, I guess it is the path to learning.

I am not sure how to work around delay() within Wi-Fi and MQTT reconnection code. Scheduler uses the delay() to switch back and forth to the critical task(Pin A3's State).

Do you think the Scheduler library might be the issue?

Sorry, but I have no experience of the Scheduler library

Do you have any idea how to work around these 2 while loop and delay(). I guess this would allow me to get rid of the Scheduler library. Hence, I would be able to call all functions within void loop() without being stuck in the event of Wi-Fi and/or MQTT disconnected.

Thank you.

You could use if instead of while and millis() timing in the dependant code block if you want to print the full stops at 5 second intervals

Thank you for the alternative. I thought the delay() was required for the proper functioning of WiFi.begin() and mqttClient.connect().

Below is a tidied up version of the initial code. I got rid of the Scheduler library, Serial() and delay(). And all functions are called within void loop().

It appears that mqttClient.setKeepAliveInterval(0); solved MQTT's irregular halt.

However, I am facing an issue regarding WiFi/MQTT reconnection portion. When WiFi and MQTT are connected, the code functions as expected. In the event that one is missing, it obstruct void loop().

I would be grateful if you could point me to a solution that would not block void loop() but periodically try reconnect WiFi/MQTT.

Thank you.

#include <SPI.h>
#include <WiFiNINA.h>
#include <ArduinoMqttClient.h>
#include "arduino_secrets.h"

const int PSON = A3;
const int SSR = 8;
const int FAN = 6;

unsigned long ssrTimer = 0;  // SSR timer
unsigned long fanTimer = 0;  // Fan timer

int psState = 0;   // PSON status
int ssState = 0;   // SSR status
int fnState = 0;   // FAN status

//Enter data in secret tab(arduino_secrets.h)
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);

const char broker[] = "10.0.0.2";
int port = 1883;
const char topic[] = "RP2040Nano/PSon";
const char topic2[] = "RP2040Nano/SSR";
const char topic3[] = "RP2040Nano/FAN";

void setup() {
  pinMode(PSON, INPUT);  // analog pin A3 as input
  pinMode(SSR, OUTPUT);  // digital pin 8 as output
  pinMode(FAN, OUTPUT);  // digital pin 6 as output

  digitalWrite(SSR, LOW);
  digitalWrite(FAN, LOW);

  // Attempt to connect to WiFi network:
  WiFi.begin(ssid, pass);

  mqttClient.setKeepAliveInterval(0);
  // Username and Password for MQTT authentication
  mqttClient.setUsernamePassword(SECRET_MQTT_USER, SECRET_MQTT_PASS);
  mqttClient.connect(broker, port);
}

void loop() {
  psState = analogRead(PSON);  // read analog input status
  ssState = digitalRead(SSR);  // read digital output status
  fnState = digitalRead(FAN);  // read digital output status

  // call poll() regularly to allow the library to send MQTT and prevent disconnection by the broker
  mqttClient.poll();

  if (psState > 50) {
    ssrTimer = 100;  // delay SSR for 10 seconds
  }

  psOFF();

  if (ssState == 1) {
    fanTimer = 100;  // delay Fan for 10 seconds
  }

  fnOFF();

  if (WiFi.status() != WL_CONNECTED) {
    WiFi.end();
    WiFi.begin(ssid, pass);
  }

  if (WiFi.status() == WL_CONNECTED && !mqttClient.connected()) {
    mqttClient.stop();
    mqttClient.connect(broker, port);
  }

  if (WiFi.status() == WL_CONNECTED && mqttClient.connected()) {
    tele();
  }
}

void psOFF() {
  static unsigned long sspreviousTime;
  unsigned long sscurrentTime = millis();  // return time since board active

  // move forward if 100 ms has elapsed
  if (sscurrentTime - sspreviousTime < 100) {
    return;
  }
  
  sspreviousTime = sscurrentTime;

  if (!ssrTimer) {
    digitalWrite(SSR, LOW);  // if timer is done or still 0, set digital output LOW
  } else {
    digitalWrite(SSR, HIGH);  // set digital output HIGH
    digitalWrite(FAN, HIGH);  // set ditigal output HIGH
    ssrTimer--;
  }
}

void fnOFF() {
  static unsigned long fnpreviousTime;
  unsigned long fncurrentTime = millis();  // return time since board active

  // move forward if 100 ms has elapsed
  if (fncurrentTime - fnpreviousTime < 100) {
    return;
  }

  fnpreviousTime = fncurrentTime;

  if (!fanTimer) {
    digitalWrite(FAN, LOW);  // if timer is done or still 0, set digital output LOW
  } else {
    digitalWrite(FAN, HIGH);  // set digital output HIGH
    fanTimer--;
  }
}

void tele() {
  static unsigned long previousMillis;
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= 10000) {
 
    previousMillis = currentMillis;

    // send message, the Print interface can be used to set the message contents
    mqttClient.beginMessage(topic);
    mqttClient.print(psState);
    mqttClient.endMessage();

    mqttClient.beginMessage(topic2);
    mqttClient.print(ssState);
    mqttClient.endMessage();

    mqttClient.beginMessage(topic3);
    mqttClient.print(fnState);
    mqttClient.endMessage();

    mqttClient.beginMessage(topic4);
    mqttClient.print(temp_deg);
    mqttClient.endMessage();
  }
}

I don't see why the loop() function should be blocked for longer than

 WiFi.begin(ssid, pass);

or

mqttClient.connect(broker, port);

takes to run. If they are blocking loop() then I don't know how that could be avoided because if a reconnection is needed then the function needs to be called.

I glanced through the WiFiNINA GitHub repo, I guess both WiFi.begin and Client.connect contain while loop().

No worries then, thank you for your inputs.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.