There is no way to know if the connection has closed or not, you can only know if the connection was working at some point in the past.
Your communication protocol as shown is only half duplex (i.e., it's only one way - the Arduino sends to Python but never the other way around) which simplifies things somewhat.
There are a number of strategies you can employ in your case, and any number of them can be combined to create a system as robust (and complex) as you like.
First you would need to make your protocol full duplex. This is, you need your Python program to be able to send information to the Arduino. Since you aren't doing that at the moment the implementation of it can be in any way you like.
One method you can use is called Keepalive. In this system the Python program will periodically send an "I am here" message to the Arduino. The Arduino receives these messages and remembers when the last one was (hint: use the millis()
function). If a message hasn't been received for more than a pre-defined timeout period the link is assumed to be dead.
Another method which is good if you are sending data very regularly from the Arduino to the Python program is to use a system of acknowledgements. In this situation every bit of data sent to the Python program is followed by the Python replying with an "I got that" message. If the Arduino doesn't get that response after sending its data within a certain timeout period (can be much shorter than for a keepalive timeout) then the link is assumed to be dead.
Wrapped around both these methods you can add an "I have arrived" and an "I am leaving" message. These are sent by the Python program when it starts and just before it's about to exit so the Arduino knows when the link should be alive and when it shouldn't. If it gets an "I am leaving" message then it knows that it shouldn't care whether it gets any acknowledgement or keepalive messages, so it doesn't need to think about timeouts. Only after it has received an "I have arrived" message should it start caring.
So for instance your protocol, with all these things implemented, might look like this (>>
= message from Arduino to Python, <<
is message from Python to Arduino):
>> 1
>> 1
>> 1
>> 0
>> 0
>> 1
<< A - I have arrived
>> 1
<< Y - Yes, I got that
>> 1
<< Y - Yes, I got that
>> 0
<< Y - Yes, I got that
<< K - keepalive - we know the link is alive and working.
>> 0
<< Y - Yes, I got that
>> 1
<< Y - Yes, I got that
(Program crashes now.)
>> 1
*no Y received - assume connection is closed*
>> 1
... etc ...
In this instance the 0 and 1 are what you are sending at the moment. A is an "I have Arrived" message, Y is a "Yes I got that" message, K is a "Keepalive" message, and if the program had exited cleanly an L would have been an "I am Leaving" message.
Keeping the messages simple (single characters) means it's really easy for the Arduino to parse those messages and work out what is going on.
There is a certain amount of balancing act to be performed when implementing your system between how often you are sending data and how long you are willing to wait for a valid response or message to flag the connection closed.
Expanding your existing example program to incorporate these messages might look like:
unsigned long lastKA = 0;
unsigned long lastSent = 0;
boolean isConnected = false;
void setup() {
Serial.begin(9600);
pinMode(7, INPUT_PULLUP);
}
void loop() {
// Check the timeouts if we are connected
if (isConnected) {
// First check to see if the Keepalive timeout has passed
if (millis() - laskKA > 5000) { // 5 second keepalive timeout)
isConnected = false;
}
// If we have sent data...
if (lastSent > 0) {
// ... check that we got a response in time
if (millis() - lastSent > 100) { // 100ms Y timeout
isConnected = false;
}
}
}
// If we are connected then send some data
if (isConnected) {
int val = digitalRead(7);
if (val == HIGH) {
Serial.print("1");
} else {
Serial.print("0");
}
Serial.println(""); // new line
// and set the timer for the response
lastSent = millis();
}
// Now process incoming messages.
if (Serial.available()) {
int c = Serial.read();
// If the message is an A then turn on the connection and
// treat it as a Keepalive as well.
if (c == 'A') { // Arrived
isConnected = true;
lastKA = millis();
// If it's a Leaving message then turn off the connection.
} else if (c == 'L') { // Leaving
isConnected = false;
// If it's a Keepalive then reset the timer.
} else if (c == 'K') { // Keepalive
lastKA = millis();
// A Y acknowledgement will disable the sent timer.
} else if (c == 'Y') { // Yes
lastSent = 0; // Cancel the sent timer
}
}
}