I have a project where I use WifiClient to make regular connection to a server (running on both ESP32 and ESP8266).
It works great until for some reason the client.connect(url, port) returns false on connection. After that the only way I can make the WifiClient work again is by rebooting, it will never connect again. Even reconnecting to the Wifi does not make a difference.
Is there a way to debug why the connection fails (the WIFI connection seems ok).
Is there a way to reset the WifiClient.
Reduced code:
WiFiClient client;
loop(){
client.setTimeout(2500);
if (client.connect(serverIp, serverPort)) {
// Send request to the server:
displayStatus(F("Connected *"));
int contentLength = generateData();
client.println(F("POST /api/heartbeat HTTP/1.1"));
client.println(F("Host: localhost"));
client.println(F("Accept: */*"));
client.println(F("Content-Type: application/x-www-form-urlencoded"));
client.print(F("Content-Length: "));
client.println(contentLength + 5);
client.println();
client.print(F("data="));
client.print(commsBuffer);
client.flush();
// Done here, as the response availability sometimes takes time.
lastConnection = millis();
updateServer = false;
updatingServer = true;
// Set timout counter and wait for data to be available
timeout = millis();
yield();
} else {
client.clearWriteError();
client.flush();
client.stop();
updatingServer = false;
increaseErrorCount();
if (WiFi.status() == WL_IDLE_STATUS) {
Serial.print(F("Idle WIFI, trying to restart... (debug)"));
startWifi();
}
yield();
}
if (timeout > 0 && client.available()) {
// Reset timeout counter;
timeout = 0;
short readLine = 0;
boolean nextDataLine = 0;
int readCharacter = 0;
boolean httpError = false;
// Reset commsBuffer
for (unsigned int i = 0; i < commsBufferLength; i++) {
commsBuffer[i] = '\0';
}
while (client.available()) {
//process data
yield();
}
}
}
I use these tasks to keep the wifi and mqtt connection open. Perhaps you may find something useful.
void MQTTkeepalive( void *pvParameters )
{
sema_MQTT_KeepAlive = xSemaphoreCreateBinary();
xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
// setting must be set before a mqtt connection is made
MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
for (;;)
{
//check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
{
xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
MQTTclient.loop();
xSemaphoreGive( sema_MQTT_KeepAlive );
}
else {
log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
{
connectToWiFi();
}
connectToMQTT();
}
vTaskDelay( 250 ); //task runs approx every 250 mS
}
vTaskDelete ( NULL );
}
////
void connectToWiFi()
{
int TryCount = 0;
while ( WiFi.status() != WL_CONNECTED )
{
TryCount++;
WiFi.disconnect();
WiFi.begin( SSID, PASSWORD );
vTaskDelay( 4000 );
if ( TryCount == 10 )
{
ESP.restart();
}
}
WiFi.onEvent( WiFiEvent );
} // void connectToWiFi()
////
void connectToMQTT()
{
MQTTclient.setKeepAlive( 90 ); // needs be made before connecting
byte mac[5];
WiFi.macAddress(mac);
String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
while ( !MQTTclient.connected() )
{
// boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password, NULL , 1, true, NULL );
vTaskDelay( 250 );
}
MQTTclient.setCallback( mqttCallback );
MQTTclient.subscribe( topicOK );
} // void connectToMQTT()
////
Why are you making the connection over and over and over again? If you are going to do that you should disconnect() before making another connection. Connecting over and over and over again will corrupt the WiFI stack.
And post the entire code. Not a snippet.
Include a WiFI.disconnect() before connecting anyways, WiFi.disconnect() resets the WiFi stack to its default values as one of its functions.
if you have to use ```yield()`` something is very wrong.
I connect over and over again using client.connect as the device submits temperature, humidity and pressure readings every 10 seconds when there are changes, or every 2 minutes as a "ping" to show it is alive. I don't connect over an over again to the Wifi, the Wifi connection remains.
I always use the client.stop(); function, but for the WifiClient I cannot find any other function to reset it. I will try and put in a disconnect and reconnect to the Wifi if a connection failure happens.
[EDIT: A Wifi disconnect and reconnect does not fix the problem]
I threw in a few yields() at one stage as I got a HW Watchdog timeout.
Here is an expanded WiFI callback I've used to troubleshoot WiFi thingies.
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;
}
}
Thank you. I don't think it is the Wifi connection that is the issue, as I can connect to the device using a server call to the device.
Should I use an alternative to WifiClient to make the POST request? I do need one that is not blocking, and WifiClient works well that way that other tasks can be performed until the connection is available (and doesn't block if there is a timeout for some reason).
Thanks juraj, but that means I need to hold the main loop until the result is available, and at time, as the devices are deployed on a meshed Wifi, connection timeouts occur. That is why it is global, so the loop() can run till data is available. Unless there is a work around.
the underling tcp/ip stack has limited number of connections. you don't stop() the WiFiClient and the server doesn't close the connection. I guess a connect() closes the previous connection, but it takes time until closing is negotiated with the other side so by quickly connecting you run out of connections. but it should recover and connect as soon as a socket is available (unless the server bans you).
Thank you. I have been delving a bit more deeper, and I have a feeling there is a String of a char[] that is overflowing and corrupts the stack. I am replacing the String functions just in case and put some guards on the chars, and see if that could have been the cause.