I have a problem with setting up a continuously running python written socket server on a RasPi (1st gen) running the latest Raspbian build. What I wanted to do is to set up the RasPi as a data server for remote temp. sensors which uses ESP32 with WiFi for comms. The problem I'm having is with my server freezes which happen on an unregular basis. What the server does (or at least should be doing) is, it accepts a remote connection, gets data, slices it into chunks and saves data to a file. The script starts of running great, with everything running as expected, but over time it freezes and for the love of the Coding Gods I have no idea why. Maybe someone here could help me?
Server code below:
import socket
import datetime
PARAMS=['T','P','A','H','B','C']
sensors={'id':0}
for param in PARAMS:
sensors[param]={'value':0.0,'index_start':0,'index_end':0}
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('192.168.0.10', 7543 ))
s.listen(1)
while True:
print("****Waiting for connection")
conn, addr = s.accept()
#create index for data slicing
contents=[]
with conn:
print('***Incoming connection from: ', addr)
while conn: # change here!
print("****What do you have for me? ")
content = conn.recv(1024)
print("***Received: ")
print(content)
contents.append(content)
print("****Data put into the pool")
if not content:
print("****No more data")
break
#conn.sendall(data)
print("***Data passed for processing: ")
print(contents)
now=datetime.datetime.now()
timestamp=now.strftime("%Y-%m-%d %H:%M:%S")
temp_list=[]
for c in contents:
clean_c=str(c).lstrip('b\'').rstrip('\'')
temp_list.append(clean_c)
text=''.join(temp_list)
print(text)
for i,param in enumerate(PARAMS):
sensors[param]['index_start']=text.index(param)+2
if param != 'T':
prev_param=PARAMS[i-1]
sensors[prev_param]['index_end']=text.index(param)
if param == 'C':
sensors[param]['index_end']=len(text)-1
sensors['id']=text[:sensors['T']['index_start']-2]
for param in PARAMS:
sensors[param]['value']=text[sensors[param]['index_start']:sensors[param]['index_end']]
if param == 'C':
sensors[param]['value']=text[sensors[param]['index_start']:]
value_tuple=(timestamp,sensors['id'],)
for param in PARAMS:
value_tuple+=(sensors[param]['value'].replace('.',','),)
text='\t'.join(value_tuple)
with open('/home/pi/Desktop/sensors.csv','a+') as file:
file.write(text+'\n')
print("****Closing connection")
print(timestamp)
#conn.close()
The freeze always happens on the
content = conn.recv(1024)
I know that this function locks my program, but I don't know why it even goes there if there's no data? And what to do to make it .... well .. not do that? I close the connection on the client side and from what I know the with function should respond to socet closure, right?
Any help would be greatly appreciated.
EDIT1:
So I've been asked about stack errors but no. After I Ctr+C I get a standard response, ONLY with the content = conn.recv(1024) reference:
Traceback (most recent call last):
File "/home/pi/My_Python/Server2.py", line 27, in <module>
conn, addr = s.accept()
File "/usr/lib/python3.7/socket.py", line 212, in accept
fd, addr = self._accept()
KeyboardInterrupt: Execution interrupted
As for the client code:
#define ver 0.21
#define date "2020.09.03"
#include <displayVersion.h>
displayVersion caseVersion;
#define DEEP_SLEEP_TIME 60
#define numberOfMeasurements 10
#include <WiFi.h>
#define ONBOARD_LED 2
int sensorID = 1001;
bool debug=true;
float temperature,pressure,altitude,humidity;
float AVGtemperature,AVGpressure,AVGaltitude,AVGhumidity;
int batteryLevel;
int AVGbatteryLevel;
bool batteryCharging;
const char* ssid = "xxxx";
const char* password = "xxxx";
const uint16_t port = 7543;
const char * host = "192.168.0.10";
bool wifiConnectionStatus;
void setup()
{
// setup onboard IOs
Serial.begin(115200);
caseVersion.display(ver, date, 1000);
pinMode(ONBOARD_LED,OUTPUT);
pinMode(BATTERY_LEVEL_PIN,INPUT);
pinMode(BATTERY_CHARGING_PIN,INPUT);
// setup WiFi
bool status;
Serial.println("Starting operation...");
WiFi.begin(ssid, password);
Serial.println("Connecting to WiFi...");
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println("");
Serial.print("WiFi connected with IP: ");
Serial.println(WiFi.localIP());
}
void loop()
{
// checks i f its connected to WiFi
if (WiFi.status()==3)
{
digitalWrite(ONBOARD_LED,HIGH);
wifiConnectionStatus=true;
}
else
{
digitalWrite(ONBOARD_LED,LOW);
wifiConnectionStatus=false;
}
// gets sensor data a set number of times
Serial.println ("Starting measurements...");
for (int i=0;i<numberOfMeasurements; i++)
{
temperature= 4;
AVGtemperature=AVGtemperature+temperature;
pressure=5;
AVGpressure=AVGpressure+pressure;
altitude=6;
AVGaltitude=AVGaltitude+altitude;
humidity= 7;
AVGhumidity=AVGhumidity+humidity;
// gets battery data
batteryLevel = analogRead(BATTERY_LEVEL_PIN);
AVGbatteryLevel=AVGbatteryLevel+batteryLevel;
Serial.printf("Measurement No: %d\n", i+1);
delay(300); // to be uncommented later on
}
AVGtemperature=AVGtemperature/numberOfMeasurements;
AVGpressure=AVGpressure/numberOfMeasurements;
AVGaltitude=AVGaltitude/numberOfMeasurements;
AVGhumidity=AVGhumidity/numberOfMeasurements;
AVGbatteryLevel=AVGbatteryLevel/numberOfMeasurements;
int temp = analogRead(BATTERY_CHARGING_PIN);
if (temp > 1000) batteryCharging = 1;
else batteryCharging = 0;
// while in debug
if (debug)
{
Serial.print("**Sensors** ");
Serial.printf("Temperature: %f ", AVGtemperature);
Serial.printf("Pressure: %f ", AVGpressure);
Serial.printf("Altitude: %f ", AVGaltitude);
Serial.printf("Humidity: %f ", AVGhumidity);
Serial.print("**Status** ");
Serial.printf("Battery level: %d % ", AVGbatteryLevel);
Serial.printf("Battery charging status: %d \n", batteryCharging);
Serial.printf("Data to be sent: \n");
Serial.printf("\n%dT:%2fP:%2fA:2%fH:2%fB:%dC:%d",sensorID,AVGtemperature,AVGpressure,AVGaltitude,AVGhumidity,AVGbatteryLevel,batteryCharging);
}
// connects to a sever
if (wifiConnectionStatus)
{
WiFiClient client;
if (!client.connect(host, port))
{
if (debug) Serial.println("Connection to host failed");
//delay(500);
//return; // removed to save energy
goToDeepSleep();
}
if (debug) Serial.println("Connected to server successful!");
// sends out data
client.printf("%dT:%2fP:%2fA:%2fH:%2fB:%dC:%d",sensorID,AVGtemperature,AVGpressure,AVGaltitude,AVGhumidity,AVGbatteryLevel,batteryCharging);
// dissconnects
if (debug)
{
Serial.println("Disconnecting...");
}
client.stop();
if (debug)
{
Serial.println("DONE...");
}
delay(5);
}
// goes to sleep
goToDeepSleep();
//wakes up and -> get temp data
}
void goToDeepSleep()
{
Serial.println("Going to sleep...");
esp_sleep_enable_timer_wakeup (DEEP_SLEEP_TIME*1000000);
esp_deep_sleep_start();
}
EDIT 2:
Ok. So first things first. I don't know how one should format their posts on stackoverflow. This is actually my first time posting. That's why I don't know if I should carry out the discussion in the comments or here or ....? I also don't know if I should post my comments and findings at the bottom of this post, at the top or in a separate post. The thing I want to point out is NOT an answer to my problem it's just a finding. That being said, here it is:
I've been thinking about what @Mark Setchell and @bnaecker have said about reading a set number of bytes and it got me thinking. What exactly is a stop?
Apart from terminating a connection locally it also sends out a message. And I think I'm getting that message.... See, whenever I run my server and print out my received data, I get 2 streams. First is my data stream and the other one is:
(...)
***Received:
b''
****Data put into the pool
(...)
could this be the EOF from the stop() function? And if so, would there be a chance that my server is somehow not receiving this message sometimes? This would make it freeze. But if that's the case I would need to have a way to see if a socket is open or not. If my client closes a connection that means that the socket is done for and the server should detect it. I could work with that. But how to get that? I was thinking of suing the 'conn' from my script but as I've just literally started to use this language it's still overwhelming to me. Then is 'conn' an object? A variable? When I print it, I get huge amount of data from it. So, is it a string? I'm at a loss here...
EDIT3:
This is getting ridiculous with all those post-edits, but I guess these are the rules.
First of all, I would like to give out a HUGE 'thank You!' to each and every one who has been helping me so far. You guys rock! :D
It took me a while to answer because I've been troubleshooting. I guess this is one of the things which I'm not particularly loving about Python. There's no repository with a complete description of functions. For example, everywhere I've looked there’s this statement that “socket.recv() is a blocking function”. And (to quote Alec Baldwin in a commercial) yeah, it's a blocking function except, no it isn't. What you can do is conn.recv(59,socket.MSG_DONTWAIT) What it does is, it gets the incoming buffer and that's it. No waiting. I would guess it should be the same with conn.recv() I was so happy to try it and it's been running ok until I got [Errno 11] Resource temporarily unavailable which basically says that there was nothing to read. I thought that it may have been the problem with getting the EOF signal - judging from when has the exception been thrown - even though I send/read a set number of bytes. This means that the problem is consistent with what I’ve been experiencing so far. Cool So then I've decided to use Your advice and put a timeout on the socket. This has been the longest run so far! 2h! I was so happy! Until it crashed. On that timeout. Ugh… So now I'm running my next test with exception handling, like @glory9211 and @urban had suggested. I'll let You guys know how it goes. Fingers crossed.
Cheers, Rafał