Solved - ESP32 - WiFi TCP Problem

I'm currently using an ESP32 under Arduino IDE to generate and send 33 bytes of data via WiFi (TCP Packets). The ESP32 is configured as an Access Point, so I'm connecting from my PC to this AP and the data is sent when the ESP32 receives an 'b'. The problem is that after like 2 mins, the sending stops while my code is still running (I print something on the Serial port to see that). I checked Wireshark and I noticed that the ESP32 sends randomly a packet containing FIN flag (so I guess it stops the communication without any reason).

#include <WiFi.h>
#include <SPI.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <ArduinoJson.h>

#define JSON_BUFFER_SIZE 1024
#define PI 3.1415926535
// Set these to your desired credentials.
const char *ssid = "WiFi AP";
const char *password = "abcd1234";
unsigned long long timing,n=0;

WiFiServer server(80);

unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;

byte mess[]={0xA0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,8,8,8,10,10,15,15,50,50,0xC0};
int val_finale;
int sin1,sin2;
int f1=20;
int Fe=250;
float Te=1.0/Fe;

size_t wifi_latency = 0;
IPAddress local_ip(192, 168, 4, 1);
IPAddress network_gateway(192, 168, 4, 1);
IPAddress subnet_mask(255, 255, 255, 0);

WebServer web_server(80);
WiFiClient tcp_client;

bool streaming_enabled = false;
uint8_t channel_setting_buffer[8] = {0};

void get_system_info()
{
    DynamicJsonDocument json_document(JSON_BUFFER_SIZE);
    JsonObject json_object = json_document.to<JsonObject>();  
    json_object["board_connected"] = true;
    json_object["heap"] = ESP.getFreeHeap();
    json_object["ip"] = WiFi.softAPIP().toString();
    json_object["latency"] = wifi_latency;
    json_object["mac"] = WiFi.softAPmacAddress();
    json_object["name"] = "OpenBCI-FFFF";
    json_object["num_channels"] = 8;
    json_object["version"] = "v2.0.5";
    String json_string = ""; 
    serializeJson(json_document, json_string);   
    web_server.send(200, "text/json", json_string);
}

size_t gain_from_channel(uint8_t channel_index)
{
  return 1;
}

void get_board_info()
{
    DynamicJsonDocument json_document(JSON_BUFFER_SIZE);
    JsonObject json_object = json_document.to<JsonObject>();  
    json_object["board_connected"] = true;
    json_object["board_type"] = "cyton";    
    JsonArray gains = json_object.createNestedArray("gains");
    for (size_t channel_index = 0; channel_index < 8; channel_index++) gains.add(gain_from_channel(channel_index));
    json_object["num_channels"] = 8;
    String json_string = "";
    serializeJson(json_document, json_string);
    web_server.send(200, "text/json", json_string);
}

uint8_t digit_from_char(char digit_char)
{
    return digit_char - '0';
}

void process_command()
{    
    DynamicJsonDocument json_document(JSON_BUFFER_SIZE);  
    deserializeJson(json_document, web_server.arg(0));  
    JsonObject json_object = json_document.as<JsonObject>();
    String command = json_object["command"];
    String return_message = "";
    bool streaming_state = streaming_enabled;  
    streaming_enabled = false;
  
    delayMicroseconds(50);
    
    if (command[0] == '~')
    {
        uint8_t sampling_rate = digit_from_char(command[1]);        
        return_message = "Success: Sample rate is now ";
        return_message += "250";
        return_message += "Hz";
    }
    
    else if (command == "b") streaming_state = true;
    else if (command == "s") streaming_state = false;
    streaming_enabled = streaming_state;
    web_server.send(200, "text/plain", return_message);
}

void start_streaming()
{
    streaming_enabled = true;
    web_server.send(200);
}

void stop_streaming()
{
    streaming_enabled = false;
    web_server.send(200);
}

void get_tcp_config()
{
    DynamicJsonDocument json_document(JSON_BUFFER_SIZE);
    JsonObject json_object = json_document.to<JsonObject>(); 
    json_object["connected"] = (tcp_client.connected() != 0) ? true : false;
    json_object["delimiter"] = false;
    json_object["ip_address"] = tcp_client.remoteIP().toString();
    json_object["output"] = "raw";
    json_object["port"] = tcp_client.remotePort();
    json_object["latency"] = wifi_latency;  
    String json_string = "";  
    serializeJson(json_document, json_string);
    web_server.send(200, "text/json", json_string);
}

IPAddress ip_from_string(String ip_string)
{
    IPAddress ip_address(0, 0, 0, 0);
    ip_address.fromString(ip_string);
    return ip_address;
}

void set_tcp_config()
{
    streaming_enabled = false; 
    DynamicJsonDocument json_document(JSON_BUFFER_SIZE);   
    deserializeJson(json_document, web_server.arg(0));
    JsonObject json_object = json_document.as<JsonObject>();
    String tcp_client_ip = json_object["ip"];
    wifi_latency = json_object["latency"];
    uint16_t tcp_client_port = json_object["port"];    
    tcp_client.stop();    
    tcp_client.connect(ip_from_string(tcp_client_ip), tcp_client_port);
    tcp_client.setNoDelay(1);  
    get_tcp_config();
}

void stop_tcp_connection()
{
    streaming_enabled = false;   
    tcp_client.stop();
    get_tcp_config();
}

void invalid_request()
{
    web_server.send(404, "text/plain", "Invalid Request!");
}

void switch_raw_output()
{
    web_server.send(200, "text/plain", "Output mode configured to raw");
}

void gen_sinus(unsigned char *mess, int n)
{
    sin1 = (int)(10000*sin(2*PI*f1*n*Te)+10000);
    mess[1]=n;
    mess[2]=((unsigned char)(sin1>>16));
    mess[3]=((unsigned char)(sin1>>8));
    mess[4]=((unsigned char)sin1);
}

void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println("Configuring access point...");
    WiFi.mode(WIFI_AP);
    WiFi.softAP(ssid, password);
    delay(250);    
    WiFi.softAPConfig(local_ip, network_gateway, subnet_mask);      
    delay(250);
    MDNS.begin("openbci");
    
    web_server.on("/all", HTTP_GET, get_system_info);
    web_server.on("/board", HTTP_GET, get_board_info);
    web_server.on("/command", HTTP_POST, process_command);      
    web_server.on("/stream/start", HTTP_GET, start_streaming);
    web_server.on("/stream/stop", HTTP_GET, start_streaming);
    web_server.on("/output/raw", HTTP_GET, switch_raw_output);    
    web_server.on("/tcp", HTTP_GET, get_tcp_config);
    web_server.on("/tcp", HTTP_POST, set_tcp_config);
    web_server.on("/tcp", HTTP_DELETE, stop_tcp_connection);
    web_server.onNotFound(invalid_request);
      
    MDNS.addService("http", "tcp", 80);
    web_server.begin();
}

void loop() {

  if(streaming_enabled == true)
  {
        gen_sinus(mess,n);
        n++;
        tcp_client.write(mess,33);
        //Serial.println(n);
        delay(10);
  }
  web_server.handleClient();
}

How can I solve this ? PS: I dont know much about communication protocols.

if you change the pace of the tcp_client.write() what happens?

(with something like this to avoid delay)

void loop() {
  static uint32_t lastTCP;
  web_server.handleClient();
  if (streaming_enabled && (millis() - lastTCP >= 100)) { // TCP message every @10Hz (every 100ms)
    gen_sinus(mess, n);
    tcp_client.write(mess, 33);
    n++;
    lastTCP = millis();
  }
}

there might be otherwise keep alive or DHCP lease issues... you might want to print a status of the WiFi connexion

1 Like

Hello, thanks for your answer. I just tried a temporary solution but even without the delay it's still not working. Can you give me more informations about keep alive or DHCP ? I don't know much about it... But I guess it's not really a problem of WiFi as the access point remains available even after the packets crash. It's more like the ESP stops the communication after some time (I wonder if it's not a problem on the libraries).

do you mean with my code above?
Not working means it disconnects after 2 minutes?

Do you still have the web server working when the TCP flow stops?

1 Like

Yes with your code it still crashes after 2 minutes. My code keeps running even after the packets are not sent (it just seems like the tcp_client.write() is ignored or I don't know) and I can even see the AP with my PC after the TCP flow stops.

OK so it means the Wi-Fi connexion is fine

when you say "crash" you mean the tcp_client.write() just does not do anything ?
can you add code to test if it's still connected and if not reconnect?

the FIN flag was coming from the ESP or from the PC?

1 Like

Sorry if I'm not using the right terms :confused:
Yes, I mean the code runs normally but the TCP flow stops so I just suggest that it's a problem with the write function...
I don't think it reconnects as I can see with Wireshark that the ESP32 sends the FIN flag to the PC directly after a 'normal' data packet...
Here is the Wireshark file (delay_10ms_1_canal) if you want to check it (use ip.dst == 192.168.4.2 && tcp as filters to see packets coming to the PC)
Thanks for your time.

hard to tell just from the Wireshark. there are multiple FIN messages along the way as well as RESET

can you test with this loop

void loop() {
  static uint32_t lastTCP;
  web_server.handleClient();
  if (streaming_enabled && (millis() - lastTCP >= 100)) { // TCP message every @10Hz (every 100ms)
    if (tcp_client.connected() == 0) {
      Serial.println("bad luck, disconnected");
    }
    gen_sinus(mess, n);
    size_t byteCount = tcp_client.write(mess, 33);
    Serial.print(n);
    Serial.write('\t');
    Serial.println(byteCount);
    n++;
    lastTCP = millis();
  }
}

it should confirm the disconnect

1 Like

Thanks a lot for your help, I solved the problem by reinstalling the libraries of ESP32, seems like the one I had installed from the board manager have some bugs but the ones from GitHub are fine...
It's working perfectly now!

Thanks again,
Adel.

Good to know!