Skip to content
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

If I put a delay in the loop, it stopped connecting to the server #877

Open
Betsq opened this issue Mar 28, 2024 · 7 comments
Open

If I put a delay in the loop, it stopped connecting to the server #877

Betsq opened this issue Mar 28, 2024 · 7 comments

Comments

@Betsq
Copy link

Betsq commented Mar 28, 2024

I have this piece of code:
void loop() { webSocket.loop(); delay(2000); }
when I set the delay, the websocket cannot connect in any way and constantly log 'Disconnected'. But if I remove the delay, everything connects fine

I'm using ESP32

@Betsq Betsq changed the title If you put a delay in the loop, it stopped connecting to the server If I put a delay in the loop, it stopped connecting to the server Mar 28, 2024
@sostenesapollo
Copy link

any answer ?

@furdog
Copy link

furdog commented Aug 7, 2024

Simply DO NOT put any DELAY in loop with websockets.
WS must work and update in real time, delays will screw this requirement.

You could run websockets in separate thread if you want to use delays so hard:

void WS_thread(void *pvParameters)
{
	while (true)
		webSocket.loop();
}

void setup()
{
	//...
	
	xTaskCreatePinnedToCore(
		WS_thread, //Task function
		"WS_thread", //Task name
		10000, //Stack size for task
		NULL,
		1, // Task priority
		NULL,
		1 // processor CORE the task is being run on (0 or 1)
	);
}

void loop()
{
	//Your blocking code here ...
	delay(2000);
}

Alternatively you could use non-blocking approach for timers:

static clock_t timestamp_prev = 0;
static clock_t timestamp      = 0;

clock_t get_delta_time_ms()
{
	clock_t delta;
	
	timestamp = millis();
	delta = timestamp - timestamp_prev;
	timestamp_prev = timestamp;
	
	return delta;
}

//...

clock_t my_timer = 0;

void loop()
{
	//Calculate time delta and put it into the variable.
	//Call get_delta_time_ms() ONLY ONCE per loop (this is important).
	clock_t delta = get_delta_time_ms();
	
	//Increment your timer by delta
	my_timer += delta;
	
	//Check if 2 seconds has elapsed
	if (my_timer >= 2000) {
	    my_timer -= 2000;
	    //my_timer = 0; //same as above, but less precise
	    
	    //Do some of your task here
	}
	
	//This websocket loop is not blocked by any command or delay
	webSocket.loop();
}

Also take note not to use ANY blocking functions inside websocket callback, or it will crash your system easily.
DO NOT use any flash related functions inside WS callbacks (spiffs, littlefs and so on (will crash too)).

P.S. The examples i have provided may contain syntax errors.

@leosoftGR
Copy link

Thanks for this information @furdog

Seems that even a delay of just 10ms can cause freezings and/or crashes.

@Links2004
Copy link
Owner

the point at which a delay is problematic depends on multiple factors.
some of them are:

  • in coming data rate (how fast is the data buffer of the TCP client full)
  • the free heap (there is more data to handle with longer loop call intervals)
  • server timeouts for ping/pong, ACK and so on (how fast does the server needs a answer)
  • the TCP implementation of the µC (ESP is more stable than a AVR for example, and not all TCP stacks handle hard closed connections that well e.g. when a server closes the connection for a timeout, or on a network disconnect)

@leosoftGR
Copy link

Thanks for the details Markus @Links2004

@AviStudio
Copy link

AviStudio commented Oct 3, 2024

void WS_thread(void *pvParameters)
{
	while (true)
		webSocket.loop();
}

void setup()
{
	//...
	
	xTaskCreatePinnedToCore(
		WS_thread, //Task function
		"WS_thread", //Task name
		10000, //Stack size for task
		NULL,
		1, // Task priority
		NULL,
		1 // processor CORE the task is being run on (0 or 1)
	);
}

void loop()
{
	//Your blocking code here ...
	delay(2000);
}

This helped me to run WS Client outside the loop(), but is very interesting that I had a small project 6 months ago (version 2.4.1) that is running until today in a loop() with delay(3000)! Now I tried to make that project work w/o success :(
here is the code:

#include <Arduino.h>
#include <WiFi.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#define USE_SERIAL Serial

const char *ssid = "";
const char *password = "";
String postData = "";

#define INPUTS 6
int GPIOs[INPUTS] = {32, 33, 34, 35, 36, 39};
bool STATEs[INPUTS] = {0, 0, 0, 0, 0, 0};

WebSocketsClient webSocket;
StaticJsonDocument<200> doc;

void webSocketEvent(WStype_t type, uint8_t *payload, size_t length)
{
    switch (type)
    {
    case WStype_DISCONNECTED:
        USE_SERIAL.printf("[WSc] Disconnected! \n");
        break;
    case WStype_CONNECTED:
        USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
        // send message to server when Connected
        webSocket.sendTXT("{\"Type\":\"Connected\"}");
        break;
    case WStype_TEXT:
        USE_SERIAL.printf("[WSc] get text: %s\n", payload);
        // send message to server
        // webSocket.sendTXT("message here");
        break;
    case WStype_BIN:
        USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
        // send data to server
        // webSocket.sendBIN(payload, length);
        break;
    case WStype_PING:
        USE_SERIAL.printf("[WSc] got a PING \n");
        break;
    case WStype_PONG:
        USE_SERIAL.printf("[WSc] got a PONG \n");
        break;
    case WStype_ERROR:
    case WStype_FRAGMENT_TEXT_START:
    case WStype_FRAGMENT_BIN_START:
    case WStype_FRAGMENT:
    case WStype_FRAGMENT_FIN:
        break;
    }
}

void webSocketInit()
{
    // server address, port and URL
    webSocket.begin("192.168.0.163", 8081, "/?id=" + String(1));

    // event handler
    webSocket.onEvent(webSocketEvent);

    // void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
    webSocket.enableHeartbeat(3000, 2000, 2);

    // try ever 5000 again if connection has failed
    webSocket.setReconnectInterval(5000);
}

void setup()
{
    Serial.begin(115200);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("Connecting to WiFi..");
    }
    // Print ESP Local IP Address
    Serial.println(WiFi.localIP());
    for (int i = 0; i < INPUTS; i++)
    {
        pinMode(GPIOs[i], INPUT);
    }
    webSocketInit();
}

void sendWS(int GPIO, bool State)
{
    doc["GPIO"] = GPIO;
    doc["STATE"] = State;
    serializeJson(doc, postData);
    USE_SERIAL.println(postData);
    webSocket.sendTXT(postData);
    postData = "";
}

void readPins()
{
    for (int i = 0; i < INPUTS; i++)
    {
        Serial.println(STATEs[i]);
        if (STATEs[i] != digitalRead(GPIOs[i]))
        {
            STATEs[i] = !STATEs[i];
            if (STATEs[i] == 1)
            {
                Serial.println("AC ON! on GPIO: " + String(GPIOs[i]));
                sendWS(GPIOs[i], STATEs[i]);
            }
            else
            {
                Serial.println("AC OFF! on GPIO: " + String(GPIOs[i]));
                sendWS(GPIOs[i], STATEs[i]);
            }
        }
    }
}

void loop()
{
    webSocket.loop();
    readPins();
    delay(3000);
}

webSocket.loop(); for me doesn't make sense as ESP32 should do another tasks before sending some info to server!

@furdog
Copy link

furdog commented Oct 3, 2024

webSocket.loop() does more than just sends your messages.
It has to update more often to work as intended. In your case it updates once per 3000 second.
There's no way it would work somehow stable. Imagine driving your car with 3 second reaction delay.
Links2004 already explained why it is problematic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants