Yet another TCPClient example (but this one works!)

After reading about all the problems people have with TCPClient, all the conflicting and misleading information based on old bugs, etc., I finally got my own code working.

So maybe I can save you many hours of battling with using TCPClient in the real world (latency, downtime, slow servers, etc.) with this thorough example:

5 Likes

It is examples such as these which I recently requested elsewhere. Thank you. I am pleased to see that a lot of what amounts to superstitious programming (throwing in random waits and calling functions which ought not be necessary, e.g. checking that the WiFi is up as well as checking Spark.connected) is not featured in your example. You use the functions provided(^) as intended and all seems to work. This is great and should be welcomed by all as all ought to be in favour of demystifying the programming of the Spark Core.

(^) However the function Spark_WLAN_Loop() is not documented and its use is deprecated. And @zach has written we should just use the intended function, viz. Spark.process(). Some here insist on using the function you use. I think it has the appearance and effect of driving a wedge between an inner crowd and ordinary punters - things like this have the appearance of only allowing things to work if you ask for help from an expert in the forum first. How else would you know of this apparently necessary function which is not actually intended for application programming. Or, if it is necessary, then it must be documented. IM(not so H)O

1 Like

SPARK_WLAN_Loop calls Spark.process() but is, as @psb777 mentioned, not documented. Spark.process() does not do everything that WLAN_Loop does. Which one is the “correct one” to call is a good question. I’ve asked such a question on the firmware repo issue relevant to this issue. It looks like @mdma has a plan to clear all this up in a new firmware update so that’s awesome.

Until I am told otherwise I’d stick with SPARK_WLAN_Loop since it seems to also handle disconnects from the cloud unlike Spark.process(). Hopefully with @mdma’s changes we can put this issue to rest.

Hi @drcheap

I like this – it is a great example. It follows the general form that I have been advocating for and showing how to do for quite some time now: a loop with timeout to wait for return data, followed by a loop to read (and possibly discard) data, also with a timeout.

People get into to trouble when they don’t pickup all the data sent from the server. There are buffers in the TI part and another buffer in the TCPClient object and just flushing the client data is not sufficient.

Please, @harrisonhjones, can you explain what you mean by “handle disconnects from the cloud”? Do you mean that SPARK_WLAN_Loop() does not block if the Cloud unexpectedly evaporates, and that this provides a way to have Cloud-connected MANUAL mode code which does not block should the Cloud connection disappear unexpectedly? Because that is what I would hope you might mean, but my readings of currently proposed developments at Github make me doubt that that is quite what you mean.

I’m a bit confused by this thread.

There are Spark Cloud problems and TCPClient problems. The Spark Cloud problems can occur w/o any TCPClient activity. And you can have problems with TCPClient w/o any Spark Cloud active.

SO … IMO the water is getting very muddy.

This example by @drcheap is an example that shows how to introduce delays and tests to make sure you data gets sent and you receive and process all of the incoming data. I’m a bit concerned that it uses client.print statements as we all know character operations at the TCPClient layer is not good practice. But, it’s a good starting point and much better example than what’s in the documentation.

I’m glad he posted it and it may help others get started with TCPClient examples. But it does not address the fundamental low level problems we are experiencing with the CC3000 chip and it’s implementation on the Spark.

This is what Spark.process() does:

void SparkClass::process(void)
{
#ifdef SPARK_WLAN_ENABLE
  if (SPARK_CLOUD_SOCKETED && !Spark_Communication_Loop())
  {
    SPARK_FLASH_UPDATE = 0;
    SPARK_CLOUD_CONNECTED = 0;
    SPARK_CLOUD_SOCKETED = 0;
  }
#endif
}

While SPARK_WLAN_Loop (which is too long to post here) has the following line

Spark_Handshake()

Which appears to attempt to reconnect to the cloud if necessary.

I’m not sure if WLAN_Loop will block if the cloud disappears.

As @mtnscott mentioned, there is no need to talk to the spark cloud with TCPClient if you don’t want to.

Tbh, given that SPARK_WLAN_Loop has the following check:

if(SPARK_FLASH_UPDATE || System.mode() != MANUAL)
{
  Spark.process();
} 

In manual mode it looks like Spark.process() isn’t even called.

All in all, I’m not sure exactly what needs to be called to keep up the CC3000-to-Spark connection. I think the answer is WLAN_Loop but I will check later (or someone can correct me)

2 Likes

@mtnscott, the point is that the Cloud runs over TCP and there is no TCP without WiFi. If the WiFi is down there is no TCP and the Cloud cannot be up. Similarly, Cloud.connected() implies TCP is working implies WiFi.ready() implies WiFi.connected(). OK, we all know that.

It might be that there are issues with the TCP libraries overlaying the Berkeley sockets calls doing the work underneath. [That the Cloud is using TCP doesn’t mean its using the TCP libraries but could be using teh sockets interface directly] But what this sample app does show is that it is possible to get a TCP connection working without all the strange random delays and redundant calls I see in some code posted here in the forum.

@bko seems to agree - this is an example of how it should be done.

But who says “character operations at the TCP layer is not good practice”? One is able (or ought to be able if the [so-called?] TCP is compliant with the protocol spec) to read and write a TCP socket byte by byte or buffer by buffer, and it is perfectly permissable to write (or read) it one end character by character and to read (or write it) buffer by buffer the other end. TCP is (1) a connected protocol and (2) it guarantees order sent is order arrived and (3) it does not respect packet boundaries. Yes, there are packets underneath, but the boundaries are not detectable in the [application level] TCP protocol.

Also whereas there have been problem with the TI chip it now becomes evident that some things which were repeatedly held to be impossible because of TI CC3000 issues are now known to be reasonably readily implementable. For instance there are discussions in the last few days on github about work seemingly now in progress to allow for MANUAL mode user code not to be blocked when the Cloud unexpectedly disappears. That will make several here - I’m guessing you included - very happy.