WiFi & Sockets

I’m trying to get very basic sockets communications going between two 8266 WiFi nodes. It works sporadically. There seems to be no real problem with the basic configuration of the two 8266 nodes, as the setup code is all copied from my working application that has 7 8266s all talking to each other over a tree-structured network where three of the 8266s are both APs and Stations, the rest are just Stations.

But, with the below code, most of the time the “server” messages do make it to the “client” correctly, and they are “reflected” back, but rarely make it back to the server.

What am I doing wrong here? The SERVER define controls whether the code is built for the server node or client node. I am running both on NODEMCUs.

#include <ESP8266WiFi.h>

#define SERVER

#if defined(SERVER)
WiFiServer server = WiFiServer(8000);

#endif

uint8_t Cnt = 123;

uint8_t State = 0;

WiFiClient client = WiFiClient();

IPAddress Netmask = IPAddress(255, 255, 255, 0);

char *STASSID = "TP-LINK_A47DC2";
char *STAPass = "79517515";
IPAddress STAGatewayIP = IPAddress(10, 0, 0, 1);
IPAddress STAIP = IPAddress(10, 0, 0, 50);

char *APSSID = "WiFiNetwork";
char *APPass = "WiFiPassword";
IPAddress APIP = IPAddress(192, 168, 0, 1);

IPAddress ClientIP = IPAddress(192, 168, 0, 2);


char s[80];
int i = 0;
uint32_t timer = 0;

#if defined(SERVER)
void ServerSetup()
{
	boolean success = false;

	while (!success)
	{
		// Disconnect, if connected
		WiFi.disconnect();

		// Set Mode
		if (!(success = WiFi.mode(WiFiMode_t::WIFI_AP_STA)))
			continue;

		// Configure STA
		if (!(success = WiFi.config(STAIP, STAGatewayIP, Netmask, STAGatewayIP)))
			continue;

		// Configure AP
		if (!(success = WiFi.softAPConfig(APIP, STAGatewayIP, Netmask)))
			continue;

		// Start/Connect AP + STA
		WiFi.persistent(true);
		if (!(success = WiFi.setAutoConnect(true)))
			continue;

		if (!(success = WiFi.begin(STASSID, STAPass)))
			continue;

		if (!(success = WiFi.setAutoReconnect(true)))
			continue;

		if (!(success = WiFi.softAP(APSSID, APPass, 1)))
			continue;
	}

	server.begin();

	Serial.printf("\n\nServer Ready\n");
}


void ServerLoop()
{
	switch (State)
	{
		case 0:
			client = server.available();
			if (client)
			{
				Serial.printf("Server: Wrote: %03d ", Cnt);
				client.printf("\r\n%03d\r\n", Cnt);
				timer = millis();
				State = 1;
			}
			break;

		case 1:
			client = server.available();
			while (client.available())
			{
				char c = client.read();
				if (c == '\r')
					continue;
				else if (c == '\n')
				{
					if (i > 0)
					{
						int val = atoi(s);
						Serial.printf("Read: %03d => ", val);
						if (val == Cnt)
						{
							Serial.printf("Match\n");
							Cnt++;
							State = 0;
						}
						else
						{
							Serial.printf("No Match\n");
							Cnt++;
							client.stop();
							State = 0;
						}
					}
					i = 0;
					s[i] = '\0';
				}
				else
				{
					s[i++] = c;
					s[i] = '\0';
					Serial.print(c);
				}
			}
			if (millis() - timer > 3000)
			{
				client.stop();
				Serial.printf("Timeout\n");
				Cnt++;
				State = 0;
			}
			break;
	}
}

#else
void ClientSetup()
{
	boolean success = false;

	while (!success)
	{
		// Disconnect, if connected
		WiFi.disconnect();

		// Set Mode
		if (!(success = WiFi.mode(WiFiMode_t::WIFI_STA)))
			continue;

		// Configure STA
		if (!(success = WiFi.config(ClientIP, APIP, Netmask, APIP)))
			continue;

		WiFi.persistent(true);
		if (!(success = WiFi.setAutoConnect(true)))
			continue;

		// Connect STA
		if (!(success = WiFi.begin(APSSID, APPass)))
			continue;

		if (!(success = WiFi.setAutoReconnect(true)))
			continue;
	}

	client.connect(APIP, 8000);

	Serial.printf("\n\nClient Ready\n");
}


void ClientLoop()
{
	if (!client.connected())
	{
		//client.stop();
		client.connect(APIP, 8000);
	}
	else
	{
		char s[80];
		int i = 0;
		while (client.available())
		{
			char c = client.read();
			if (c == '\r')
				continue;
			else if (c == '\n')
			{
				if (i > 0)
				{
					int val = atoi(s);
					Serial.printf("Client: Wrote: %03d\n", val);
					client.printf("\r\n%03d\r\n", val);
				}
				i = 0;
				s[i] = '\0';
			}
			else
			{
				s[i++] = c;
				s[i] = '\0';
			}
		}
	}
}
#endif


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

#if defined(SERVER)
	ServerSetup();
#else
	ClientSetup();
#endif
}



void loop()
{
#if defined(SERVER)
	ServerLoop();
#else
	ClientLoop();
#endif
}

Regards,
Ray L.

I am not an expert on how the esp wifi library is implemented, but I think a server can have more clients and available returns one of them.

you send data over 'socket', the client esp responses on that 'socket', but you get some client from the server with available. by chance it is the same, but it can be some other. some old not closed. you do not test client.connected() and you do not close 'sockets' disconnected by client

I think you're right, but I can't find a single example that makes clear exactly how to do this correctly. I want to be able to send and receive data between two nodes. AFAICT, the client side is pretty straight-forward - if not connected, do a client.connect. if client.available is true, read data, otherwise, write data. But the server side is not at all clear. How do I know WHEN to do a server.available()? WHEN is the connection closed? HOW do I make the connection persist, since I need to be sending data in both directions fairly often (at least once/second). The problem I see most often is the server thinks there is a connection, and sends data, but the client never gets it.

This should not be difficult, but the examples I've found are all so trivial, they are of no help whatsoever.

Regards, Ray L.

client.connected(), tests the state of the socket. works for server side socket too. or you can use state(). ESTABLISHED is the right one.

you should hold the client object if you want read the answer from the client. the server is there only to initiate the connection. then the server side and client side sockets are simply connected sockets.

to distinguish clients, client object has

  IPAddress remoteIP();
  uint16_t  remotePort();
  IPAddress localIP();
  uint16_t  localPort();

If there's a way to make this work reliably, it is eluding me. I've spent hours trying dozens of different configurations of the code, and haven't found anything that works more than sporadically. It seems to me, the basic logic should be:

Server Side:

if (!client.connected())
    client = server.available();
else if (client)
{
    client.print("Something");
    while (client.available()
        Serial.print((char)client.read());
}

Client Side:

if (!client.connected())
    client.connect(ssid, password);
else
{
    client.print("Something");
    while (client.available())
        Serial.print((char)client.read());
}
[\code]

But, that logic simply does not work.  It may sporadically get some data through, but not often.

It seems to me that before doing a write/print on a client, client.connected() should be verified, as the operation will obviously fail if the client is NOT connected.  But reads can be done anytime, as the client object may contain valid data even after the connection has closed.  Of course, even checking client.connected() immediately before the write, there is no guarantee the connection won't be closed a uSec later.  So, I just don't see how you can ever be sure the data actually got out.

Surely, somewhere, there is a good example of how to do this right, and not just trivial examples that transmit a single message in one direction, then close the socket?

Regards,
Ray L.

The more I dig into this, the less sense it makes… I’ve backed off to about the most trivial example I could think of - the client makes the connection, sends some data, then closes the connection. The result is… confusing. First, here is the code:

#include <ESP8266WiFi.h>

//#define SERVER

#if defined(SERVER)
WiFiServer server = WiFiServer(8888);

#endif

WiFiClient client = WiFiClient();

IPAddress Netmask = IPAddress(255, 255, 255, 0);

char *STASSID = "TP-LINK_A47DC2";
char *STAPass = "79517515";
IPAddress STAGatewayIP = IPAddress(10, 0, 0, 1);
IPAddress STAIP = IPAddress(10, 0, 0, 50);

char *APSSID = "WiFiNetwork";
char *APPass = "WiFiPassword";
IPAddress APIP = IPAddress(192, 168, 0, 1);

IPAddress ClientIP = IPAddress(192, 168, 0, 2);


char s[80];
int i = 0;
uint32_t timer = 0;

#if defined(SERVER)
void ServerSetup()
{
	boolean success = false;

	while (!success)
	{
		// Disconnect, if connected
		WiFi.disconnect();

		// Set Mode
		if (!(success = WiFi.mode(WiFiMode_t::WIFI_AP_STA)))
			continue;

		// Configure STA
		if (!(success = WiFi.config(STAIP, STAGatewayIP, Netmask, STAGatewayIP)))
			continue;

		// Configure AP
		if (!(success = WiFi.softAPConfig(APIP, STAGatewayIP, Netmask)))
			continue;

		// Start/Connect AP + STA
		WiFi.persistent(true);
		if (!(success = WiFi.setAutoConnect(true)))
			continue;

		if (!(success = WiFi.begin(STASSID, STAPass)))
			continue;

		if (!(success = WiFi.setAutoReconnect(true)))
			continue;

		if (!(success = WiFi.softAP(APSSID, APPass, 6)))
			continue;
	}

	server.begin();

	pinMode(16, OUTPUT);

	Serial.printf("\n\nServer Ready\n");
}


void ServerLoop()
{
	if (!client.connected())
	{
		digitalWrite(16, HIGH);
		client = server.available();
	}
	else if (client.connected())
	{
		digitalWrite(16, LOW);
		while (client.available())
		{
			Serial.print((char)client.read());
		}
	}
}

#else

void ClientSetup()
{
	boolean success = false;

	while (!success)
	{
		// Disconnect, if connected
		WiFi.disconnect();

		// Set Mode
		if (!(success = WiFi.mode(WiFiMode_t::WIFI_STA)))
			continue;

		// Configure STA
		if (!(success = WiFi.config(ClientIP, APIP, Netmask, APIP)))
			continue;

		WiFi.persistent(true);
		if (!(success = WiFi.setAutoConnect(true)))
			continue;

		// Connect STA
		if (!(success = WiFi.begin(APSSID, APPass)))
			continue;

		if (!(success = WiFi.setAutoReconnect(true)))
			continue;
	}
	client.setTimeout(5000UL);

	pinMode(16, OUTPUT);

	Serial.printf("\n\nClient Ready\n");
}


void ClientLoop()
{
	static int32_t last = 0;

	if (!client.connected())
	{
		digitalWrite(16, HIGH);
		if (client.connect(APIP, 8888))
		{
			digitalWrite(16, LOW);
			uint32_t delta = millis() - last;
			client.printf("Hello %lu\n", delta);
			Serial.printf("Wrote %lu\n", delta);
			last = millis();
			delay(10);
			client.stop();
		}
	}
}
#endif


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

#if defined(SERVER)
	ServerSetup();
#else
	ClientSetup();
#endif
}



void loop()
{
#if defined(SERVER)
	ServerLoop();
#else
	ClientLoop();
#endif
}

The LED on each NODEMCU is lit whenever the client.connected() == true. The client does a connect(), sends a message, prints that same message to its console, waits 1 mSec, then does a client.stop(). The server waits for a connection, prints whatever data it gets to its console.

What I see is, at times, it behaves as expected - the LEDs blink more or less in unison, and the same data is displayed on both consoles. But, that only last a few seconds at most, then BOTH LEDs get stuck ON, the latest message is displayed on the server, but NOT on the client. After some period of time, as much as 1-15 seconds, the LEDs go out, the message finally appears on the client console. Sometimes it will do another burst of correct transfers, other times BOTH LEDS go out for several seconds. Overall throughput is abysmal, averaging single digits of packets per second. Getting stuck with the LEDs ON, and the client message NOT being displayed indicates the client.print() is taking a looooooooong time to execute. Why?? Both 8266s see a valid connection, so how is it getting stuck like that??

Regards,
Ray L.

Interestingly, when the client.print call "hangs", it hangs for exactly 5 seconds every time. In most cases, for some period of time after the hang, it continues to hang on subsequent cycles, with the server never receiving any data, despite seeing a valid connection the whole time. Eventually, it all starts working again.

This is completely unusable as it is. Who could possibly use a WiFi adaptor that goes off-line for 5 or more seconds every few seconds? There MUST be something I'm doing wrong, but d@mned if I can see what it is, and the problem seems to be happening in the bowels of the ESP libraries....

Regards, Ray L.

This is interesting/disconcerting.... I changed the code so the softAP is forced to channel 11, and it now seems to be working well, with out the long "stalls". This suggest a problem with interference, but it was still failing on channels 1 and 6 with my TPLINK AP turned off. I don't know what to make of this...

Regards, Ray L.

you could try core 2.4 rc2. I had strange connection problems with rc1 and with rc2 it seems stable.

I'm using 2.2.0. I started with 2.3.0, and found it has SERIOUS problems. Where can I get 2.4?

Switching channels does seem to resolve my problems, but I don't know why, since there are NO other networks here when my router is turned off. Channel 1 sucks. Channel 6 is only slightly better. Channel 11 works great. Most higher channels seem to work well.

Regards, Ray L.

add https://github.com/esp8266/Arduino/releases/download/2.4.0-rc2/package_esp8266com_index.json in IDE properties

Unfortunately, that seems to have completely screwed me. I can no longer build ANY 8266 code. The libraries are installed, and the boards all show up in the Boards menu, but all builds fail:

Arduino: 1.8.4 (Windows 10), Board: "NodeMCU 0.9 (ESP-12 Module), 80 MHz, 115200, 4M (3M SPIFFS)"

E:\Users\RayL\Documents\AppData\Arduino\arduino-1.8.4\arduino-builder -dump-prefs -logger=machine -hardware E:\Users\RayL\Documents\AppData\Arduino\arduino-1.8.4\hardware -hardware C:\Users\RayL\AppData\Local\Arduino15\packages -tools E:\Users\RayL\Documents\AppData\Arduino\arduino-1.8.4\tools-builder -tools E:\Users\RayL\Documents\AppData\Arduino\arduino-1.8.4\hardware\tools\avr -tools C:\Users\RayL\AppData\Local\Arduino15\packages -built-in-libraries E:\Users\RayL\Documents\AppData\Arduino\arduino-1.8.4\libraries -libraries C:\Users\RayL\Documents\Arduino\libraries -fqbn=esp8266:esp8266:nodemcu:CpuFrequency=80,UploadSpeed=115200,FlashSize=4M3M -ide-version=10804 -build-path C:\Users\RayL\AppData\Local\Temp\arduino_build_137549 -warnings=none -build-cache C:\Users\RayL\AppData\Local\Temp\arduino_cache_68001 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.xtensa-lx106-elf-gcc.path=C:\Users\RayL\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\1.20.0-26-gb404fb9-2 -prefs=runtime.tools.mkspiffs.path=C:\Users\RayL\AppData\Local\Arduino15\packages\esp8266\tools\mkspiffs\0.1.2 -prefs=runtime.tools.esptool.path=C:\Users\RayL\AppData\Local\Arduino15\packages\esp8266\tools\esptool\0.4.8 -verbose C:\Users\RayL\Documents\Arduino\sketch_dec06a\sketch_dec06a.ino

Board nodemcu (platform esp8266, package esp8266) is unknown

Error compiling for board NodeMCU 0.9 (ESP-12 Module).

Regards, Ray L.

sorry. did you try setting board parameters again in Tools menu after upgrading esp core package?

I ended up having to re-install the IDE, then the 8266 package. No clue what went wrong the first time, but I couldn't figure out how to fix it. It's working fine now.

Regards, Ray L.

end you returned to 2.2 or you try the 2.4 rc2?

2.4-rc2, which was handy, because I also needed the ability to set the max # of connections for softAP mode to 8.

Regards,
Ray L.

and the connection stability is better?

I actually didn't have connection problems with 2.2.0, once I started using higher numbered channels. 2.3.0 has TERRIBLE connection problems. I switched to 2.4.0-rc2 to be able to increase the number of AP connections. I do not understand why the channel number makes such a difference in reliability, given that the lower channel work very poorly even when there are NO other WiFi networks in the area (I live out in the sticks, so my network is the only one within about 500 feet or more.

Regards, Ray L.

Hmmmm… Seems I am now back to having connection problems… I’m playing with different ways of communicating with a number of clients. One approach is having everyone using a single port, talking to a single server. That seems to work well, with good through-put, for some period, then the server eventually decides to stop responding most of the time. One or two connections will get through, sporadically. Another approach, in the code below, sets up multiple servers on that same port. That works, surprisingly, MUCH worse - with MUCH slower response and throughput, and eventually ALL the servers stop responding. I’ve also tried multiple servers on different ports, which each client having its own port. This also does not work particularly well.

Why do the servers stop responding? I am currently doing a server.begin() after each connection closes, which seems to slightly improve reliability (the data flows a bit longer before the server stops responding), but also slows down the connection rate, so it’s not really a help overall.

Regards,
Ray L.

#include <ESP8266WiFi.h>
extern "C" {
#include "user_interface.h"
}

#define SERVER
#define ECHO

#define NSOCKETS	6

#if defined(SERVER)
WiFiServer servers[NSOCKETS] = { WiFiServer(8000), WiFiServer(8000), WiFiServer(8000), WiFiServer(8000), WiFiServer(8000), WiFiServer(8000) };
uint32_t statusTime = 0;
#endif

WiFiClient clients[NSOCKETS] = { WiFiClient(), WiFiClient(), WiFiClient(), WiFiClient(), WiFiClient(), WiFiClient() };

IPAddress Netmask = IPAddress(255, 255, 255, 0);

char *STASSID = "TP-LINK_A47DC2";
char *STAPass = "79517515";
IPAddress STAGatewayIP = IPAddress(10, 0, 0, 1);
IPAddress STAIP = IPAddress(10, 0, 0, 50);

char *APSSID = "WiFiNetwork";
char *APPass = "WiFiPassword";
IPAddress APIP = IPAddress(192, 168, 0, 1);

IPAddress ClientIP = IPAddress(192, 168, 0, 2);
//IPAddress ClientIP = IPAddress(192, 168, 0, 3);
//IPAddress ClientIP = IPAddress(192, 168, 0, 4);
//IPAddress ClientIP = IPAddress(192, 168, 0, 5);
//IPAddress ClientIP = IPAddress(192, 168, 0, 6);


char s[80];
int i = 0;
uint32_t timer = 0;

#if defined(SERVER)
void ServerSetup()
{
	boolean success = false;

	while (!success)
	{
		// Disconnect, if connected
		WiFi.disconnect();

		// Set Mode
		if (!(success = WiFi.mode(WiFiMode_t::WIFI_AP_STA)))
			continue;

		// Configure STA
		if (!(success = WiFi.config(STAIP, STAGatewayIP, Netmask, STAGatewayIP)))
			continue;

		// Configure AP
		if (!(success = WiFi.softAPConfig(APIP, STAGatewayIP, Netmask)))
			continue;

		// Start/Connect AP + STA
		WiFi.persistent(true);
		if (!(success = WiFi.setAutoConnect(true)))
			continue;

		//if (!(success = WiFi.begin(STASSID, STAPass)))
		//	continue;

		Serial.print("\nSTA Connected to TP-LINK\n");

		if (!(success = WiFi.setAutoReconnect(true)))
			continue;

		//struct softap_config config;
		//wifi_softap_get_config(&config); // Get config first.
		//config.max_connection = 8;
		//wifi_softap_set_config(&config);

		if (!(success = WiFi.softAP(APSSID, APPass, 11)))
			continue;
		
		struct softap_config config;
		wifi_softap_get_config(&config); // Get config first.
		config.max_connection = 8;
		wifi_softap_set_config(&config);

		Serial.printf("Waiting...\n");
		delay(5000);

		Serial.printf("\nmax_connections=%d num_connections=%d\n", config.max_connection, wifi_softap_get_station_num());

		int num_connections = wifi_softap_get_station_num();

		Serial.print("AP+STA Started\n");
	}

	for (int i=0; i<NSOCKETS; i++)
		servers[i].begin();

	pinMode(16, OUTPUT);

	Serial.printf("\n\nServer Ready\n");
}


void ServerLoop()
{
	static uint32_t last = 0;

	if (millis() - statusTime > 5000)
	{
		Serial.printf("connections=%d ", wifi_softap_get_station_num());
		for (int i = 0; i < NSOCKETS; i++)
			Serial.printf("%c ", clients[i].connected() ? 'X' : '-');
		Serial.print("\n");
		statusTime = millis();
	}

	for (int i = 0; i < NSOCKETS; i++)
	{
		if (clients[i].connected())
		{
			digitalWrite(16, LOW);
			if (clients[i].available())
				Serial.printf("%d %6ld: ", i, millis() - last);
			if (clients[i].available())
			{
				while (clients[i].available())
				{
					Serial.print((char)clients[i].read());
				}
				servers[i].begin();
				last = millis();
			}
		}
		if (!clients[i].connected())
		{
			digitalWrite(16, HIGH);
			clients[i] = servers[i].available();
			clients[i].setTimeout(10);
		}
	}
}

#else

void ClientSetup()
{
	boolean success = false;

	while (!success)
	{
		// Disconnect, if connected
		WiFi.disconnect();

		// Set Mode
		if (!(success = WiFi.mode(WiFiMode_t::WIFI_STA)))
			continue;

		// Configure STA
		if (!(success = WiFi.config(ClientIP, APIP, Netmask, APIP)))
			continue;

		WiFi.persistent(true);
		if (!(success = WiFi.setAutoConnect(true)))
			continue;

		// Connect STA
		if (!(success = WiFi.begin(APSSID, APPass)))
			continue;

		Serial.print("\nSTA Connected to AP\n");

		if (!(success = WiFi.setAutoReconnect(true)))
			continue;
	}
	for (int i=0; i<NSOCKETS; i++)
		clients[i].setTimeout(10UL);

	pinMode(16, OUTPUT);

	Serial.printf("\n\nClient Ready\n");
}


void ClientLoop()
{
	static int32_t last[NSOCKETS] = { 0, 0, 0, 0, 0, 0 };
	static uint16_t cnt[NSOCKETS] = { 0, 0, 0, 0, 0, 0 };

	for (int i = 0; i < NSOCKETS; i++)
	{
		if (!clients[i].connected())
		{
			digitalWrite(16, HIGH);
			clients[i].connect(APIP, 8000);
		}
		if (clients[i].connected())
		{
			digitalWrite(16, LOW);
			last[i] = millis();
			clients[i].printf("Hello %u\n", (i * 100) + cnt[i]);
			uint32_t delta = millis() - last[i];
			Serial.printf("%d: Wrote %u %lu\n", i, (i * 100) + cnt[i]++, delta);
			//delay(10);
			clients[i].stop();
			delay(1);
		}
	}
}
#endif


void setup()
{
	Serial.begin(115200);
	//Serial.setDebugOutput(true);
	Serial.print("\n\n");

#if defined(SERVER)
	ServerSetup();
#else
	ClientSetup();
#endif
}



void loop()
{
#if defined(SERVER)
	ServerLoop();
#else
	ClientLoop();
#endif
}

you still do not close the sockets on server side. they close themselves with a delay and the count of sockets is limited what I know.

I think opening more servers on one port is not a good idea. And restarting servers with begin makes it even more chaotic for TCP.