Opta - Conflict between webServer - mDNS and NTPClient. Web Server not found

Hi all,

Sorry to disturb the communicty but I need your help w/ a big project requiring, in particular, :

Both work properly independently. No pb.
Both work w/ EthernetClient library.

As soon as I merge both codes, the web Server doens't work. I mean not found in local network.

To be clear :

  • to connect OPTA to my TCP server, I use :

           EthernetClient clientTCP;
           .....
           clientTCP.connect(TCP_SERVER_ADDR, TCP_SERVER_PORT);
           .....
    
  • to run Web Server, I use :

           EthernetClient clientWeb;
           EthernetServer webServer(80);
           .....
           into loop
                        clientWeb = webServer.available(); 
                        .....
    

If I disable clientTCP.connect, web Server works well.
Therefore, I think that it is a pb w/ the number of EthernetClient. Therefore I tried :

         EthernetClient client[2];
         .....
         client[0].connect(TCP_SERVER_ADDR, TCP_SERVER_PORT);
         client[1] = webServer.available(); 

But same behavior. Web server doesn't work :frowning:

I tried to put clientWeb declaration before TCP connect. But always the same pb. Web server doesn't work. I think that clientTCP.connect must take the priority.

I think that there is a conflict w/ EthernetClient. But how to solve it, I don't kown.
All the ideas will be welcoming, believe me :slight_smile:

Hope that it is clear !
Thanks to read me.

are you on a current version of the mbed Arduino core?

Thanks for your message.
Arduino Mbed OS Opta Boards V4.2.1

can you show a complete sketch?

In order to be sure, I follow your advice about MBED version. I mean upgrade my Arduino v1.8.19 w/ the last one, V4.2.4.
And now, it works !
It is a good news but I don't understand why. V4.2.1 is not old. It should be work w/ mDNS library, webServer and tcpClient.
I will continue my project w/ a particular attention about this part. And if pb, I will come back to this topic.
Sorry for the disturbance.
Thanks

To answer you, here is an extract of my project into a dedicated sketch and small adaptation in order to work in stand alone.
For information, all connection security (reboot, missing connection, and so on...) have been removed for easy reading

#include <Ethernet.h>
#include <mbed_mktime.h>

// webServer
#include <SPI.h>
#include <EthernetUdp.h>
#include <ArduinoMDNS.h>
#include <ArduinoJson.h>

EthernetUDP udp;
MDNS mdns(udp);
EthernetServer webServer(80);

// tcpClient
EthernetClient tcpClient;
const char* TCP_SERVER_ADDR = "xxx.xxx.xxx.xxx";
const int TCP_SERVER_PORT = 3007;
String tcpStrReceive = "";

// timer
unsigned long timerDHCPRenewal = 0;
unsigned long timerCyclicSync = 0;

void setup()
{

  while (!Serial) { }
  
  // Ethernet connection via DHCP
  if (Ethernet.begin() == 0) {
    Serial.println("ethernet DHCP error !");
    while(1);
  }
  
  IPAddress ip = Ethernet.localIP();
  Serial.print("Ethernet.localIP(): ");
  Serial.println(ip);

  // webServer
  webServer.begin();
  if (mdns.begin(ip, "myWebServer")) {
    mdns.addServiceRecord("mDNS Webserver._http", 80, MDNSServiceTCP);
    Serial.println("mDNS startup : http://myWebServer.local");
  } else {
    Serial.println("mDNS error !");
    while(1);
  }
  
  // tcpClient
  if (tcpClient.connect(TCP_SERVER_ADDR, TCP_SERVER_PORT)) {
    if (tcpClient.connected()) {
      tcpClient.write("$SUN,CYCLIC_SYNC\r\n");
      tcpClient.flush();
    }
  } else {
    Serial.println("tcp connect error !");
    while(1);
  }
  
}

void loop()
{ 

  // maintain renewal DHCP
  if (millis() - timerDHCPRenewal >= 20000UL) {
    Ethernet.maintain();
    timerDHCPRenewal = millis();      
  }

  // webServer
  mdns.run(); // https://github.com/arduino-libraries/ArduinoMDNS/blob/master/examples/Ethernet/RegisteringServices/RegisteringServices.ino
  EthernetClient clientWeb = webServer.available();            
  if (clientWeb) {
    String request = clientWeb.readStringUntil('\r');
    Serial.print("Requête: ");
    Serial.println(request);
    String jsonResponse;
    if (request.indexOf("GET /data") >= 0) {
      Serial.println("GET /data");
      StaticJsonDocument<200> doc;
      doc["status"] = "ok";
      doc["time"] = String(rtcGet_Func());
      doc["value1"] = "value1";
      doc["value2"] = "value2";
      doc["value3"] = "value3";
      doc["value4"] = "value4";
      doc["valueN"] = "valueN";
      serializeJson(doc, jsonResponse);
      clientWeb.println("HTTP/1.1 200 OK");
      clientWeb.println("Content-Type: application/json");
      clientWeb.print("Content-Length: ");
      clientWeb.println(jsonResponse.length());
      clientWeb.println();
      clientWeb.println(jsonResponse);
      Serial.print("jsonResponse: ");
      Serial.println(jsonResponse);
    } else {
      Serial.println("GET /xxxxxxxxxxxxxxxxxx");
      clientWeb.println("HTTP/1.1 404 Not Found");
      clientWeb.println();  
    }
    clientWeb.stop();
  }

  // tcpClient
  // message IN
  if (tcpClient.available()) {
    char server2Opta = tcpClient.read();
    if (server2Opta == '\r') {
        Serial.print("SERVER2OPTA: ");
        Serial.println(tcpStrReceive);
        String opta2Server = "";
        if (tcpStrReceive.equals("$SUN,VER")) opta2Server = "OK:VER=BETA V1.0 2025-04-08";
        // else if (...) ....;
        else opta2Server = "OK:DEFAULT";  
        tcpStrReceive = "";      
        tcpSend_Func(opta2Server);
    } else tcpStrReceive += server2Opta; 
  }
  // message OUT
  if (millis() - timerCyclicSync >= 10000UL) {
    Serial.println("CYCLIC SYNC");
    tcpSend_Func("CYCLIC_SYNC");
    timerCyclicSync = millis();    
  }

}

void tcpSend_Func(String msg2Server) {
  if (msg2Server.length() != 0) {
    msg2Server = "$SUN," + rtcGet_Func() + "," + msg2Server;
    msg2Server += "\r\n";
    int ArrayLength = msg2Server.length() + 1; //+1 for 0x00h
    char charArray[ArrayLength];
    msg2Server.toCharArray(charArray,ArrayLength);
    tcpClient.write((const uint8_t*)charArray, sizeof(charArray));
    tcpClient.flush();
    Serial.print("OPTA2SERVER: ");
    Serial.print(msg2Server);
  }
}

String rtcGet_Func() {
  char buffer[32];
  tm t;
  _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);
  strftime(buffer, 32, "%Y%m%d%H%M%S", &t); //format YYYYMMDDHHMMSS
  return String(buffer);
}


I was betting on my fix in 4.1.1 so maybe you had an older version than 4.2.1

1 Like

Hi, how do you manage to not stuck the loop inside request of the client on the webserver?

Hi,
Thanks for your message.
In my example above, for me, the only blocking method is tcpConnect.
Indeed, if my TCP connector is not open or not accessible, the timeout accordingly is ~18sec. before to continue.
It is the reason why in my global project, the tcpConnect is executed in low priority. I mean only when there is no others sensitives sensors.

So, on the webserver part, the data will be sent to the browser only once in the loop on the clientWeb connection because you have nested inside a if (clientWeb) instead of a while (clientWeb) right?

void loop()
{ 
  // webServer
  mdns.run(); // https://github.com/arduino-libraries/ArduinoMDNS/blob/master/examples/Ethernet/RegisteringServices/RegisteringServices.ino
  EthernetClient clientWeb = webServer.available();            
  if (clientWeb) {
    String request = clientWeb.readStringUntil('\r');
    Serial.print("Requête: ");
    Serial.println(request);
    String jsonResponse;
    if (request.indexOf("GET /data") >= 0) {
      Serial.println("GET /data");
      StaticJsonDocument<200> doc;
      doc["status"] = "ok";
      doc["time"] = String(rtcGet_Func());
      doc["value1"] = "value1";
      doc["value2"] = "value2";
      doc["value3"] = "value3";
      doc["value4"] = "value4";
      doc["valueN"] = "valueN";
      serializeJson(doc, jsonResponse);
      clientWeb.println("HTTP/1.1 200 OK");
      clientWeb.println("Content-Type: application/json");
      clientWeb.print("Content-Length: ");
      clientWeb.println(jsonResponse.length());
      clientWeb.println();
      clientWeb.println(jsonResponse);
      Serial.print("jsonResponse: ");
      Serial.println(jsonResponse);
    } else {
      Serial.println("GET /xxxxxxxxxxxxxxxxxx");
      clientWeb.println("HTTP/1.1 404 Not Found");
      clientWeb.println();  
    }
    clientWeb.stop();
  }

  // tcpClient
  // message IN
  if (tcpClient.available()) {
    char server2Opta = tcpClient.read();
    if (server2Opta == '\r') {
        Serial.print("SERVER2OPTA: ");
        Serial.println(tcpStrReceive);
        String opta2Server = "";
        if (tcpStrReceive.equals("$SUN,VER")) opta2Server = "OK:VER=BETA V1.0 2025-04-08";
        // else if (...) ....;
        else opta2Server = "OK:DEFAULT";  
        tcpStrReceive = "";      
        tcpSend_Func(opta2Server);
    } else tcpStrReceive += server2Opta; 
  }
  // message OUT
  if (millis() - timerCyclicSync >= 10000UL) {
    Serial.println("CYCLIC SYNC");
    tcpSend_Func("CYCLIC_SYNC");
    timerCyclicSync = millis();    
  }

}

void tcpSend_Func(String msg2Server) {
  if (msg2Server.length() != 0) {
    msg2Server = "$SUN," + rtcGet_Func() + "," + msg2Server;
    msg2Server += "\r\n";
    int ArrayLength = msg2Server.length() + 1; //+1 for 0x00h
    char charArray[ArrayLength];
    msg2Server.toCharArray(charArray,ArrayLength);
    tcpClient.write((const uint8_t*)charArray, sizeof(charArray));
    tcpClient.flush();
    Serial.print("OPTA2SERVER: ");
    Serial.print(msg2Server);
  }
}

String rtcGet_Func() {
  char buffer[32];
  tm t;
  _rtc_localtime(time(NULL), &t, RTC_FULL_LEAP_YEAR_SUPPORT);
  strftime(buffer, 32, "%Y%m%d%H%M%S", &t); //format YYYYMMDDHHMMSS
  return String(buffer);
}

Yes it is.
It is just to send few data on request from my Android device application in the same local network.
Reason why I use mDNS in order to able to find OPTA web server in DHCP mode.

Thanks for the response @manucast. Do you use a scheduler like TaskScheduler on your complete sketch?

Hi Juraj,
I come back to you about my initial issue.
Because the pb is present yet :frowning:

Initially, I thought that it was a conflict between tcp client and web server. But not as wrote above. Sorry for my mistake !

After fully investigation, I discover that there is a conflict between webServer and my RTC module.
This RTC module is implemented w/ NTPClient.h and EthernetUDP.h librairies.
As soon as timeClient.begin(); is called, the webServer becomes blocked few sec. after !
Maybe I did a mistake but I don't know where. Here is the part of code blocking the webServer.

#include <NTPClient.h>
#include <mbed_mktime.h>
#include <EthernetUdp.h>
EthernetUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 60000);

timeClient.begin();

Please commennt, thanks :slight_smile:

No. I work w/ a small main.ino and a lot of different module (.h && .ino).
Each modules are called into main loop the fastest as possible (no delay() implemented)
I know perfectly where are the blocking method.
Therefore, as soon as I trigger a sensitive function, I stop calling modules accordingly. For example, tcp module or rtc module or ...
For the rest, all works together w/o issue :slight_smile:

1 Like

Here is my solution in order to solve this issue between webServer and NTPClient, and allow both working simultaneously.
Put begin()/update() and add end() into a function periodically called instead of begin into setup(), update() into loop() and never end(). I mean :


  ...
  timeClient.begin();
  timeClient.update();
  epoch = timeClient.getEpochTime();
  set_time(epoch);
  timeClient.end();
  ...

W/ this modification, all works perfectly :wink:

1 Like

the mbed networking in the Arduno platform is set to use max 4 sockets. maybe that is the problem

Thanks for your message.
But, I don't think so.
3 in my side :

  • 1x EthernetClient for tcp communication w/ my remote tcp server,
  • 1x NTPClient for update date/time,
  • 1x EthernetServer for web server.
  • that's all.

@manucast
sorry for the question again but I don't understand how you can make the webserver work when a client is connected without blocking the loop by not executing tasks. Thanks for the answer

maybe the NTP UDP needs two.
and the web server's client is one too.

Hi AlphaGamma,

To be clear, the clients trigger a small request when it needs to retrieve informations from OPTA.
To avoid blocking on clientWeb.readStringUntil('\r') command, I use a timeout (.setTimeout()).

I noticed that if client connected to local network by :

  • by RJ45, setTimeout(10) is enough,
  • by WiFi, timeout value must increase to 800ms. I mean setTimeout(800). Otherwise, the request is not 100% sure to be complet.