Websocket server loop behaviour

Hi,

I'm working on a websocket server but I'm running into a problem I don't understand. I hope someone can enlighten me. My code is below.

-- I am running a websocket server on a Wiznet W5100s EVB Pico
-- Using Ethernet_Generic and WebSockets2_Generic libraries
-- A local HTML5/Javascript webpage makes a socket connection
-- Data is exchanged in JSON using the ArduinoJson library

Now this is what puzzles me:
-- Data sent from the webpage to the server is processed instantly after if (ws_client.available())
-- A connected potmeters value that is also read in the loop should also be sent to the client instantly

And that is not happening.

When there is no client connected, the read potmeter code (outside if (ws_client.available())) runs at "loop speed". However, if a cliient is connected it is only run when a message is sent from the client to the server. For example, when the server receives a "ping".

I hope this explains the problem well enough for someone to let me know what is going on here.

Thanks in advance!

My code:

#include <Ethernet_Generic.h>             // https://github.com/khoih-prog/Ethernet_Generic
#include <WebSockets2_Generic.h>          // https://github.com/khoih-prog/WebSockets2_Generic
// JSON
#include <ArduinoJson.h>

// Serial debugging
#include <Streaming.h>

using namespace websockets2_generic;              

#define SS_PIN              17                    
#define WEBSOCKETS_PORT     8080

byte mac[] = {0x00, 0x08, 0xDC, 0xAB, 0xCD, 0xEF};            
IPAddress ip(192, 168, 1, 120);                              
IPAddress gateway(192, 168, 1, 1);
IPAddress DNSserver(0, 0, 0, 0);
IPAddress subnet(255,255,255,0);

// Websocket server
WebsocketsServer ws_server;                // Create websocket server
WebsocketsClient ws_client;                // Create global instance websocket client
bool ws_client_connected = false;          // Boolean to determine is the websocket is active

// Keep Alive 
unsigned long lastAlive = 0;               // timestamp of last alive message from client

// JSON
StaticJsonDocument<128> jDocRx;            // https://arduinojson.org/v6/assistant/#/step1 to determine size
StaticJsonDocument<128> jDocTx;            // jDocRx for receiving, jDocTx for sending

// POTMETER
int potValue;
int previousPotvalue;


void initEthernet()                        // Initialize ethernet
{
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH);

  Ethernet.init (SS_PIN);
}

// ==================================================================================================
// SETUP

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

  initEthernet();

  Ethernet.begin(mac, ip, gateway, DNSserver, subnet);             // start ethernet, use Static IP

  ws_server.listen(WEBSOCKETS_PORT);
}

// ==================================================================================================
// LOOP

void loop()
{  
  //======================
  if(!ws_client_connected)                                         // A new connection should only be accepted when there is no active connection
  {
    ws_client = ws_server.accept();
    ws_client_connected = true;                                    // A client is connected, set to true
    lastAlive = millis();                                          // Timestamp the moment of connection as first alive
  }

  //==========================================================
   if( ((millis() - lastAlive) > 5000) && ws_client_connected)     // The client sends a ping every 2 seconds
  {                                                                // If no "alive" is received for 5 seconds &AND& ws_client_connected is true 
    ws_client.close();                                             // close the socket                               
    ws_client_connected = false;                                   // and set websocket connected to false
    Serial << "connection closed: client inactive" << endl;
  }

  //========================
  if (ws_client.available())
  {    
    WebsocketsMessage msg = ws_client.readNonBlocking();

    Serial << "Message received: " << msg.data() << endl;

    deserializeJson(jDocRx, msg.data());                           // Deserialize messagedata into jDocRx object
    
    String id = jDocRx["id"];                                      // The messages contains an id to determine the kind of message and destination

    if(id == "ping")
    {
      lastAlive = millis();                                        // Timestamp the last alive message from client
    }
    
    Serial << " inside ws_client.available()  " << endl;          // <<<<<<< THIS ONLY PRINTS AT WHEN A CLIENT SENDS DATA
    
  } // end if ws_client.available()
  
  Serial << " outside ws_client.available() " << endl;            // <<<<<<< THIS ONLY PRINTS AT "LOOP SPEED" WHEN NO CLIENT IS CONNECTED

  //========================
  potValue = analogRead(A1);
  
  if(abs(potValue - previousPotvalue) > 10){                      // If the porvalue changes by more then 10,
    previousPotvalue = potValue;                                  // store the new value and
    sendPotValue(potValue);                                       // send the new value to the client
  }
  
} // end loop

void sendPotValue(int potVal)                                    // Function that sends potvalue to client
{
  jDocTx["id"] = "pot";
  jDocTx["val"] = potVal;
  char buff[128];
  size_t len = serializeJson(jDocTx, buff);
  
  ws_client.send(buff, len);
}

Ok, I have narrowed it down to the following:
WebsocketsMessage msg = ws_client.readNonBlocking();
Actually seems to be blocking?

I put some "traces" / timestamps in the code. And there is a 2 second delay at WebsocketsMessage msg = ws_client.readNonBlocking(); The client sends an "alive" message every two seconds. With no client connected there is no delay.

So why is readNonBlocking actually blocking?

Thanks for reading!

Traces / timestamps in the code:

  
  Serial << "Trace 1: " << millis() << " milliseconds" << endl;

  if (ws_client.available())
  {   
     
    Serial << "Trace 2: " << millis() << " milliseconds" << endl; 
    
    WebsocketsMessage msg = ws_client.readNonBlocking();

    Serial << "Trace 3: " << millis() << " milliseconds" << endl; 
    
    Serial << "Message received: " << msg.data() << endl;

    deserializeJson(jDocRx, msg.data());                           // Deserialize messagedata into jDocRx object
    
    String id = jDocRx["id"];                                      // The messages contains an id to determine the kind of message and destination

    if(id == "ping")
    {
      lastAlive = millis();                                        // Timestamp the last alive message from client
    }
    
    Serial << "Trace 4: " << millis() << " milliseconds" << endl; 
    
  } // end if ws_client.available()
  
  Serial << "Trace 5: " << millis() << " milliseconds" << endl; 

  //========================
  potValue = analogRead(A1);
  
  if(abs(potValue - previousPotvalue) > 10){                      // If the porvalue changes by more then 10,
    previousPotvalue = potValue;                                  // store the new value and
    sendPotValue(potValue);                                       // send the new value to the client
  }

  Serial << "Trace 6: " << millis() << " milliseconds" << endl;

The result is

No client connected:
================
Trace 1: 6202 milliseconds
Trace 5: 6202 milliseconds
Trace 6: 6202 milliseconds
================
Trace 1: 6203 milliseconds
Trace 5: 6203 milliseconds
Trace 6: 6203 milliseconds
================
Trace 1: 6203 milliseconds
Trace 5: 6203 milliseconds
Trace 6: 6203 milliseconds
================
Trace 1: 6203 milliseconds
Trace 5: 6203 milliseconds
Trace 6: 6203 milliseconds


With client connected:
================
Trace 1: 44416 milliseconds
Trace 2: 44416 milliseconds	    <<< Two second difference between trace 2
Trace 3: 46416 milliseconds	    <<< and trace 3 after:  WebsocketsMessage msg = ws_client.readNonBlocking();
Message received: {"id":"ping"}
Trace 4: 46416 milliseconds
Trace 5: 46416 milliseconds
Trace 6: 46416 milliseconds
================
Trace 1: 46416 milliseconds
Trace 2: 46416 milliseconds
Trace 3: 48421 milliseconds
Message received: {"id":"ping"}
Trace 4: 48421 milliseconds
Trace 5: 48421 milliseconds
Trace 6: 48421 milliseconds```

Not sure exactly what it is your'e trying to accomplish, but I have to wonder if HTTP, using the ESPAsyncWebServer might be a simpler way to go. It is nearly trivial to send data asynchronously in either direction, using simple POST/GET requests for async requests/data from the client to the server, and server-sent events for sending data asynchronously from the server to the client.

@ RayLivingston, thanks for the suggestion but I chose to use a websocket connection to have nearly realtime duplex data exchange in JSON. Encoding the data in HTTP POST/GET would be much more cumbersome, wouldn't it?

At the moment sending data works as expected from client to server. So I would like to stick with this appraoch. Server to client is the problem right now, because of the blocking. If I can resolve that it works as I need it to.

EDIT: I think I have to read up on the whole websocket concept. There is something I'm not understanding about connections, clients dis/connecting, clients being connected, dropping and accepting connections.... If someone has a good guide on this, please let me know.

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