How to return to loop from task started in loop on ESP8266?

I have a client command interpreter for incoming data via a socket connection.
It is one of several tasks that get checked repeatedly inside loop().
One of the commands is a bit time consuming so if this is sent and decoded in the handler called in loop, the handler does not return to loop until the task is complete. This can take upwards of 10 seconds.
My problem is that the response to the command is sent out via another task called in loop() and the delay until the response is sent back to the client can make the client time out.
The task is sending a response right away and then executes the requested operation. But the problem is that the response sending is really a function that stuffs a buffer with the response string.
The buffer is checked in a later task in loop() and the content is sent if the buffer is not empty.
Problem is that I don't get here until the commanded operation has completed including the time consuming part (which consists of a delay to stabilize the electronics).
I use delay() inside the command execution, which at least makes the watchdog happy. But my application is sluggish...

Is there any way to yield from a task execution and make the loop() function run to its end and then return to the command interpreter at the point of the yield?

I think in order to help with this we'd need to see your code.

DrAzzy:
I think in order to help with this we’d need to see your code.

OK, I have tried to extract the important parts of the code for sharing, see below.
Note that the device also handles other tasks so I am looking for a way to let the loop continue while waiting for a delay to time out inside the processing and then return to the next line after the delay.
It is similar to how one would use an RTOS where the delay is part of the RTOS and signals that a task is put on hold during the wait and resumed after the delay has passed.
HandleDHTsensors() is the current problem because it switches on power to the DHT sensors and then waits for 2 seconds using function delay(2000) before proceeding to read the sensors. Sensor reading also takes some time (in the DHT library). Then DHT power is removed (no delay here) and the data are sent to the web server with a short delay following each sensor’s transmission.
So the delays inside HandleDHTsensors() are depending on the sensor count and can be upwards of 4-10 seconds.

void ReplyCmd(char* buf, int len)  //Called from RespondToCommand()
{
    buf[len++]=ETX;	//Need to add final ETX
    buf[len]=0;
    //add the report string to the cnftxbuf global buffer
    strcat(cnftxbuf, buf); //Content processed inside HandleConfigServer()
    SerialDebug.println(buf);
}

void RespondToCommand(char* cmd)  //Called from ProcessConfigCall()
{
	byte b, m;
	byte iptmp[4];
	int len;
	long bl, n;
	unsigned int i, tdht;
	int p, err=0;
	char msg[80], tmp[10];
	char command;
	uint8_t mac[6];
	char mac_char[18];
	String TimeMsg = "";

	memset(&mac_char, 0, sizeof(mac_char));

	command = *cmd++;

	switch (command)
	{
		//Lots of command cases not shown..
		
		case 'e':   //Execute a command on the ESP8266
				//First byte is a command code
				//Following bytes are command specific paqrameter(s)
				//Return result message
			switch (*cmd)
			{
				case '1': //Check WiFi signal level
					if (!GetWifiSignalLevel(tmp))
					{
						strcpy(msg,"WiFi_RSSI=");
						strcat(msg, tmp);
						strcat(msg," dB");
					}
					else strcpy(msg,"WiFi_RSSI=ERR"); //Wrong state of WiFi
					ReplyCmd(msg, strlen(msg));
					break;

				case '2': //Make DHT measurement
					strcpy(msg, "DHT measurement started");
					ReplyCmd(msg, strlen(msg));
					HandleDHTsensors(millis());  //Takes some time to process!
					break;

				case '3': //Return current NTP time
					#ifdef USE_NTP_TIME

						NTP.getTime();
						TimeMsg = NTP.getTimeDateString();
						strcat(msg, TimeMsg.c_str());
					#else
						strcpy(msg, "NTP time undefined");
					#endif
					ReplyCmd(msg, strlen(msg));
					break;

				case '4':	//Toggle DHT 5V power for testing
					bool currstate, newstate;
					currstate = bool(digitalRead(DHT_POWERPIN));
					newstate = !currstate;
					DHT_Power_Ctrl(newstate);
					strcpy(msg, "DHT power toggled: ");
				    utoa(newstate, tmp, 10);  //convert value to a string
				    strcat(msg, tmp);
					ReplyCmd(msg, strlen(msg));
					break;

				default:
					break;
			}
			break;
		default:
			strcpy(msg, "ERR unknown command");
		    ReplyCmd(msg, strlen(msg));
			err=2;  //To block the reply below if cmd not found


	}
	if ((err != 2)&&(command < 0x61)) //Write commands use capital letter as cmd byte, reply is OK or ERR
	{
		if (err==1)
			strcpy(msg, "ERR");
		else
			strcpy(msg, "OK");

		ReplyCmd(msg, strlen(msg));
	}
}


void ProcessConfigCall(char *buf, int len)  //Called from HandleConfigServer()
{
	int res;
	char msg[] = "ERROR-Buffer Overrun";
	char cmd[BUFSIZE];

	res = AddDataToCmdbuf(buf, len);
	switch (res)
	{
	case -1:
		ReplyCmd(msg, sizeof(msg));
		ResetBuffer();
		return;
	case 0:
		return;
	case 1:	//Full command detected so extract it and process request
		if (PopCommand(cmd)== 0)
		{
			SerialDebug.print("Rx: "); SerialDebug.println(cmd);
			RespondToCommand(cmd);
		}
	}
}


void HandleConfigServer()  //Called from loop()
{
	uint8_t i;
	char cnfBuf[100]; //Buffer for config server
	unsigned int cnfBytesAvail, cnfBytesIn, cnfBytesOut;
	
	//------- check if there are any new Config clients -------------
	if (tcpConfigSrv->hasClient())
	{
		for (i = 0; i < MAX_CONF_CLIENTS; i++)
		{
			//find free/disconnected spot
			if (!tcpConfigClients[i] || !tcpConfigClients[i].connected())
			{
				if (tcpConfigClients[i]) tcpConfigClients[i].stop();
				tcpConfigClients[i] = tcpConfigSrv->available();
				SerialDebug.print("New Config client: "); SerialDebug.println(i);
				continue;
			}
			else
				SerialDebug.println("New Config client rejected: No free socket");
		}
		//no free/disconnected spot so reject
		WiFiClient tcpConfigClient = tcpConfigSrv->available();
		tcpConfigClient.stop();
	}

	  //--------------- check client for commands ---------------------------

	// Check for Config data ------
	for (i = 0; i < MAX_CONF_CLIENTS; i++)
	{
		if (tcpConfigClients[i] && tcpConfigClients[i].connected())
		{
			//get data from the config client and push it to the Configuration function
			while ((cnfBytesAvail = tcpConfigClients[i].available()) > 0)
			{
				cnfBytesIn = tcpConfigClients[i].readBytes(cnfBuf, min(sizeof(cnfBuf), cnfBytesAvail));
				if (cnfBytesIn > 0)
				{
					ProcessConfigCall(cnfBuf, cnfBytesIn);
					delay(0);
				}
			}
		}
	}

	//----------- Now check if there are config data responses to be returned -----------
	//Send buffered data back to config clients
	if ((cnfBytesOut = strlen(cnftxbuf)) > 0)
	{
		for (i = 0; i < MAX_CONF_CLIENTS; i++)
		{
			if (tcpConfigClients[i] && tcpConfigClients[i].connected())
			{
				//Now check if there are config data to be returned
				tcpConfigClients[i].write((uint8_t*)cnftxbuf, cnfBytesOut);
			}
			delay(0);
		}
		cnftxbuf[0] = 0; //Reset buffer after transmit
	}
}



void loop()
{
	//Check if a new DHT reading is requested:
	unsigned long tickcount = millis();
	if (first_dht_done == 0)
	{
		first_dht_done = 1;
		HandleDHTsensors(tickcount);
	}
	else
	if ((dht_interval > 0) && ((unsigned long)(tickcount - lasttickcount) >= dht_interval)) //Regular reading scheduled
	{
		HandleDHTsensors(tickcount);  //This task takes some time to complete, longer for more sensors
	}

	HandleConfigServer();  //Interactive configuration, receive and execute commands

	HandleWebConfigServer();

	//Check for firmware update request
	httpServer.handleClient();

	//Send startup report, only once
	if (StartMessageSent == false)
	{
		if (WiFi.status() == WL_CONNECTED)
		{
			SendStartReport();
			StartMessageSent = true;
		}
	}

	//Handle software reset
	if (Resetflag)
	{
	if (resetwait == 0) //First time here
		resetwait = millis();  //Get start time
	if((millis() - resetwait) > 1000) //Wait 1000 ms until reset
		ESP.restart(); //then reset
	}
}

Just like any other problematic, blocking task, you have to rewrite 'HandleDHTsensors()' (which you didn't post) so that it breaks the job into smaller chunks that can be handled in a non-blocking manner.

It sounds like the majority of the time is wasted just waiting. So, why block the rest of your code during that period? Just check if the waiting period is over yet. If not, return immediately with an indication that it's not done. If the DHT library takes too long to return a reading, rewrite it in non-blocking fashion also.

So, 'HandleDHTsensors()' will be called hundreds (maybe thousands) of times before the task is done. Finally, when finished, it will return a flag indicating same. At that time, you can use the results.

Then I have to build the handler as a state machine...
Had hoped for a simpler way.
Next year. After New Year celebration.

BosseB:
Then I have to build the handler as a state machine...

Yes, that's it - in a lot fewer words than I used :slight_smile:

Next year. After New Year celebration.

That can be your New Year's resolution -- don't write any more blocking code.