Problems with TCPClient

Hello everyone !

I’m struggeling to get this piece of code to work as expected with my particle photon …
So my setup is: one particle p1 and one particle photon, the p1 is supposed to play LED animations and i can use regular GET/PUT commands to control it using Postman (https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop)
To play the next animation, i can send a PUT command with {“play”:“next”} to http://p1_ip_address/animation
With the help of particle photon, i wanted to cycle through all animations using {“play”:“next”} every 5 seconds.
The problem is: the animation is not changed every 5 seconds, it takes most of the time more than 39 seconds for the animation to change …
Using Postman i’m able to instantly change the animation

TCPClient client;

IPAddress p1_server(192,168,2,158);
const int serverPort = 80;
const unsigned long MAX_SEND_FREQUENCY_MS = 5000;

unsigned long lastSent = 0;
String command = "";

void setup() {
	command = "{ \"play\":\"next\" }";
}

void loop() {
	if(millis() - lastSent > MAX_SEND_FREQUENCY_MS){
		lastSent = millis();

		if(client.connect(p1_server, serverPort)){
			client.println("PUT /animation HTTP/1.1");
			client.printlnf("Host: %c", p1_server);
			client.println("Content-Type: application/json");
			client.println();
			client.println(command);
			client.flush();
			client.stop();
		}
	}
}

Why are you using %c here? This only stands for a single character.

You could try

client.printlnf("Host: %s", (const char*)p1_server.toString());
1 Like

I’d add some debugging log code so you can see where the delay is.

Turn it on at the top:

SerialLogHandler logHandler;

Insert calls like:

Log.info("before connect");
Log.info("after connect");
Log.info("after close");

and see where the delay is. Log.info works like Serial.print, except it timestamps the entries which is kind of handy in this case.

1 Like

Hello again,
I tried your solution ScruffR :

It didn't help :confused:

and rickkas7, here's a sample of the terminal output:

0000055666 [app] INFO: before connect
0000055671 [app] INFO: after connect
0000055672 [app] INFO: after stop

0000060667 [app] INFO: before connect
0000060683 [app] INFO: after connect
0000060684 [app] INFO: after stop

And before you suggest it, i already tried the httpclient lib and had the same result :confused:

The timestamps on the left are in milliseconds, so it’s making the connections every 5 seconds.

My first guess is that since you’re not reading the HTTP response code from the server, the server is timing out before acting on the request. The flush call does not not clear the received data buffer.

My second guess is that the server is not sensing the end of the request, and also is waiting to time out. Adding a Content-Length may help.

2 Likes

Hi @Orbinom

I think the “Content-Length” header is required here.

As @rickkas7 points out you should wait and read the response from the server before calling stop. You can gain more insight into what is going wrong from that sometimes as well.

@rickkas7, @bko
The Content-Length header didn’t fix anything …

I tried to read the response using

    if (client.available()){
    response = client.read();
    Serial.print(response);
    }

with char response; outside setup and loop
but nothing is being displayed

At this point i’m open to any suggestions and modifications to the whole loop()
Here’s how i proceeded:

    if (client.available()){
    response = client.read();
    Serial.print(response);
    }
    if (!client.connected())
    {
        Serial.println();
        Serial.println("disconnecting.");
        client.stop();
    }
    	if(millis() - lastSent > MAX_SEND_FREQUENCY_MS){
    		lastSent = millis();
        Serial.println("before connect");
    		if(client.connect(p1_server, serverPort)){
    		    Serial.println("after connect");
    			client.println("PUT /animation HTTP/1.1");
    			client.printlnf("Host: %s", (const char*)p1_server.toString());
    			client.printlnf("Content-Length: %d", command.length());
    			client.println("Content-Type: application/json");
    			client.println();
    			client.println(command);
    		}
    	}
    	delay(50);

If you want to receive the full response a single character char response; won't do.
You will need an array to read the whole response into.

Since you want to have a proper HTTP request/response "conversation" you could give HTTPClient a shot.

I don't really want a conversation as much as i want to PUT {“play”:“next”} every 5 seconds ..
I already gave HTTPClient a shot and it wasn't really reliable in my case: it randomly returns -1 and an empty response (the fail rate is at 80%)

Problem solved !
Avoid using client.println(); !!! use client.print() once instead with all headers and content concatenated into one String (request).
So it looks sthg like:

Serial.println("before connect");
if(client.connect(p1_server, serverPort)){
    Serial.println("after connect");
    client.print(request);
    while(!bytes && client.connected()) {
        bytes = client.available();
        delay(100);
    }
    while (client.available()){
        Serial.print(client.read());
    }
    if (!client.connected()){
        Serial.println();
        Serial.println("disconnecting.");
        client.stop();
    }
}

client.println() sends the line multiple times (sometimes incomplete) and that causes the problem.