After a lot of testing I am pretty sure that there is problem with TCPServer/Client and WiFi interaction which causes the entire Photon to reset when WiFi connectivity disappears at a point when there isTCP activit - I’ve tried firmware revisions 0.5.3 and 0.6.0 and see the same outcome with both.
I have tried various test programs including a very simple web server (code below based on an example) and a test program in Python to access it I have also set up a WiFi access point for testing that only this one device connects to… If I run the test program under normal conditions it works fine and doesn’t fail any web requests. But if I then disconnect the Ethernet cable to the access point the Photon first loses connection to the particle cloud (cyan flashing) and then in around 1 case in 3 it results in a full reset - sometimes the LED goes to flashing green before going to flashing red and other times straight to flashing red.
I have monitored the WiFi.ready() result and in all cases the result is still true (WiFi is ready) when the reset occurs so there seems to be no way to catch this situation and avoid the call to the TCP code which I believe is triggering the crash.
SYSTEM_MODE(AUTOMATIC);
char ssid[] = "rdint02";
char password[] = "testtesttest";
TCPServer server(80);
void setup() {
Serial.begin(115200); // initialize serial communication
delay(3000);
Serial.printlnf("System version %s", System.version().c_str());
WiFi.on();
WiFi.setCredentials(ssid,password);
WiFi.connect();
while (WiFi.connecting()) {
// print dots while we wait to connect
Serial.print(".");
delay(300);
}
IPAddress localIP = WiFi.localIP();
while (localIP[0] == 0) {
localIP = WiFi.localIP();
Serial.println("waiting for an IP address");
delay(1000);
}
Serial.printf("SSID: %s, IP: ", WiFi.SSID());
Serial.println(WiFi.localIP());
server.begin();
}
int lastWiFiReady = false;
int lastParticleConn = false;
void loop() {
if ((lastWiFiReady != WiFi.ready()) || (lastParticleConn != Particle.connected()))
Serial.printlnf("WiFiReady %d ParticleConn %d", WiFi.ready(), Particle.connected());
lastWiFiReady = WiFi.ready();
lastParticleConn = Particle.connected();
TCPClient client = server.available();
if (client) {
bool currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
/*Serial.write(c);*/
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println();
client.println("<!DOCTYPE HTML><html>Hello World</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
}
}
For reference this is the Python code I’m using to test:
import time
import serial
import threading
import requests
comport = "COM8"
failureCounts = {
"success": 0,
"requestOtherException": 0,
"requestTimeouts": 0,
"requestConnErr": 0,
"serialTimouts": 0,
"serialExceptions": 0,
"ReopenedSerialPorts": 0,
"non200StatusCodes": 0
}
# Read data from serial port and echo
def serialRead(ser):
global serialIsClosing
serExcept = False
while True:
# Handle closing down
if serialIsClosing:
break
# Get a char if there is one
try:
if ser.isOpen():
val = ser.read(1)
if len(val) == 0:
continue
print(val.decode("utf-8"), end="")
else:
ser = serial.Serial(port=comport, baudrate=115200, timeout=1)
if ser.isOpen():
print("Reopened Serial Port")
failureCounts["ReopenedSerialPorts"] += 1
serExcept = False
serTimeoutExcept = False
except serial.SerialTimeoutException:
failureCounts["serialTimouts"] += 1
if not serTimeoutExcept:
print("Serial timeout exception")
serTimeoutExcept = True
serExcept = False
ser.close()
except serial.SerialException:
failureCounts["serialExceptions"] += 1
if not serExcept:
print("Serial exception")
serExcept = True
serTimeoutExcept = False
ser.close()
print("Basic web server stress test")
# Serial connection
serialIsClosing = False
serPort = serial.Serial(port=comport, baudrate=115200, timeout=1)
# Thread for reading from port
thread = threading.Thread(target=serialRead, args=(serPort,))
thread.start()
def showFailCounts():
print(".......", end="")
for key, value in failureCounts.items():
print(" ", key, value, end="")
print()
# Test web using request
for i in range(100000):
try:
r = requests.get('http://192.168.0.129', timeout=10.0)
# print(r.status_code)
if r.status_code != 200:
failureCounts["non200StatusCodes"] += 1
else:
failureCounts["success"] += 1
except requests.exceptions.ConnectTimeout:
failureCounts["requestTimeouts"] += 1
except requests.exceptions.ConnectionError:
failureCounts["requestConnErr"] += 1
except requests.exceptions.RequestException as excp:
failureCounts["requestOtherException"] += 1
print("Other exception", excp)
showFailCounts()
time.sleep(1)