Skip to content

WiFi Socket Disconnect Issues and WiFi reconnect question. #1464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
neltnerb opened this issue Jun 4, 2018 · 11 comments
Closed

WiFi Socket Disconnect Issues and WiFi reconnect question. #1464

neltnerb opened this issue Jun 4, 2018 · 11 comments
Labels
Status: Stale Issue is stale stage (outdated/stuck)

Comments

@neltnerb
Copy link

neltnerb commented Jun 4, 2018

Hardware:

Board: ESP32-DevKitC
Core Installation/update date: 1/Jun/2018
IDE name: Platform.io with VS Code
Flash Frequency: 40MHz
Upload Speed: 115200

Description:

I am having issues with TCP/IP connection closing and with reconnecting to a WiFi network if it is disconnected. Once I close my first TCP/IP connection, the device ignores all future connections until I reboot the chip. I think that WiFi.status() may also be buggy.

Sketch:

This is the full library I am writing with an example program using it.
https://gitlab.com/brianneltner/NeltnerLabs_MultiProtocol_Template/

These are the most relevant portions:

WiFi.status() doesn't ever return WL_CONNECT_FAILED

while ((WiFi.status() != WL_CONNECTED) and (WiFi.status() != WL_CONNECT_FAILED)) {
    delay(500);
    Serial.print(".");
  }
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("WiFi connected.");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    server.begin();
  }
  else {
    Serial.println("");
    Serial.println("WiFi Connection Failed.");
    Serial.print("SSID: ");
    Serial.println(ssid);
    Serial.print("Password: ");
    Serial.println(password);
  }

This function never exits if the wifi configuration is wrong. I am working around it with a manual timeout, but shouldn't it return WL_CONNECT_FAILED after it fails to connect? My code seems like the better way to do this.

How to automatically reconnect and disconnect closed sockets?

WiFiClient client;

void loop() {
  delay(500);
  checkSerial(Serial);
  if (client) {
    if (checkWiFi(client) == 1) { // A return code of 1 means it is not connected.
      client.stop();
      client = server.available();
    }
  }
  // If the wifi is connected, connect the client to the open socket.
  else if (WiFi.status() == WL_CONNECTED) client = server.available();
  // If it's disconnected, try to connect. This doesn't work because you can't do begin multiple times.
  else if (WiFi.status() != WL_IDLE_STATUS) WiFi.begin(ssid.c_str(), password.c_str());
}

My idea here was that if(client) is true, it runs checkWiFi (which reads and parses the data) which returns 0 normally and returns 1 if client.connected() is not true. If the client is disconnected, it tries to stop the client and reconnect to an available server connection (what does server.available() actually do here?). I originally did not have that check and it also did not work, this was my attempt to make it work.

If there is no current client, but WiFi is connected, it reinitializes the client if a socket is open. This doesn't seem to work at all, or else only works once.

If there is no current client, the WiFi is not connected, and the WiFi is not actively attempting to connect, I attempted to reconnect using WiFi.begin(). This did not work when I updated the ssid over a serial port from a non-functional configuration. After rebooting it did connect fine with the newly saved ssid.

Debug Messages:

I am not using the Arduino IDE and am unsure how to use the core debugger with the Platform.io/VS Code IDE. Happy to provide with some help in how to run it.

@justoke
Copy link

justoke commented May 31, 2019

Issue Unresolved

I have struggled for a few weeks to find a way to ensure that an ESP32 can keep connected to WiFi AP and survive WiFi outages and so on. This is the most basic and fundamental requirement of an IOT device. From my own experience and that of others based on the many issues raised around WiFi connection issues, there seems to be a inherent instability to the ESP32 WiFi stack. The reconnect() method is completely useless and should be re-written. It will never reconnect your ESP32 if the WiFi AP drops out and try to reconnect it. By contrast I have some ESP8266 devices running for over 18 months now and I have never had to reset them manually. The WiFi stack on them seems as resilient as can be.

The pain

After getting stung many times now with devices that just go offline without any hope of recovery, I make this fundamental assumption when designing my code patterns: there is no WiFi! Code for this scenario and ensure that you can sync or resume work when connectivity is established.

Anyway, as developers we must work around and solve such challenges. So far this is the most promising pattern I have come up with. Although it has only been running on some test devices for a few days now, they have not required manual reset/reboot or ESP.restart().

Suggested Pattern with test cases

If you could try this code and tests and report back that would be helpful.

  1. Disconnect the WiFi AP by turning it off on your router.
  2. Power off your router while the ESP32 is connected. This happens in the real world more often than we think!
  3. Reset the ESP32 when the WiFi AP is disabled or powered off.
  4. For the brave - change the AP password.

The device should go into the reconnect cycle in each case and once the WiFi AP is enabled/powered up it should recover again.

#include <WiFi.h>

void delayfor(long milliseconds);
boolean reconnect();

// Update these with values suitable for your network.
const char* ssid = "**";
const char* password = "***";

void setup() {
  Serial.begin(115200);
}

long lastReconnectAttempt = 0;

void loop() {

    if (!WiFi.isConnected()) {
        long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } 
else {
    
    // Do Work here
    Serial.print("WiFi Connected. Doing work!");
    delayfor(2000);

  }

}

boolean reconnect() 
{
  if (!WiFi.isConnected())
  {
    Serial.print("Reconnecting WiFi ");
    WiFi.disconnect(false);
    Serial.println("Connecting to WiFi...");
    WiFi.mode(WIFI_AP_STA);
    WiFi.begin(ssid, password);
    delayfor(250);
    if ( WiFi.status() != WL_CONNECTED )
    {
        return false;
    }
    
     Serial.println("Connected");
  }
}

void delayfor(long milliseconds)
{
    long d;
    d = millis();

    while (millis()-d < milliseconds) {
        
        yield();
    }
}

On Success

/* Powered Up with WiFi AP enabled*/
2019/05/31 20:24:59: WiFi Connected. Doing work!
2019/05/31 20:25:01: WiFi Connected. Doing work!
/* Disabled WiFi AP on router*/
2019/05/31 20:25:03: Reconnecting WiFi Connecting to WiFi...
2019/05/31 20:25:08: Reconnecting WiFi Connecting to WiFi...
2019/05/31 20:25:13: Reconnecting WiFi Connecting to WiFi...
2019/05/31 20:25:18: Reconnecting WiFi Connecting to WiFi...
2019/05/31 20:25:23: Reconnecting WiFi Connecting to WiFi...
2019/05/31 20:25:28: Reconnecting WiFi Connecting to WiFi...
2019/05/31 20:25:33: Reconnecting WiFi Connecting to WiFi...
/* Enabled WiFi AP on router*/
2019/05/31 20:25:36: WiFi Connected. Doing work!
2019/05/31 20:25:38: WiFi Connected. Doing work!
/*We can do this all day! For days and days */

Get on with a useful IOT project

@Naegolus
Copy link

Hi,

I would like to test your code, but i am not able to make the arduino library building.
Which versions are you using:

  • Crosstool-NG
  • esp-idf
  • xtensa compiler
  • arduino library

At the moment i use
xtensa-esp32-elf-gcc8_2_0-esp32-2019r1-linux-amd64.tar.gz
for the compilers.
Because of that i need some current master branch of esp-idf but the arduino library needs version 3.2 of esp-idf.

As you said. An IoT device without Wifi is pretty useless ...
I heard that the arduino code should be more stable.

@lbernstone
Copy link
Contributor

The current arduino-esp32 uses esp-idf v3.2, which in turn uses uses the 80-5.2.0 xtensa compiler. Unless you want to do the work of porting to the new versions, you will need to use the supported versions. See https://github.com/espressif/arduino-esp32/blob/master/package/package_esp32_index.template.json for links.

@Naegolus
Copy link

Naegolus commented Jul 7, 2019

Thank you very much!
I will try to run the tests this week.

@Naegolus
Copy link

Hi,

@lbernstone: Sorry. Was kind of RTFM issue ..
@justoke: Tests attached. All cases seem to be working. Thank you! If you have any questions. Don't hesitate to ask.

br
Joe

test-1-2.txt

test-3.txt

test-4.txt

@neltnerb
Copy link
Author

I'm not certain I understand well enough to know if the above discussion is related to my original report. With apologies, I'm not a software developer so I'm not sure how github's reporting system works.

My issue was that even when the ESP32 was continually connected to the wifi network, I could not reliably send commands over TCP/IP because after sending a message the computer would close the socket on it's end but for some reason the ESP32 left it open so no new sockets could be created.

This was the command I used to check for data on the interface:

int checkWiFi(WiFiClient& interface) {
  if (interface.connected()) {
    while (interface.available() > 0) {
      char incomingByte = interface.read();
      
      // If the byte is a carriage return or newline (since I can't guarantee which if either will come
      // first), then send the command previously read to evaluateCommand() and clear the commandstring
      // buffer. This has the convenient effect of rendering irrelevant whether LabView or other such
      // GUI sends something reasonable for a termination character, as long as it sends *something*.
      
      if ((incomingByte == 0x0D) || (incomingByte == 0x0A)) {
        // This tests that there's a string to return in the buffer. If not, ignore. This is both
        // to avoid testing when it's an empty command, and also to deal with sequential CR/NL that
        // can happen with some GUIs sending serial data.
        
        if (wifistring.size() != 0) {
          // Append the termination character to the command string.
          wifistring.push_back('\0');
    
          // Evaluate the data.
          InterfaceClass port = InterfaceClass(&interface);
          evaluateCommand(wifistring.data(), port);
        }
        wifistring.clear();
      }

      // If the byte is not a carriage return, and is a normal ASCII character, put it onto the commandstring.
      else if ((incomingByte >= 0x20) && (incomingByte <=0x7E)) {
        wifistring.push_back(incomingByte);
      }
    }
    return 0;
  }
  else return 1;
}

I don't think it had anything to do with the wifi simply being unreliable, these would be tests done seconds apart. I tried to workaround it by checking for client connections and closing sockets when no one was connected to them, but this didn't work at all.

For clarity, InterfaceClass was a generic class that I built so that I could treat serial interfaces and wifi interfaces the same way with my function evaluateCommand that I use to register commands and their function references and help text and automatically let me add new commands and have them appear when I send 'help' to get a list of available registered options.

Anyway, it sounds like a productive discussion so apologies again if this is something unrelated. My version all works exactly once and then I have to reboot. The web server did seem to close sockets properly, so perhaps I'm the only one sending strings directly over TCP/IP or something.

The second issue I mentioned was that WiFi.status() doesn't ever return WL_CONNECT_FAILED. My intent was that at the least if it failed it should ask you to update the SSID and password, noting that this failed so early in my attempt to use it that I didn't get so far as to implement reconnections and the other stuff you're mentioning.

I agree that if I'd gotten past this roadblock, checking the status and finding it disconnected and then finding that it fails when you reconnect should probably do something like timeout for a while and then attempt reconnect while querying the serial port for new SSID/password information. Mine was failing even while connected over short time frames if I had even the same computer attempt to send two commands in a row.

@justoke
Copy link

justoke commented Jul 12, 2019

@Naegolus Thank you for the detailed logs - the additional information is interesting. How did you enable that output? I would like to do the same.

@neltnerb I got to your issue via the related issue. While this may not be the cause of your issue, my point was that sometimes it is the low level WiFi status and behaviour that causes clients like TCP and MQTT to fail in a way that they never recover. I merely wanted to present a pattern that would ensure that the ESP32 would recover from AP outages. You could try this pattern just to see if it it helps.

Also a similar issue was raised on the painlessMESH project.

@Naegolus
Copy link

@justoke In the project directory type "make menuconfig"
Then set: "Component config -> Log output -> Default log verbosity"
to: "Info"

@neltnerb
Copy link
Author

neltnerb commented Jul 12, 2019 via email

@stale
Copy link

stale bot commented Sep 10, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

@stale
Copy link

stale bot commented Sep 24, 2019

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Stale Issue is stale stage (outdated/stuck)
Projects
None yet
Development

No branches or pull requests

4 participants