Python script partially inactive after few hours - Raspberry Pi - python

I wrote a Python script for the Raspberry Pi in order to monitor changes of 4 GPIO inputs, write them into a databse and send a message to my phone via a telegram bot.
Everything works fine, except that after a couple of hours, some processes of the script shut down. The script is still running (knwoing from a log file), but especially the GPIO event detect and the telegram bot API stop working. This mostly occurs over night.
Because it works perfectly fine for the first few hours after startup, I guess it shouldn't be a bug in the program, but you never know.
I started up the script via rc.local, but switched to System.d for running on startup instead. Works fine for that.
So in a nutshell: Script runs fine, but after a couple of hours some functions in the script become inactive (GPIO event detect, log messages, telegram API - kinda feels like the Pi is falling asleep).
#!/usr/bin/env python
#Import libraries
import time
import RPi.GPIO as GPIO
import telepot
import MySQLdb
import os.path
import logging
import datetime
#start logging
logfile = '/home/pi/Betrieb/logs/log_'+datetime.datetime.now().strftime("%Y-%m-%d_%H-%M")
logging.basicConfig(filename=logfile,level=logging.DEBUG,format='%(asctime)s %(message)s')
logging.debug('=========================================================')
logging.debug('Logging started!')
#initialize connection to Telegram bot
bot = telepot.Bot('##########################################')
#Connect to MySQL-database
con = MySQLdb.connect('localhost',"root","###########","################")
c = con.cursor()
###########################################################################################################
class User:
def __init__(self, id, name, user_id, enable):
self.id = id
self.name = name
self.chat_id = user_id
self.enable = enable
class Machine:
def __init__(self, id, number, name, pin, enable):
self.id = id
self.number = number
self.name = name
self.pin = pin
self.enable = enable
###########################################################################################################
def my_callback(pin):
c.execute("SELECT name FROM machines WHERE pin=%s" % pin)
con.commit()
data = c.fetchall()
for set in data:
machine_name=set[0]
### Attention: multiple sets with same ID can exist
if GPIO.input(pin):
print "Rising edge detected "+str(pin)
string = "INSERT INTO malfunction(machine_name,machine_pin,status) VALUES('%s',%s,1)" % (machine_name,pin)
c.execute(string)
con.commit()
for i in range(0,len(user)):
if user[i].enable:
bot.sendMessage(user[i].chat_id,"Stoerung "+ machine_name)
print "Sent message to", user[i].name
logging.debug('Detected malfunction on Pin %s and sent message to %s', str(pin), user[i].name)
else:
print "Falling edge detected on "+str(pin)
string = "INSERT INTO malfunction(machine_name,machine_pin,status) VALUES('%s',%s,0)" % (machine_name,pin)
c.execute(string)
con.commit()
for i in range(0,len(user)):
if user[i].enable:
bot.sendMessage(user[i].chat_id,"Stoerung behoben "+ machine_name)
print "Sent message to", user[i].name
logging.debug('Solved malfunction on Pin %s and sent message to %s', str(pin), user[i].name)
def updateData():
global machine
global user
logging.debug('Update data.')
#Update user data
c.execute("SELECT * FROM telegram_users")
con.commit()
data = c.fetchall()
user = []
del user[:]
for set in data:
newUser = User(set[0],set[1],set[2],set[3])
user.append(newUser)
#Update machine data
try:
machine
except NameError:
machine = []
else:
for i in range(0,len(machine)):
if machine[i].enable:
GPIO.remove_event_detect(machine[i].pin)
del machine[:]
c.execute("SELECT * FROM machines")
con.commit()
data = c.fetchall()
GPIO.setmode(GPIO.BCM)
for set in data:
# 0 = id / 1 = number / 2 = name / 3 = pin / 4 = enable #
newMachine = Machine(set[0],set[1],set[2],set[3],set[4])
machine.append(newMachine)
if set[4]:
GPIO.setup(set[3], GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.add_event_detect(set[3], GPIO.BOTH, callback=my_callback, bouncetime=300)
logging.debug('Added Event Detect on Pin %s', set[3])
###########################################################################################################
updateData()
logging.debug('Initial Update finished. Start looping.')
Counter = 0
while True:
Counter +=1
if Counter == 600:
logging.debug('Still running!')
Counter = 0
time.sleep(1);
lockfile = "/home/pi/Betrieb/lockfiles/request_update"
if os.path.isfile(lockfile):
os.remove(lockfile)
updateData()
print "Data updated."
logging.debug('Deleted lockfile and updated data.')

Related

DHT22 wrong data if sending not if just printing in commandline

i've got a Problem with the reading of the Data from my 2 DHT22 Sensors.
I'm using following:
Raspberry PI4 B with RaspbianOS
2 DHT22 Sensors
The Project is to report the temperature of our fridge and of our freezer. For this i want to write the Data into a MongoDB or directly into a thingspeak.com channel. But here a the values at least from sensor 2 (freezer) the majority of the time wrong. The Values from sensor 1 (fridge) are the majority of the time right ...
The Goal would be to have the rights data into my online storage ... as it shows in the test print out.
If i'm just showing the Data of the two sensors with my python script everything looks good. Pythoncode test print:
import Adafruit_DHT as dht
from time import sleep
#Set DATA pin
Pin_Fridge = 15
Pin_Freezer = 14
while True:
h_Fridge,t_Fridge = dht.read_retry(dht.DHT22, Pin_Fridge)
h_Freezer,t_Freezer = dht.read_retry(dht.DHT22, Pin_Freezer)
print('Fridge: Temp={0:0.1f}*C Humidity={1:0.1f}%'.format(t_Fridge,h_Fridge))
print('Freezer: Temp={0:0.1f}*C Humidity={1:0.1f}%'.format(t_Freezer,h_Freezer))
sleep(5)
but if I'm now try to write the Data to thingspeak or mongodb the freezer data is the most time wrong.
Pythoncode Thingspeak :
import thingspeak
import time
import Adafruit_DHT
channel_id = XXXXXX # PUT CHANNEL ID HERE
write_key = 'XXXXX' # PUT YOUR WRITE KEY HERE
#Set DATA pin
Pin_Fridge = 15
Pin_Freezer = 14
dht = Adafruit_DHT
def measure(channel):
try:
#Read Temp and Hum from DHT22
h_Freezer,t_Freezer = dht.read_retry(dht.DHT22, Pin_Freezer)
h_Fridge,t_Fridge = dht.read_retry(dht.DHT22, Pin_Fridge)
# write
response = channel.update({'field1': t_Fridge, 'field2': h_Fridge, 'field3': t_Freezer, 'field4': h_Freezer})
print(response)
except:
print("connection failed")
if __name__ == "__main__":
channel = thingspeak.Channel(id=channel_id, api_key=write_key)
while True:
measure(channel)
# free account has an api limit of 15sec
time.sleep(15)
see here: (field 1 = temperature fridge, field 2 = humidity fridge, field 3 = temperature freezer, field 4 = humidity freezer)
Python code mogodb:
from http import client
import Adafruit_DHT as dht
from time import sleep
from pymongo import MongoClient
import pymongo
import datetime;
#Set DATA pin
Pin_Fridge = 15
Pin_Freezer = 14
Interval = 6
def get_database():
try:
# Provide the mongodb atlas url to connect python to mongodb using pymongo
CONNECTION_STRING = "mongodb+srv://XXXXXXX/?retryWrites=true&w=majority"
# Create a connection using MongoClient. You can import MongoClient or use pymongo.MongoClient
client = MongoClient(CONNECTION_STRING)
print("Connected to MongoDB successfully!!!")
# Create the database for our example
return client['brk-regenstauf']
except Exception as e:
print(e)
print("Could not connect to MongoDB. Trying again!")
# Get the database
dbname = get_database()
collection_name = dbname["Cooling Reporting"]
while True:
try:
h_Fridge,t_Fridge = dht.read_retry(dht.DHT22, Pin_Fridge)
sleep(2)
h_Freezer,t_Freezer = dht.read_retry(dht.DHT22, Pin_Freezer)
# ct stores current time
ct = datetime.datetime.now()
print(ct)
fridge = {"type":"fridge","time":ct,"temperature":t_Fridge,"humidity":h_Fridge}
freezer = {"type":"freezer","time":ct,"temperature":t_Freezer,"humidity":h_Freezer}
print(fridge)
print(freezer)
results = collection_name.insert_many([fridge, freezer])
except pymongo.errors.DuplicateKeyError:
continue
sleep(Interval)
see here:
For sure there are not running at the same time.
Anybody has any suggestion? :)

How do I use/create a db cursor in my python thread?

I'm getting threading errors when I try to use or create a db cursor in my process_id function. Each thread will have to use the database to process data for their passed id.
I can't utilize a cursor in the thread/process_id at all(I get threading errors and the DB never updates)...I've coded it a lot of different ways. The code works when I don't use threads.
I have very specific requirements for how this code is to be written, slow and stable is fine. I also cut out a lot of error handling/logging before posting. Daemon/Infinite loop is required.
How do I spin up a new cursor in each thread?
import threading
import time
from datetime import datetime
import os
import jaydebeapi, sys
#Enter the values for you database connection
database = "REMOVED"
hostname = "REMOVED"
port = "REMOVED"
uid = "REMOVED"
pwd = "REMOVED"
connection_string='jdbc:db2://'+hostname+':'+port+'/'+database
if (sys.version_info >= (3,0)):
conn = jaydebeapi.connect("com.ibm.db2.jcc.DB2Driver", connection_string, [uid, pwd], jars="REMOVED")
else:
conn = jaydebeapi.connect("com.ibm.db2.jcc.DB2Driver", [connection_string, uid, pwd])
# Thread Pool Variables
max_threads = 5
used_threads = 0
# define main cursor
cus=conn.cursor()
def process_id(id):
#create a cursor for a thread
cus_id=conn.cursor()
cus_id.execute("SOME QUERY;")
cus_id.close()
global used_threads
used_threads = used_threads - 1
return 0
def daemon():
global num_threads, used_threads
print("Daemon running...")
while True:
#ids to process are loaded into a list...
for id in ids_to_process:
if used_threads < max_threads:
t = threading.Thread(target=process_id, args=(int(id),))
t.start()
used_threads += 1
return 0
daemon()

running python script in background

my script is a server that listens to clients requests and send responses. It handles requests by threading:
class Server:
def __init__(self):
self.host = ''
self.port = 50000
self.backlog = 5
self.size = 1024
self.server = None
self.threads = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.host,self.port))
self.server.listen(5)
except socket.error, (value,message):
if self.server:
self.server.close()
print "Could not open socket: " + message
sys.exit(1)
def run(self):
self.open_socket()
input = [self.server,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for s in inputready:
if s == self.server:
# handle the server socket
c = Client(self.server.accept())
c.start()
self.threads.append(c)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
# close all threads
self.server.close()
for c in self.threads:
c.join()
class Client(threading.Thread):
def __init__(self,(client,address)):
threading.Thread.__init__(self)
self.client = client
self.address = address
self.size = 1024
def run(self):
running = 1
while running:
data = self.client.recv(self.size)
if data:
data2 = data.split()
if data2[0] == 'Hello':
status = 'Hello'
#fetch from database users by location
reply= '6'
if data2[0] == 'Index':
status = 'Index'
#fetch from database users by location
reply='I'
db = MySQLdb.connect(host="localhost", # your host, usually localhost
user="root", # your username
passwd="Rambo_9134", # your password
db="secure_login") # name of the data base
# you must create a Cursor object. It will let
# you execute all the queries you need
cur = db.cursor()
# Use all the SQL you like
cur.execute("SELECT ml.member,m.username FROM locations l JOIN memberlocation ml ON(l.id = ml.location) JOIN members m ON(m.id = ml.member) where l.id = 1;")
# print all the first cell of all the rows
data = []
for row in cur.fetchall() :
print row[1]
data.append({row[0]:row[1]})
print 'JSON', json.dumps(data)
reply = data
self.client.send(json.dumps(reply))
else:
self.client.close()
running = 0
if __name__ == "__main__":
s = Server()
s.run()
this script runs perfectly but it stops when i press enter. I have tried many alternatives: deamon, nohup, ... i couldn't make it run as a service in the background. i think this is a programming issue
how can i make this script run in the background as a service ?
For a quick and easy way in a test/dev environment you can use screen.
screen -S mySessionName
This starts a new screen session with the name mySessionName and attaches to that session. Inside this session you can now run your code.
Use Ctrl+A, D to detach from that session. Your code will continue to run.
To reattach to that session use:
screen -r mySessionName
To show all sessions use:
screen -ls
In a production environment however you should be looking at supervisor. This serverfault question might help.
Make a PHP or HTML script devoted solely to running that python program. Then, run that PHP/HTML script on the server and you're good :).

How to check the connection alive in python?

I make project read RFID tag using python on raspberry pi and using reader RDM880.
My idea is to take the time in and time out to check with the staff to work on time or not.
I try to add card_ID, time_in, time_out to local mysql and remote mysql (IP: 192.168.137.1) using python.
It has the same table in remote and local mysql.
If mysql remote is broken, I want only add to local mysql.
Here is my code:
import serial
import time
import RPi.GPIO as GPIO
import MySQLdb
from datetime import datetime
from binascii import hexlify
serial=serial.Serial("/dev/ttyAMA0",
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=0.1)
db_local = MySQLdb.connect("localhost","root","root","luan_van") #connect local
db = MySQLdb.connect("192.168.137.1", "root_a","","luan_van") #connect remote
ID_rong = 128187 # reader respone if no card
chuoi= "\xAA\x00\x03\x25\x26\x00\x00\xBB"
def RFID(str): #function read RFID via uart
serial.write(chuoi)
data = serial.readline()
tach_5 = data[5]
tach_6 = data[6]
hex_5 = hexlify(tach_5)
hex_6= hexlify(tach_6)
num_5 = int(hex_5,16)
num_6 = int(hex_6,16)
num_a = num_5 * 1000 + num_6
if(num_a != ID_rong):
tach_7 = data[7]
tach_8 = data[7]
hex_7 = hexlify(tach_7)
hex_8= hexlify(tach_8)
num_7 = int(hex_7,16)
num_8 = int(hex_8,16)
num = num_8 + num_7 * 1000 + num_6 * 1000000 + num_5 * 1000000000
else:
num = num_5 * 1000 + num_6
return num
def add_database(): # add card_ID and time_in to remote mysql
with db:
cur = db.cursor()
cur.execure("INSERT INTO tt_control(Card_ID,Time_in) VALUES ('%d',NOW()) " %num)
return
def add_database_local(): # add card_ID and time_in to remote mysql
with db_local:
cur = db_local.cursor()
cur.execure("INSERT INTO tt_control(Card_ID,Time_in) VALUES ('%d',NOW()) " %num)
return
def have_ID(int): #check ID in table tt_control
with db_local:
cur = db_local.cursor(MySQLdb.cursors.DictCursor)
cur.execute("SELECT * FROM tt_control WHERE Card_ID = '%d'" %num)
rows = cur.fetchall()
ID=""
for row in rows:
ID = row['Card_ID']
return ID
def add_time_out(): #add time out to remote mysql
with db:
cur = db.cursor(MySQLdb.cursors.DictCursor)
cur.execute("UPDATE tt_control SET Time_out = NOW() WHERE Card_ID = '%d'" %num)
return
def add_time_out_local(): #add time out to local mysql
with db_local:
cur = db_local.cursor(MySQLdb.cursors.DictCursor)
cur.execute("UPDATE tt_control SET Time_out = NOW() WHERE Card_ID = '%d'" %num)
return
def add_OUT(): #increase Card_ID to distinguish second check
with db:
cur = db.cursor(MySQLdb.cursors.DictCursor)
cur.execute("UPDATE tt_control SET Card_ID = Card_ID + 1 WHERE Card_ID = '%d'" %num)
return
def add_OUT_local(): #increase Card_ID to distinguish second check
with db_local:
cur = db_local.cursor(MySQLdb.cursors.DictCursor)
cur.execute("UPDATE tt_control SET Card_ID = Card_ID + 1 WHERE Card_ID = '%d'" %num)
return
while 1:
num = RFID(chuoi)
time.sleep(1)
Have_ID =have_ID(num)
if(num != ID_rong):
if(Have_ID ==""):
add_database() #---> it will error if remote broken, how can i fix it?
add_database_local()
else:
add_time_out() #---> it will error if remote broken, how can i fix it? I think connection alive can fix, but I don't know
add_time_out_local()
add_OUT()
add_OUT_local() #---> it will error if remote broken, how can i fix it?
You have a couple choices:
(not as good) Ping the server regularly to keep the connection alive.
(best) Handle the MySQLdb exception when calling cur.execute by re-establishing your connection and trying the call again. Here's an excellent and concise answer for how to do just that. From that article, you handle the exception yourself:
def __execute_sql(self,sql,cursor):
try:
cursor.execute(sql)
return 1
except MySQLdb.OperationalError, e:
if e[0] == 2006:
self.logger.do_logging('info','DB', "%s : Restarting db" %(e))
self.start_database()
return 0
(lastly) Establish a new database connection just before you actually call the database entries. In this case, move the db and db_local definitions into a function which you call just before your cursor. If you're making thousands of queries, this isn't the best. However, if it's only a few database queries, it's probably fine.
I use the following method:
def checkConn(self):
sq = "SELECT NOW()"
try:
self.cur.execute( sq )
except pymysql.Error as e:
if e.errno == 2006:
return self.connect()
else:
print ( "No connection with database." )
return False
I used a simple technique. Initially, I connected to DB using:
conect = mysql.connector.connect(host=DB_HOST, user=DB_USER, password=DB_PASS, database=DB_NAME)
Whenever I need to check if the DB is still connected, I used a line:
conect.ping(reconnect=True, attempts=3, delay=2)
This will check if the DB connection is still alive. If not, it will restart the connection which solves the problem.
It just makes sense not to use a status checker function before executing a SQL. Best practice shall handle the exception afterward and reconnect to the server.
Since the client library is always on the client side, there is no way to know the server status (connect status does depend on server status of course) unless we ping it or connect it.
Even if you ping the server and make sure the connection is fine and let the code execute down to the following line, the connection theoretically still could be down within that glimpse of time. So it's still not guaranteed that you will have a good connection right after you check the connection status.
On the other hand, ping is as expensive as most operations. If your operation fails because of a bad connection, then it's as good as using the ping to check the status.
Considering these, why bother to use ping or other no-matter built-in or not-built-in functions to check the connection status? Just execute your command as if it is up, then handle the exception in case it is down. This might be the reason the mysqlclient library does not provide a built-in status checker in the first place.

Python clean keyboard interupt

I have a python script in which I'm using threading to wait for inputs on my raspberry pi. This is the first time I've ever used threading before and I'm having some difficulties handling the KeyboardInterrupt. Its not really an issue, but if the user presses control-c to early, like right as soon as the program starts, python freaks out and spews a console full of import errors. Ive tried wrapping the imports in try - excepts, but that doesn't seem to work. I have a feeling its something with the threading as I've never had this issue before.
Besides wrapping everything in try-excepts i also tried:
from threading import Thread
cond = threading.Condition(threading.Lock())
cond.acquire()
cond.wait(None)
which I hear was suppose to not allow the keyboard interrupt until its been loaded?
Is there anyway just to not have python do anything on keyboardinterrupts?
Any help would be appreciated. Thanks!
My Code:
import RPi.GPIO as GPIO
import pymysql as mysql
import time
import urllib.request
from threading import Thread
GPIO.setmode(GPIO.BOARD)
GPIO.cleanup()
class table():
__slots__ = ['pin','num','time']
def __init__(self, pin, num, time=0):
self.pin = pin
self.num = num
self.time = time
# Room ID
room = 6
# time (minutes)
thetime = 38
# table Setup
tableList = [ \
table(11,6), \
table(13,2), \
table(15,4), \
table(19,5), \
table(21,3), \
table(23,1), \
]
def settable(table):
with urllib.request.urlopen("http://time.zyphox.com") as url:
curtime = int(url.read())+(thetime*60)
con = mysql.connect(host="345", user="435", passwd="345435", db="34534543")
cur = con.cursor()
cur.execute("UPDATE tables SET time='"+str(curtime)+"' WHERE number='"+str(table)+"' AND id_room='"+str(room)+"'")
cur.close()
con.close()
print("Setting table " + str(table) + " to " + str(thetime) + " minutes...")
def watchtable(table):
GPIO.setup(table.pin, GPIO.IN)
while True:
if(GPIO.input(table.pin)!=1 and table.time <= time.time()):
settable(table.num)
table.time = time.time() + 10
def main():
print("Loading kitchen System...")
for table in tableList:
t = Thread(target=watchtable,args=(table,))
t.daemon=True
t.start()
time.sleep(1)
print("Successful!")
while True:
time.sleep(300)
print("- Pinging MYSQL database.")
main()

Categories

Resources