Board: ESP32-DevKitC
Core Installation/update date: 1/Jun/2018
IDE name: Platform.io with VS Code
Flash Frequency: 40MHz
Upload Speed: 115200
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.
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:
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.
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.
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.
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.
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
Hi,
I would like to test your code, but i am not able to make the arduino library building.
Which versions are you using:
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.
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.
Thank you very much!
I will try to run the tests this week.
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
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.
@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.
@justoke In the project directory type "make menuconfig"
Then set: "Component config -> Log output -> Default log verbosity"
to: "Info"
Thanks for explaining!
President, Neltner Labs
(617) 938-7735
[email protected]
http://neltnerlabs.com
On Fri, Jul 12, 2019, 4:09 AM justoke notifications@github.com wrote:
@Naegolus https://github.com/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 https://github.com/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
https://gitlab.com/painlessMesh/painlessMesh/issues/275#note_188175796
was raised on the painlessMESH project.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/espressif/arduino-esp32/issues/1464?email_source=notifications&email_token=AADIXUM577YRP66RZHUZ5KTP7A3Z7A5CNFSM4FDFJXR2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZZBMTQ#issuecomment-510793294,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AADIXUIFO5V6KUWA6LCKDZ3P7A3Z7ANCNFSM4FDFJXRQ
.
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_DEL] This stale issue has been automatically closed. Thank you for your contributions.
Most helpful comment
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