Use multi-tasking esp32

Hello! Please help me with the code. I have a problem with controlling light bulbs while connecting or reconnecting wifi or mqtt. That is, if I am reconnecting wifi or mqtt, I cannot use the buttons to control the light bulbs. Thank you very much in advance)

#include <ezButton.h>
#include <WiFi.h>
#include <PubSubClient.h>

// WiFi
const char *ssid = "KSI";
const char *password = "!#ksi33#!";

// MQTT Broker
const char *mqtt_broker = "192.168.82.43";
const char *topic_1 = "esp32/led1";
const char *topic_2 = "esp32/led2";
const char *mqtt_username = "smart";
const char *mqtt_password = "Trimble2228";
const int mqtt_port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);


/// constants won't change
const int BUTTON_PIN_1 = 26;
const int BUTTON_PIN_2 = 25;
const int LED_PIN_1 = 4;
const int LED_PIN_2 = 2;

ezButton button_1(BUTTON_PIN_1);
ezButton button_2(BUTTON_PIN_2);

// variables will change:
int ledState_1 = LOW;   // the current state of LED
int ledState_2 = LOW;

void setup() {
  Serial.begin(115200);
  WiFi.disconnect(true);
  delay(1000);
  WiFi.onEvent(WiFiStationDisconnected, SYSTEM_EVENT_STA_DISCONNECTED);
  pinMode(LED_PIN_1, OUTPUT);
  pinMode(LED_PIN_2, OUTPUT);
  button_1.setDebounceTime(50);
  button_2.setDebounceTime(50);
  
  setup_wifi();
  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  button_1.loop();
  button_2.loop();
  
  if(button_1.isPressed()) {
    ledState_1 = !ledState_1;
    digitalWrite(LED_PIN_1, ledState_1);
    char val[5];
    itoa(ledState_1, val, 10);
    client.publish("esp32/led1_st", val);
  }
  if(button_2.isPressed()) {
    ledState_2 = !ledState_2;
    digitalWrite(LED_PIN_2, ledState_2);
    char val[5]; 
    itoa(ledState_2, val, 10);
    client.publish("esp32/led2_st", val);
  }
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  if (String(topic) == "esp32/led1") {
    Serial.print("Changing led1_st to ");
    if(messageTemp == "1"){
      Serial.println("on");
      digitalWrite(LED_PIN_1, HIGH);
      ledState_1 = HIGH;
    }
    else if(messageTemp == "0"){
      Serial.println("off");
      digitalWrite(LED_PIN_1, LOW);
      ledState_1 = LOW;
    }
  }
  if (String(topic) == "esp32/led2") {
    Serial.print("Changing led2_st to ");
    if(messageTemp == "1"){
      Serial.println("on");
      digitalWrite(LED_PIN_2, HIGH);
      ledState_2 = HIGH;
    }
    else if(messageTemp == "0"){
      Serial.println("off");
      digitalWrite(LED_PIN_2, LOW);
      ledState_2 = LOW;
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()){
    String client_id = "esp32-client-";
    //client_id += String(WiFi.macAddress());
    if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
      client.subscribe(topic_1);
      client.subscribe(topic_2);
    } else {
      delay(2000);  
    }
  }
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("Disconnected from WiFi access point");
  Serial.print("WiFi lost connection. Reason: ");
  Serial.println(info.disconnected.reason);
  Serial.println("Trying to Reconnect");
  setup_wifi();
}

Do you mean that after you have reconnected you cannot use the buttons to control the lights ?

You will not, of course, be able to control the lights whilst the reconnections are taking place if that is what you mean

Perhaps on a multi-tasking / multi-core ESP32.

Yes, but the code is not written to use 2 cores, is it ?

Excuse me, please. I didn't quite understand how to implement multithreading in my code. Please help with this.

ESP32 runs FreeRTOS. Here is some reference material.

Just be aware that ESP32 FreeRTOS has some modifications to the vanilla version, mostly to support two cores. You can read about those starting here.

Perhaps more to the point, why do you care about this? Just wait until after WiFi / MQTT connection to use your buttons. It doesn't take that long.

I agree. What is the urgency behind controlling the lights and in any case, you have delay()s in both the connection functions

There is also the danger of the local state of the lights getting out of step with what is reported to the MQTT broker. What is the broker going to do with the information anyway ?

I want to do this because I need to control the buttons in any way (whether wifi or mqtt works or not). But due to the fact that when wifi is turned off, the code enters the reconnect loop and I cannot use the buttons.

I found a code suitable for me on the Internet, but it still does not work for me. Help with it please.

void keepWiFiAlive(void * parameter){
    for(;;){
        if(WiFi.status() == WL_CONNECTED){
            vTaskDelay(10000 / portTICK_PERIOD_MS);
            continue;
        }

        Serial.println("[WIFI] Connecting");
        WiFi.mode(WIFI_STA);
        WiFi.begin(ssid, password);

        unsigned long startAttemptTime = millis();

        // Keep looping while we're not connected and haven't reached the timeout
        while (WiFi.status() != WL_CONNECTED && 
                millis() - startAttemptTime < 20000){}

        // When we couldn't make a WiFi connection (or the timeout expired)
      // sleep for a while and then retry.
        if(WiFi.status() != WL_CONNECTED){
            Serial.println("[WIFI] FAILED");
            vTaskDelay(30000 / portTICK_PERIOD_MS);
        continue;
        }

        Serial.println("[WIFI] Connected: " + WiFi.localIP());
    }
}
xTaskCreatePinnedToCore(
  keepWiFiAlive,
  "keepWiFiAlive",  // Task name
  5000,             // Stack size (bytes)
  NULL,             // Parameter
  1,                // Task priority
  NULL,             // Task handle
  0
);

full code:

#include <ezButton.h>
#include <WiFi.h>
#include <PubSubClient.h>

// WiFi
const char *ssid = "KSI";
const char *password = "!#ksi3#!";

// MQTT Broker
const char *mqtt_broker = "192.168.82.43";
const char *topic_1 = "esp32/led1";
const char *topic_2 = "esp32/led2";
const char *mqtt_username = "smart";
const char *mqtt_password = "Trimble2228";
const int mqtt_port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);


/// constants won't change
const int BUTTON_PIN_1 = 26;
const int BUTTON_PIN_2 = 25;
const int LED_PIN_1 = 4;
const int LED_PIN_2 = 2;

ezButton button_1(BUTTON_PIN_1);
ezButton button_2(BUTTON_PIN_2);

// variables will change:
int ledState_1 = LOW;   // the current state of LED
int ledState_2 = LOW;

void setup() {
  Serial.begin(115200);
  
  pinMode(LED_PIN_1, OUTPUT);
  pinMode(LED_PIN_2, OUTPUT);
  
  button_1.setDebounceTime(50);
  button_2.setDebounceTime(50);

xTaskCreatePinnedToCore(
  keepWiFiAlive,
  "keepWiFiAlive",  // Task name
  5000,             // Stack size (bytes)
  NULL,             // Parameter
  1,                // Task priority
  NULL,             // Task handle
  0
);
  
  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  button_1.loop();
  button_2.loop();
  
  if(button_1.isPressed()) {
    ledState_1 = !ledState_1;
    digitalWrite(LED_PIN_1, ledState_1);
    char val[5];
    itoa(ledState_1, val, 10);
    client.publish("esp32/led1_st", val);
  }
  if(button_2.isPressed()) {
    ledState_2 = !ledState_2;
    digitalWrite(LED_PIN_2, ledState_2);
    char val[5]; 
    itoa(ledState_2, val, 10);
    client.publish("esp32/led2_st", val);
  }
}

void keepWiFiAlive(void * parameter){
    for(;;){
        if(WiFi.status() == WL_CONNECTED){
            vTaskDelay(10000 / portTICK_PERIOD_MS);
            continue;
        }

        Serial.println("[WIFI] Connecting");
        WiFi.mode(WIFI_STA);
        WiFi.begin(ssid, password);

        unsigned long startAttemptTime = millis();

        // Keep looping while we're not connected and haven't reached the timeout
        while (WiFi.status() != WL_CONNECTED && 
                millis() - startAttemptTime < 20000){}

        // When we couldn't make a WiFi connection (or the timeout expired)
      // sleep for a while and then retry.
        if(WiFi.status() != WL_CONNECTED){
            Serial.println("[WIFI] FAILED");
            vTaskDelay(30000 / portTICK_PERIOD_MS);
        continue;
        }

        Serial.println("[WIFI] Connected: " + WiFi.localIP());
    }
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  if (String(topic) == "esp32/led1") {
    Serial.print("Changing led1_st to ");
    if(messageTemp == "1"){
      Serial.println("on");
      digitalWrite(LED_PIN_1, HIGH);
      ledState_1 = HIGH;
    }
    else if(messageTemp == "0"){
      Serial.println("off");
      digitalWrite(LED_PIN_1, LOW);
      ledState_1 = LOW;
    }
  }
  if (String(topic) == "esp32/led2") {
    Serial.print("Changing led2_st to ");
    if(messageTemp == "1"){
      Serial.println("on");
      digitalWrite(LED_PIN_2, HIGH);
      ledState_2 = HIGH;
    }
    else if(messageTemp == "0"){
      Serial.println("off");
      digitalWrite(LED_PIN_2, LOW);
      ledState_2 = LOW;
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()){
    String client_id = "esp32-client-";
    //client_id += String(WiFi.macAddress());
    if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
      client.subscribe(topic_1);
      client.subscribe(topic_2);
    } else {
      delay(2000);  
    }
  }
}

i found this function on this site: ESP32: Keep WiFi connection alive with a FreeRTOS task | Savjee.be

ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5856
entry 0x400806a8
[WIFI] Connecting
assertion "Invalid mbox" failed: file "/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/tcpip.c", line 374, function: tcpip_send_msg_wait_sem
abort() was called at PC 0x400ed51b on core 1

ELF file SHA256: 0000000000000000

Backtrace: 0x400885a0:0x3ffb1d20 0x4008881d:0x3ffb1d40 0x400ed51b:0x3ffb1d60 0x40119cfb:0x3ffb1d90 0x40119595:0x3ffb1dc0 0x40119748:0x3ffb1de0 0x40110434:0x3ffb1e20 0x400d110a:0x3ffb1e40 0x400d0b72:0x3ffb1e90 0x40140471:0x3ffb1ec0 0x400d202e:0x3ffb1ee0 0x400d2212:0x3ffb1f20 0x400d089f:0x3ffb1f50 0x400d0907:0x3ffb1f80 0x400d2e85:0x3ffb1fb0 0x4008982e:0x3ffb1fd0

Decoding stack results
0x400885a0: invoke_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c line 156
0x4008881d: abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c line 171
0x400ed51b: __assert_func at ../../../.././newlib/libc/stdlib/assert.c line 63
0x40119cfb: tcpip_send_msg_wait_sem at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/tcpip.c line 374
0x40119595: netconn_apimsg at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/api_lib.c line 109
0x40119748: netconn_new_with_proto_and_callback at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/api_lib.c line 139
0x40110434: lwip_socket at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/sockets.c line 1587
0x400d110a: WiFiClient::connect(IPAddress, unsigned short, int) at D:\���������\ArduinoData\packages\esp32\hardware\esp32\1.0.6/tools/sdk/include/lwip/lwip/sockets.h line 593
0x400d0b72: WiFiClient::connect(char const*, unsigned short, int) at D:\���������\ArduinoData\packages\esp32\hardware\esp32\1.0.6\libraries\WiFi\src\WiFiClient.cpp line 281
0x40140471: WiFiClient::connect(char const*, unsigned short) at D:\���������\ArduinoData\packages\esp32\hardware\esp32\1.0.6\libraries\WiFi\src\WiFiClient.cpp line 273
0x400d202e: PubSubClient::connect(char const*, char const*, char const*, char const*, unsigned char, bool, char const*, bool) at C:\Users\user\Documents\Arduino\libraries\pubsubclient\src\PubSubClient.cpp line 192
0x400d2212: PubSubClient::connect(char const*, char const*, char const*) at C:\Users\user\Documents\Arduino\libraries\pubsubclient\src\PubSubClient.cpp line 170
0x400d089f: reconnect() at D:\User\Рабочий стол\primer\button-relay/button-relay.ino line 161
0x400d0907: loop() at D:\User\Рабочий стол\primer\button-relay/button-relay.ino line 65
0x400d2e85: loopTask(void*) at D:\���������\ArduinoData\packages\esp32\hardware\esp32\1.0.6\cores\esp32\main.cpp line 23
0x4008982e: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c line 143

I think that's likely the problem … you're hoping to "find" a code on the internet. It seems you don't have the required experience level to write and debug multi-threaded code running on a real time operating system. Things are not likely to go well unless you're willing to spend a great deal of time working your way up a steep learning curve.

I think you would stand a better chance of success by going back to your original code and restructuring it to use standard "Arduino-type multitasking" techniques with millis()-based timing. There's no reason your WiFi and MQTT reconnect function need to be blocking. Do some Google searching on "Arduino State Machines". Also, it makes absolutely no sense to stay in a blocking loop trying to reconnect to MQTT without first making sure that the WiFi is connected.

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