Two ESP32 in WiFi - Client Cannot Reconnect On Second loop()

Hi Arduino community, I'm building a project to read my pool temperature from inside.
The temperature Server is an M5Stamp reading a DS18B20 sensor that will be mounted into my Hayward sat cell control box for convenience.
I can connect to the ESP32 Server softAP with my Android phone, connect/disconnect and sent more that 10 commands a second with connect/disconnect in between, it's solid as a rock.
Issues begins when I try to connect the Client, the other ESP32 module.
I connects fine after a reset and can fetch de temperature JSON string fine but cannot reconnect at the second loop. client.connect(server, port, 2000) always return false from second connection.

Here is the part of the code related to Client loop().
This is unfinished test code as I need to test the system for stability and reliability before continue development.

I hope to have help to understand what's going on...
Thanks for your help.

#include <Arduino.h>
#include <stdio.h>
#include <strings.h>
#include <rtc_wdt.h>
#include <esp_system.h>
#include <M5StickCPlus.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>

#define RTC_WDT_TIMEOUT 5000  //Watchdog timer delay in milliseconds

/* Global variable declaration */
//Set your softAP access point network credentials and IP
const char* ssid = "PoolTempServer";  //SSID constant with constant char pointer. WiFi.begin only accept const char*
const char* password = "0123456789";  //Password constant with constant char pointer. WiFi.begin only accept const char*
const unsigned int port = 80;         //Port constant, server listening port
IPAddress server(192, 168, 1, 1);     //PoolTempServer IP address
WiFiClient client;

float tempCelcius = 0;
float tempFahrenheit = 0;

const char answerOK[17] = "{\"answer\":\"ok\"}";  //Output a crafted compatible JSON string, answer OK string with NULL end (16+1)
const byte allowedMaxCommChars = 100;             //Maximum number of chars allowed on the interface from an answer

bool reachedMaxWifiChars = false;   //Set to true if we reached the maximum number of chars allowed from an answer
bool wifiStringComplete = false;    //Whether the comamnd string is complete and available to be parsed

bool wifiTimeoutOccured = false;      //Set to true if the wifiCommEvent function timed out by waiting data for too long
bool wifiTimeoutStarted = false;      //Set to true if the wifiCommEvent function detected the start marker and started the verification of wifiCommEvent timeout
const word wifiTimeoutValue = 2000;   //Set the wifiCommEvent timeout value in ms to stop waiting incomming data for too long after the start marker
unsigned long wifiTimerStart = 0;     //Store the millis() timer first snapshot for commEvent timeout

unsigned long currentTime = 0;        //Current milli() snapshot time, used in general timeout calculation
unsigned long previousTime = 0;       //Previous milli() snapshot time, used in general timeout calculation

char wifiString[allowedMaxCommChars];    //wifiString array to store the received wifi data, destructed after been used
char answerString[allowedMaxCommChars];  //answerString is the received "serial" or "wifi" answer string
char answerToJSON[allowedMaxCommChars];  //answerToJSON is a copy of the answerString char array to be used by the answer functions and JSON error detection

/* Initialization function
 * Code is excuted once at reset and power ON
*/
void setup()
{
  /*rtc_wdt_protect_off();  //Disable RTC WDT write protection
  //Set stage 0 to trigger a system reset after RTC_WDT_TIMEOUT delay
  rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_SYSTEM);
  rtc_wdt_set_time(RTC_WDT_STAGE0, RTC_WDT_TIMEOUT);
  rtc_wdt_enable();     //Start the RTC WDT timer
  rtc_wdt_protect_on(); //Enable RTC WDT write protection*/

  esp_sleep_enable_ext0_wakeup(GPIO_NUM_37, LOW); //Set button A (top M5 button - GPIO37) to be used to get M5Stick out of deep sleep when pressed
	
	Serial.begin(115200); //Initialize serial communication at 115200 bits per second, 8 bits, no parity, 1 stop bit (SERIAL_8N1)
	WiFi.mode(WIFI_STA);    //Set WiFi mode to Station Mode
	delay(1000);            //Waiting a little for AP to start
	WiFi.begin(ssid, password, 1, NULL, false); //Channel #1, no BSSID, no automatic connect (done in loop)
  M5.begin();
  M5.Lcd.setRotation(3);  //Init LCD rotation horizontally, LCD at right of M5 button
  //M5.Beep.tone(3300);
  //delay(100);
  //M5.Beep.mute();
}


/* Main loop function, code is executed repeatedly
*/
void loop()
{
  if (WiFi.status() != WL_CONNECTED)
  {
    M5.Lcd.fillScreen(0x0000);  //Fill LCD screen with black color
    M5.Lcd.setCursor(0, 0, 2);  //X-Y, font #2
    M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);  //White text on black background
    M5.Lcd.setTextSize(1);
    M5.Lcd.println("Connecting to PoolTempServer...");
    Serial.println("Connecting to PoolTempServer..."); //DEBUG
    M5.Lcd.print("SSID: ");
    Serial.print("SSID: "); //DEBUG
    M5.Lcd.println(ssid);
    Serial.println(ssid); //DEBUG

    while (WiFi.status() != WL_CONNECTED)
    {
      for (byte i = 0; i < 9; i++) //We wait for a server connection for 10 seconds max
      {
        rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
        WiFi.reconnect();
        delay(1000);
        M5.Lcd.print(".");
        Serial.print("."); //DEBUG
        rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
        if (WiFi.status() == WL_CONNECTED)
        {
          M5.Lcd.println("");
          M5.Lcd.println("WiFi connected!");
          M5.Lcd.print("IP address: ");
          M5.Lcd.println(WiFi.localIP());
          M5.Lcd.println("");
          Serial.println(""); //DEBUG
          Serial.println("WiFi connected!"); //DEBUG
          Serial.print("IP address: "); //DEBUG
          Serial.println(WiFi.localIP()); //DEBUG
          Serial.println(""); //DEBUG
          break;
        }
      }
      if (WiFi.status() != WL_CONNECTED)
      {
        M5.Beep.tone(3300);
        delay(100);
        M5.Beep.mute();
        M5.Lcd.println("");
        M5.Lcd.println("Cannot connect to PoolTempServer");
        Serial.println(""); //DEBUG
        Serial.println("Cannot connect to PoolTempServer"); //DEBUG
        delay(3000);
        break;
      }
    }
  }

	rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
  if (WiFi.status() == WL_CONNECTED)
  {
    if (client.connect(server, port, 2000))
    {
      delay(500);
      client.println("{\"get\":\"temp\"}");
      Serial.println("get:temp command sent to server"); //DEBUG      
      delay(1000);     
      rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
    }
    else
    {
      M5.Beep.tone(3300);
      delay(100);
      M5.Beep.mute();
      M5.Lcd.println("Cannot connect on port 80");
      Serial.println("Cannot connect on port 80"); //DEBUG
      delay(3000); //We wait to display message if connection failed, 3 secs max to be below watchdog timeout
      rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
      M5.Lcd.fillScreen(0x0000);  //Fill LCD screen with black color
      //ESP.restart();
    }
  }

  server = client.available(); //If server sent data to client, we have something to read
  if (server)
  {
    //Serial.println("WiFi server data available!");
    while (client.connected())
    {
      rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
      if (wifiTimeoutStarted)                                                     // Verify if wifiCommEvent function started the timeout verification process
      {
        if (((unsigned long)millis() - wifiTimerStart) >= wifiTimeoutValue)       //Differential timer to validate if wifiCommEvent command receiving delay is exeeding the determined value and branch if true
        {
          M5.Beep.tone(3300);
          delay(100);
          M5.Beep.mute();
          wifiTimeoutOccured = true;                                              //Set to true if we exceeded the determined wifi timeout value, wifiCommEvent() will take care of this
          wifiCommEvent();                                                        //We branch here as a wifi timeout has been detected and we need to reset the function logics
          M5.Lcd.println("WiFi answer timeout!");
          break;
        }
      }
      if (client.available() > 0) //We verify if chars are available in the wifi interface buffer
      {
        wifiCommEvent();          //We branch here if data is available to be fetched from the wifi interface
        rtc_wdt_feed();     //When called, resets the RTC Watchdog timer
      }
      if ((wifiStringComplete) && (reachedMaxWifiChars == false)) //Branch if wifi transmission has ended and MaxCommChars has not been exceeded
      {
        memcpy(answerString, wifiString, sizeof(wifiString));     //Keep a workable copy of the string when a closing JSON curly bracket has been detected
        memset(wifiString, 0, sizeof(wifiString));                //Clear the wifi string to be reused
        wifiStringComplete = false;                               //Reset the flag to be reused
        answerParser();                                           //Parsing the received commandString string and execute the right command
        memset(answerString, 0, sizeof(answerString));            //Clear the commandString string to be reused
        memset(answerToJSON, 0, sizeof(answerToJSON));            //Clear the commandToJSON string to be reused
        break;
      }
      if ((wifiStringComplete) && (reachedMaxWifiChars == true))  //Branch if wifi transmission has ended and MaxCommChars has been reached or exceeded, we are in error
      {
        M5.Beep.tone(3300);
        delay(100);
        M5.Beep.mute();
        memset(wifiString, 0, sizeof(wifiString));                //Clear the wifi string to be reused
        wifiStringComplete = false;                               //Reset the flag to be reused
        reachedMaxWifiChars = false;                              //Reset the flag to be reused, we branched here so we know we are in error
        M5.Lcd.println("WiFi answer overflow!");
        break;
      }
    }
    //while (client.read() != -1) { delay(10); }  //We need be sure the buffer is empty to close the connection properly and avoid leaving an open socket on the server
    client.flush();
    client.stop();
    //WiFi.disconnect();

    rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
    delay(2500);
    rtc_wdt_feed(); //When called, resets the RTC Watchdog timer
    delay(2500);
    M5.Lcd.fillScreen(0x0000);  //Fill LCD screen with black color
    //M5.Axp.DeepSleep();
    //ESP.restart();
  }
}

Remove all delay() calls from the sketch except the one after WiFi.reconnect().

Remove the line:

Try again and post the complete serial output you get!

@pylon, thanks to give me an answer!

Things evolved and I tested this many times, even with your suggestion.
The only way I can reconnect is to call ESP.Restart() at the end of loop...
I can't send another command and got the "Cannot connect on port 80" error each time after...
I would like to have an explanation to this. Is the ESP32 Arduino library broken in this regard ?
Is the Espressif IDF base functions behavior different ?

This reconnect would have been useful to test but the M5StickC-Plus will be put in deep sleep at end of loop and revived with a button press. This was planned originally.
So I'm almost giving up with this question.
But I still want to find an answer is it's available somewhere... ...

Why don't you provide the serial output you get (without the ESP.Restart())?

Will do...
But I'm not running most of this code anymore and I output on LCD now.
I will re-post the new code and add back serial output when I will have time later this week.

Original post code updated, simplified a little but using my M5StickC-Plus.
Here is the verbose log without ESP.restart();
I even tried a WiFi.reconnect() after the "Cannot connect on port 80" message, same thing.
Something is blocking me to connect a second time.
Works fine each time if the ESP32 is restarted.

[     5][D][esp32-hal-cpu.c:244] setCpuFrequencyMhz(): PLL: 480 / 2 = 240 Mhz, APB: 80000000 Hz
[    37][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 0 - WIFI_READY
[   126][V][WiFiGeneric.cpp:338] _arduino_event_cb(): STA Started
[   127][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 2 - STA_START
[  1128][V][WiFiGeneric.cpp:97] set_esp_interface_ip(): Configuring Station static IP: 0.0.0.0, MASK: 0.0.0.0, GW: 0.0.0.0
M5StickCPlus initializing...[  1196][I][esp32-hal-i2c.c:75] i2cInit(): Initialising I2C Master: sda=21 scl=22 freq=100000
OK
E (1764) ledc: freq_hz=0 duty_resolution=13
[  1767][E][esp32-hal-ledc.c:75] ledcSetup(): ledc setup failed!
[  1768][W][Wire.cpp:301] begin(): Bus already started in Master Mode.
Connecting to PoolTempServer...
SSID: PoolTempServer
.[  2804][V][WiFiGeneric.cpp:360] _arduino_event_cb(): STA Disconnected: SSID: PoolTempServer, BSSID: 68:67:25:82:38:c1, Reason: 8
[  2805][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 5 - STA_DISCONNECTED
[  2813][W][WiFiGeneric.cpp:950] _eventCallback(): Reason: 8 - ASSOC_LEAVE
[  2827][V][WiFiGeneric.cpp:353] _arduino_event_cb(): STA Connected: SSID: PoolTempServer, BSSID: 68:67:25:82:38:c1, Channel: 1, Auth: WPA2_PSK
[  2832][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 4 - STA_CONNECTED
[  3116][V][WiFiGeneric.cpp:367] _arduino_event_cb(): STA Got New IP:192.168.1.2
[  3116][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 7 - STA_GOT_IP
[  3119][D][WiFiGeneric.cpp:991] _eventCallback(): STA IP: 192.168.1.2, MASK: 255.255.255.0, GW: 192.168.1.1
.
WiFi connected!
IP address: 192.168.1.2

get:temp command sent to server
[  5327][I][WiFiClient.cpp:549] connected(): Unexpected: RES: 0, ERR: 119
{"temp":"values","tC":"21.00","tF":"69.80"}
[  5356][E][WiFiClient.cpp:517] flush(): fail on fd 48, errno: 11, "No more processes"


Second and subsequent loop() below
Cannot reconnect anymore
If esp.restart() is used, it will reconnect fine each time
----------------------------------------------------------

[ 12382][I][WiFiClient.cpp:253] connect(): select returned due to timeout 2000 ms for fd 48
Cannot connect on port 80
[ 17512][I][WiFiClient.cpp:253] connect(): select returned due to timeout 2000 ms for fd 48
Cannot connect on port 80
[ 22642][I][WiFiClient.cpp:253] connect(): select returned due to timeout 2000 ms for fd 48
Cannot connect on port 80

I use WiFi.disconnect() before making any connection attempt to clear and reset the WiFi stack. I also use WiFI.disconnect(0 to disconnect from the WiFi and not wifi.stop. Might try WiFi.disconnect instead of stop?

I find when I have to put in those little things like yield and rest watchdog, I've done something wrong.

here is a more extensive WiFI callback that I use to troubleshoot WiFi issues.

void WiFiEvent(WiFiEvent_t event)
{
   log_i( "[WiFi-event] event: %d\n", event );
  switch (event) {
        case SYSTEM_EVENT_WIFI_READY:
          log_i("WiFi interface ready");
          break;
        case SYSTEM_EVENT_SCAN_DONE:
          log_i("Completed scan for access points");
          break;
        case SYSTEM_EVENT_STA_START:
          log_i("WiFi client started");
          break;
        case SYSTEM_EVENT_STA_STOP:
          log_i("WiFi clients stopped");
          break;
    case SYSTEM_EVENT_STA_CONNECTED:
      log_i("Connected to access point");
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
        case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
          log_i("Authentication mode of access point has changed");
          break;
        case SYSTEM_EVENT_STA_GOT_IP:
          log_i ("Obtained IP address: %s",  WiFi.localIP() );
          break;
        case SYSTEM_EVENT_STA_LOST_IP:
          log_i("Lost IP address and IP address is reset to 0");
          //      vTaskDelay( 5000 );
          //      ESP.restart();
          break;
        case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
          log_i("WiFi Protected Setup (WPS): succeeded in enrollee mode");
          break;
        case SYSTEM_EVENT_STA_WPS_ER_FAILED:
          log_i("WiFi Protected Setup (WPS): failed in enrollee mode");
          //      ESP.restart();
          break;
        case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
          log_i("WiFi Protected Setup (WPS): timeout in enrollee mode");
          break;
        case SYSTEM_EVENT_STA_WPS_ER_PIN:
          log_i("WiFi Protected Setup (WPS): pin code in enrollee mode");
          break;
        case SYSTEM_EVENT_AP_START:
          log_i("WiFi access point started");
          break;
        case SYSTEM_EVENT_AP_STOP:
          log_i("WiFi access point  stopped");
          //      WiFi.mode( WIFI_OFF);
          //      esp_sleep_enable_timer_wakeup( 1000000 * 2 ); // 1 second times how many seconds wanted
          //      esp_deep_sleep_start();
          break;
        case SYSTEM_EVENT_AP_STACONNECTED:
          log_i("Client connected");
          break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
          break;
        case SYSTEM_EVENT_AP_STAIPASSIGNED:
          log_i("Assigned IP address to client");
          break;
        case SYSTEM_EVENT_AP_PROBEREQRECVED:
          log_i("Received probe request");
          break;
        case SYSTEM_EVENT_GOT_IP6:
          log_i("IPv6 is preferred");
          break;
        case SYSTEM_EVENT_ETH_GOT_IP:
          log_i("Obtained IP address");
          break;
    default: break;
  }
}

you may need to edit it.

You shouldn't edit previous posts to provide new versions of your code. A future reader of this thread won't be able to follow it if the original information isn't available anymore.

The following line is rather strange:

Looks like no connection really succeeds.

It's also extremely strange that your get a lot of output for the first run but only two lines for the later ones. Are you not posting complete output?

@pylon, I posted the verbose debug output of the Client, the M5StickC-Plus with LCD.
At the end, only the last 2 lines are repeating:

[ 22642][I][WiFiClient.cpp:253] connect(): select returned due to timeout 2000 ms for fd 48
Cannot connect on port 80

The Client will always connect fine each time if rebooted.
Please note, I first tested the temperature Server (the other ESP32) with my Android cell phone and a WiFi terminal application.
From Android, I can always send commands, disconnect while staying connected to the AP, reconnect, no issues.

Should I post the server loop() code too ?

I've searched and this is what I've found about the error 119:
Error #119 is ENAVAIL "No XENIX semaphores available"

Is it possible that I send the data too fast after the original connection ?
Seems socket related but I'm not sure... this is actually beyond my knowledge.

You should adapt the print statement as you don't try to connect to port 80.

You have a reuse of the server variable which probably overwrite the IP address of the server you want to connect to:

    if (client.connect(server, port, 2000))
    {

...

  }

  server = client.available(); //If server sent data to client, we have something to read
  if (server)
1 Like

I'm not sure to fully understand...

client.connect(server, port, 2000)

Always return false on second and subsequent loops.
So connection seems to be refused from the Server for an unknown reason.

Have you looked at the log files of the server to see why the connection is refused?

@Idahowalker good idea.
I will have the two boards plugged on COM ports with debug enabled on both this week end.

But the suggestion of @pylon to not reuse the "server" variable is a good one.
I've not seen it.
I will rename the variables to not overwrite the server IP first.

@pylon, you got it, my bad to have reused the "server" variable...
I feel a little miserable now ;(
Thanks for this.

I will test further this weekend and I choose your solution.

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