[SOLVED] TCPCLIENT intranet connection fails if no cloud connection

Has anyone else found that you need a cloud connection (breathing cyan) when using TCPCLIENT to connect to a service on the local LAN?

It seems that the lack of cloud connection causes the network stack to be reset which then stops TCPCLIENT from working at all. It is only resolved by resetting the Photon.

Am using firmware 0.5.3.rc1

I’m not seeing that problem. I ran the test program below on a Photon running 0.5.3rc1 and it worked normally. The important thing is that you need to use manual (or probably semi-automatic) system mode and start WiFi, but not the Particle connection.

#include "Particle.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);

const unsigned long SEND_PERIOD_MS = 20000;

enum WifiState { WIFI_STATE_NOT_CONNECTED, WIFI_STATE_CONNECTING, WIFI_STATE_CONNECTED, WIFI_STATE_RUNNING };

IPAddress serverAddr(192,168,2,4);
const int serverPort = 8080;

WifiState wifiState = WIFI_STATE_NOT_CONNECTED;
unsigned long lastSend = 0;
TCPClient client;
int seq = 0;

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

	WiFi.on();
}

void loop() {
	switch(wifiState) {
	case WIFI_STATE_NOT_CONNECTED:
		Serial.println("connecting");
		WiFi.connect();
		wifiState = WIFI_STATE_CONNECTING;
		break;

	case WIFI_STATE_CONNECTING:
		if (WiFi.ready()) {
			wifiState = WIFI_STATE_CONNECTED;
		}
		// The WiFi.connect() call never times out, it will keep trying forever so there's
		// no need to call WiFi.connect() again here.
		break;

	case WIFI_STATE_CONNECTED:
		// Do any one-time initialization here like calling udp.begin() or tcpServer.begin()
		Serial.println("connected");
		wifiState = WIFI_STATE_RUNNING;
		break;

	case WIFI_STATE_RUNNING:
		if (!WiFi.ready()) {
			Serial.println("disconnected during connected state");
			wifiState = WIFI_STATE_CONNECTING;

			// No need to call WiFi.connect() again, it will keep retrying forever
			break;
		}

		// Running with WiFi enabled here
		if (millis() - lastSend >=  SEND_PERIOD_MS) {
			lastSend = millis();

			if (client.connect(serverAddr, serverPort)) {
				Serial.printlnf("sending seq=%d", seq);
				client.printlnf("%d\n", seq++);
				client.stop();
			}
			else {
				Serial.println("connection failed");
			}
		}
		break;


	}
}

4 Likes

Thanks @rickkas7!

Am using SYSTEM_MODE(SEMI_AUTOMATIC).

I notice that in your code that you are not calling Particle.process(), so (I think) the Photon will not be trying to connect with the cloud (because you are in MANUAL mode) whereas in my situation, it is trying to connect.

For my use, am wanting to continue trying to connect to the cloud.

Unfortunately I have not got time today to modify your code to test out this situation, but will do so next week and report back.

I think it's not the presence or absence of a cloud connection that interferes with your TCPClient, but rather your (possibly frequent) reconnection attempts.

I don’t think Particle.process() is the cause because at least with system thread enabled, which you should set, you can call it even when not connected to the cloud and nothing bad happens.

In any case, the situation is different than I interpreted in your original post, and it will take a little longer to set up a test for that. Basically, you have a situation where you always want to connect to a local server, but you may or may not have a connection to the cloud - the cloud Internet link may or may not be up. But you still want to connect to the cloud when it’s available. Right?

@rickkas7, your interpretation of what is wanted is correct.

The network configuration is such that internet connection is mostly not there (because of the client’s security concerns).

I would like to modify your test code to first try and reproduce the fault and then move on from there.

@Scruffr, interesting theory! I will also test this using @rickkas7’s test scaffold.

Good news at least is that our work around was to tell the client “you must allow us to have an internet connection for the application to work”.

Hopefully back next week with some reproducible results.

Cheers - @umd

1 Like

With or without SYSTEM_THREAD(ENABLED) Particle.process() won't cause any trouble.
But other commands (e.g. at least Particle.publish() used to) should be wrapped in an
if (Particle.connected()) { ... }

This example should be closer to what you want:

#include "Particle.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);

const unsigned long SEND_PERIOD_MS = 15000;

enum WifiState { WIFI_STATE_NOT_CONNECTED, WIFI_STATE_CONNECTING, WIFI_STATE_CONNECTED, WIFI_STATE_RUNNING };

IPAddress serverAddr(192,168,2,186);
const int serverPort = 8081;

WifiState wifiState = WIFI_STATE_NOT_CONNECTED;
unsigned long lastSend = 0;
TCPClient client;
int seq = 0;

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

	WiFi.on();
}

void loop() {
	switch(wifiState) {
	case WIFI_STATE_NOT_CONNECTED:
		Serial.println("connecting");
		WiFi.connect();
		wifiState = WIFI_STATE_CONNECTING;
		break;

	case WIFI_STATE_CONNECTING:
		if (WiFi.ready()) {
			wifiState = WIFI_STATE_CONNECTED;
		}
		// The WiFi.connect() call never times out, it will keep trying forever so there's
		// no need to call WiFi.connect() again here.
		break;

	case WIFI_STATE_CONNECTED:
		// Do any one-time initialization here like calling udp.begin() or tcpServer.begin()
		Serial.println("connected");

		// Also connect to the Particle cloud
		Particle.connect();

		wifiState = WIFI_STATE_RUNNING;
		break;

	case WIFI_STATE_RUNNING:
		if (!WiFi.ready()) {
			Serial.println("Wifi disconnected during connected state");
			wifiState = WIFI_STATE_CONNECTING;

			// No need to call WiFi.connect() again, it will keep retrying forever
			break;
		}

		// Running with WiFi enabled here
		if (millis() - lastSend >=  SEND_PERIOD_MS) {
			lastSend = millis();

			Serial.println("about to connect");
			if (client.connect(serverAddr, serverPort)) {
				Serial.printlnf("sending seq=%d", seq);
				client.printlnf("%d\n", seq++);
				client.stop();
			}
			else {
				Serial.println("connection failed");
			}

			if (Particle.connected()) {
				Serial.println("publishing event");
				Particle.publish("test4", "", PRIVATE);
			}
			else {
				Serial.println("not connected to the cloud, skipping publishing event");
			}
		}
		break;

	}

	// System thread enable mode is used so this is not necessary to keep the cloud
	// connection is alive, but it is necessary to handle the system events, including
	// the button, so it's here, regardless of the connection state.
	Particle.process();
}

This worked well for me. It starts up and if there’s a connection to the Internet everything works normally. If you disconnect the Internet the Photon blinks cyan, every few minutes it blinks green for a second, but mostly it just blinks cyan. When you reconnect the Internet it goes back to breathing cyan. Meanwhile, it will continue to make connections to a local server the whole time.

This is not necessarily the simplest example of something that works, but it does work.

Sample output:

sending seq=4
publishing event
about to connect
sending seq=5
publishing event
about to connect
sending seq=6
not connected to the cloud, skipping publishing event
about to connect
sending seq=7
not connected to the cloud, skipping publishing event
Wifi disconnected during connected state
connected
about to connect
sending seq=8
not connected to the cloud, skipping publishing event
about to connect
sending seq=9
publishing event
about to connect
sending seq=10
publishing event
3 Likes

@rickkas7, I took your code and simply added Serial.print("."); to the main loop so that I could test the following scenarios by turning off and on Mobile Network Sharing:Portable Wi-Fi hotspot and Mobile Data on my mobile phone:

a) No WiFi, no Data = “…” <== GOOD
b) WiFi, no Data = "… " & “123, 124” etc on netcat listening session <== GOOD
c) WiFi, Data = "… " & “123, 124” etc on netcat listening session <== expected

So, this looks like a robust solution! Thanks for this.

Hopefully in the next day or so I will be able to get around to folding your code into mine. I will report back on the results of this.

(thanks to @Scruffr too).

3 Likes

@rickkas7, folded your code into my main loop and it works like a charm.

Happy to report - case closed!

2 Likes

Four years later and this problem is still with us. However, I put this code in my Sparkfun-derived weather station and I can confirm that this fix does indeed work with firmware 1.5.2. The Photon now automatically reconnects to the web instead of just blinking cyan forever. So a super big thank you to @rickkas7. This had been vexing me for months now.

@Michele I am using the P1 on the 1.5.2 OS, and I have experienced this on the 1.5.2 and 1.0… I was wondering if you were able to recreate the issue successfully. I am having trouble recreating it as it is happening to our customers randomly.

@rickkas7

I am getting a case where my customers are getting their controllers stuck as they have been running for a long time (3-4 weeks). From the device vitals I have noticed that cloud connection is only establishing for 1-10 ms.

@Div_IoT have you implemented the @rickkas7 solution? It is not obvious from your post if you have done that or not as you are asking about “recreating the issue”.

@UMD
Hey thanks for reaching out, I have implemented it on my dev board however not launched it via OTA as I want to make sure that it is fixing the issue im trying to solve.

The particle support ticketing team had pointed me to this thread as they think this issue is similar/same to the one I am facing.

However revisiting the article again it seems like it is a different issue. Issue I am facing is seems to do with the device not responding and not automatically reseting. (I am unable to recreate this issue as its only happened to customers).

I will create another thread.

@Div_Iot, perhaps your issue is one and the same as this issue:

1 Like