while loop (Python) becomes slow after 500 cycle in Raspberry Pi 4 - python-3.x

I am making one project with analog sensors using a raspberry pi 4 controller (4GB ram). I have connected 9 sensors currently, I am using while loop for an infinite cycle. The code is very simple taking input from sensors and using the canvas library changing the colour of shapes when it gets pressed and remains the same when not. The problem is after 500-550 cycles it is getting slower. As the number of cycles increases the response rate becomes slower and slower. Can anyone please suggest how can I overcome this problem?
I am also a noob in coding so please guide me if I did some mistake. I am writing code for only one sensor because it is the same for all the sensors.
Code:
from tkinter import *
import Rpi.GPIO as GPIO
import time
import tkinter as tk
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
.
.
.
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
root=tk.Tk()
canvas = tk.Canvas(root, bg='white', width=500, height=500)
canvas.grid()
canvas.bind('<key>', lambda event: root.destroy() if (event.char == 'q') else 0)
while True:
def sense1(on):
square1= canvas.create_rectangle(5, 5, 105, 105, width=0, fill='red' if on else 'green')
.
.
.
def sense9(on):
square9= canvas.create_rectangle(235, 235, 335, 335, width=0, fill='red' if on else 'green')
global S1,...,S9
if GPIO.input(7) == GPIO.HIGH:
S1 = sense1(True)
else:
S1 = sense1(False)
.
.
.
if GPIO.input(18) == GPIO.HIGH:
S9 = sense9(True)
else:
S9 = sense9(False)
root.update_idletasks()
root.update()
same code I have written for the other 8 sensors.

Check core temperature.
The while loop can cause the temperature of the microprocessor to rise. The raspberry has a self-protection mechanism that is activated when the core temperature is very high, generating a drop in performance. I suggest you change the while loop to callbacks
Saludos!

Related

Non-latching GPIO output that needs to disregard input

New to programming, so I'll try my best to explain:
I'm trying to use a Pi Zero in my solar setup to switch the power-on input on an x86 motherboard (basically operating as the power button). The idea is that a battery voltage monitor sets pin 14 on the Pi high when the battery is at a high enough voltage. The script reads this input then switches an output (26) on the Pi, which in turn closes a relay for one second, which will then activate the power-on input on the motherboard.
The problem is that I cannot get the output (26) to turn off after one second. The script won't ignore pin 14, and just keeps the output (26) high. (Pin 14 is naturally held high by the battery monitor until the battery level falls down below a certain threshold)
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26,GPIO.OUT)
GPIO.output(26, 1)
while True:
if GPIO.input(14)==1:
GPIO.output(26, 0)
sleep(1)
GPIO.output(26, 1)
else:
GPIO.output(26, 1)
GPIO.cleanup()
Here's one possible solution that polls the value of GPIO14 1/second
and uses the variable power_on to figure out how we respond to the
value:
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26,GPIO.OUT)
GPIO.output(26, 1)
power_on = False
while True:
if not power_on:
if GPIO.input(14) == 1:
GPIO.output(26, 0)
sleep(1)
GPIO.output(26, 1)
power_on = True
else:
if GPIO.input(14) == 0:
power_off = True
time.sleep(1)
# Note that you're never going to reach this line.
GPIO.cleanup()
Alternatively, you can have your code block until pin 14 changes, leading to something like this:
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26,GPIO.OUT)
GPIO.output(26, 1)
while True:
GPIO.wait_for_edge(14, GPIO.RISING)
GPIO.output(26, 0)
sleep(1)
GPIO.output(26, 1)
# Note that you're never going to reach this line.
GPIO.cleanup()
This uses GPIO.wait_for_edge() to wait for a rising edge on GPIO14 (a low ➡️ high transition), and then triggers your relay.

Raspberry Pi 3 - One pin sending different voltage (4.25 instead of 3.3)

I am working with the SainSmart 16-Channel Relay board and everything is working fine except for one pin is sending a different voltage. This is causing the relay to shutoff after a few seconds. All the pins show 3.3v except for pin 4 that is showing 4.25v when I test the terminal with my multi-meter. I have added a 1k resister inline with the GPIO pin because the board is designed for Arduino and not the Raspberry Pi. The resister has corrected the voltage difference issue because 15 other relays work fine. Also I am reading this voltage issue before the 1k resistor so it should be effecting this.
I have changed out the Raspberry Pi board and the relay board with another thinking that was the issue and am receiving the exact same voltage. Any ideas as to what the issue could be would be greatly appreciated.
Below is the code I am using to test pins 2-8.
from tkinter import *
import RPi.GPIO as GPIO
class Create_Button:
def __init__(self,pin):
self.pin = pin
GPIO.setup(self.pin, GPIO.OUT)
GPIO.output(self.pin, GPIO.HIGH)
self.b = Button(text="Pin %s" % self.pin + " - Turn Relay On", command=self.toggle)
self.b.pack()
def toggle(self):
print(self.pin)
if GPIO.input(self.pin):
GPIO.output(self.pin, GPIO.LOW)
self.b.config(text="Pin %s" % self.pin + " - Turn Relay On")
else:
GPIO.output(self.pin, GPIO.HIGH)
self.b.config(text="Pin %s" % self.pin + " - Turn Relay Off")
root = Tk()
root.title("Toggler")
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(FALSE)
for pin_count in range(2,9+1):
button = Create_Button(pin_count)
quitButton = Button(root, text="Quit", command=exit)
quitButton.pack(side=LEFT)
root.mainloop()
GPIO.cleanup()

Audio filter with python

As part of an interactive installation, I would like to program an audio filter LPF (cut high frequencies) on a sound that runs in a loop on python. In addition I would like to vary the cutoff frequency of the filter according to the distance of an obstacle measured by a distance sensor, all on a Raspberry Pi 3.
I have already managed to program the distance measurement by the sensor and to run a looping sound but I do not know how to do it for the audio filter. By searching on the internet I can not find an answer and a clear method on my situation.
Here is my code:
#Libraries
import RPi.GPIO as GPIO
import time
import pygame
pygame.init()
caca = pygame.mixer.Sound("bells001.wav")
pygame.mixer.music.load("bells001.wav")
#GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)
pygame.mixer.music.play(-1)
#set GPIO Pins
GPIO_TRIGGER = 23
GPIO_ECHO = 24
#set GPIO direction (IN / OUT)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)
def distance():
# set Trigger to HIGH
GPIO.output(GPIO_TRIGGER, True)
# set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, False)
StartTime = time.time()
StopTime = time.time()
# save StartTime
while GPIO.input(GPIO_ECHO) == 0:
StartTime = time.time()
# save time of arrival
while GPIO.input(GPIO_ECHO) == 1:
StopTime = time.time()
# time difference between start and arrival
TimeElapsed = StopTime - StartTime
# multiply with the sonic speed (34300 cm/s)
# and divide by 2, because there and back
distance = (TimeElapsed * 34300) / 2
return distance
if __name__ == '__main__':
try:
while True:
dist = distance()
print (dist)
time.sleep(0.2)
# Reset by pressing CTRL + C
except KeyboardInterrupt:
print("Measurement stopped by User")
GPIO.cleanup()
I tried this for now
from pydub import AudioSegment
from pydub.playback import play
song = AudioSegment.from_wav("bells001.wav")
new = song.low_pass_filter(10000)
play(new)
Unfortunately instead of me out a filtered sound, I have the sound very degraded with lots of crunches ... Anyone have an idea?
I tried other scripts but I still have problems with module or library not found while I install and reinstall all these modules.
Can anyone help me?

Python ability to manage 2 or more physical buttons

I am making a camera. I am using a RPi 3 and python. I am using 2 GPIO attached to momentary switches. With the first button I want to be able to take pictures. The second one runs a script to shutdown the system, but IT IS NOT WORKING. This is the code. Please help.
import RPi.GPIO as GPIO
from gpiozero import Button
import time
import os
from time import sleep
from picamera import PiCamera
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
button1 = Button(17)
button2 = Button(18)
camera = PiCamera()
channel = 1
if GPIO.input(18):
button2.wait_for_press()
print("Here I will run the shutdown code")
if GPIO.input(17):
frame = 1
while True:
try:
button1.wait_for_press()
camera.capture('/home/pi/Vinpics/frame%03d.jpg' % frame)
frame += 1
except KeyboardInterrupt:
print("Just a place holder")

UPDATE 2: How Do I Stop a Function in a while loop?

Update:
So I was successful in implementing threading to allow the GUI to remain unblocked while the process is running. Now i am trying to figure out how to get this while loop to break and still function properly.
I tried implementing a second variable the while statement sees, as a flag, to attempt to break the while loop after running the PUMP function inside of it once. However, now the PUMP function doesn't run at all. The GPIO pin never goes high.
What I'm looking for this to do:
-Press button.
-Sets Flag to 1
-Runs RUN() function in thread if float switch is high/ signals low water if float switch is low
- RUN() checks status of flag and float switch while running PUMP() function
- PUMP() turns GPIO pin high, and after 5 secs, calls the OFF() function
- OFF () sets flag to 0 and also sets pump GPIO to low
If during the PUMP() the float switch goes low, it should trigger and call the LOW() function, stopping the pump by setting GPIO pin to low and displaying status. This also sets the flag to 0 as well.
Code:
from tkinter import *
import tkinter.font
import RPi.GPIO as GPIO
import threading
#Variables
Flag = 0
#GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16, GPIO.OUT) #Water Pump
GPIO.setup(18, GPIO.IN) #Tank Float Switch
GPIO.output(16, GPIO.LOW)
#Window Setup
win = Tk()
win.title("Pump Test")
win.geometry("150x150+0+0")
#Label Setup
Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=0, column=0)
#Functions
def RUN ():
while GPIO.input(18) and Flag == 1:
PUMP()
if Flag == 0:
OFF()
elif GPIO.input(18) == False:
LOW()
def OFF ():
Flag = 0
GPIO.output(16, GPIO.LOW)
WTR.config(text="Water", bg="grey")
def LOW ():
Flag = 0
GPIO.output(16, GPIO.LOW)
WTR.config(text="LOW WATER", bg="red")
def WTR ():
Flag = 1
if GPIO.input(18):
threading.Thread(target=RUN).start()
if GPIO.input(18)== False:
threading.Thread(target=LOW).start()
def PUMP ():
GPIO.output(16, GPIO.HIGH)
win.after(5000, OFF)
#Buttons
WTR = Button(win, text="Water", bg="grey", command = WTR, height = 2, width = 8)
WTR.grid(row=1, column=0) #Water Pump Control
mainloop()
To ensure a UI remains responsive to user events (mouse clicks etc) and also to system events (like exposure and repainting) you should never enter a long lived loop in a function and never use sleep. Instead Tkinter provides the after method to allow you to schedule something to be done after some interval. This call adds your call into the event queue and its gets processed in due time by the code called by mainloop. For something that should occur after a delay obviously after(num_millis) is used. If you need to poll the state of a pin, then use a short time and in the handler, set another after call to call your polling function again. Note you can cancel after calls provided you retain the id value that is returned when you call the method.
Don't use time.sleep. No UI events will be processed during the sleep and the UI will be dead. Use after.
Your Flag is a module-level variable.
If you want to modify that in a function (without passing it as an argument), you need to mark it as global in the function.
Observe:
In [1]: flag = 0
In [2]: def foo(num):
...: flag = num
...:
In [3]: flag
Out[3]: 0
In [4]: foo(4)
In [5]: flag
Out[5]: 0
In [6]: foo(12)
In [7]: flag
Out[7]: 0
Calling foo sets a flag, but this is local to the function! It has no effect on the module-level object.
You have to explicitly say that you want to modify the module-level variable:
In [8]: def bar(num):
...: global flag
...: flag = num
...:
In [9]: bar(4)
In [10]: flag
Out[10]: 4
In [11]: bar(7)
In [12]: flag
Out[12]: 7
Thanks #patthoyts and #RolandSmith for the insight that helped lead me to the answer.
2 things they stated were helpful - Not using time.sleep and making sure I was using a global variable.
With some re-work on the Flag idea, and not sleeping but creating a check function to see how much time has passed. Deleted threading for now as it wasn't dire to the GUI process. Big shout out to Tom Slick with the behind the scenes help as well!
from tkinter import *
import tkinter.font
import RPi.GPIO as GPIO
import time
GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16, GPIO.OUT) # Water Pump
GPIO.setup(18, GPIO.IN) # Tank Float Switch
GPIO.output(16, GPIO.LOW)
time_to_run = 60 # time to run the pump in seconds
time_running = time.time() - 1
#Window Setup
win = Tk()
win.title("Pump Test")
win.geometry("150x150+0+0")
#Label Setup
Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=0, column=0)
#Functions
def off():
WTR.config(text="LOW WATER", bg="red")
GPIO.output(16, GPIO.LOW)
def wtr():
global time_running
time_running = time.time() + time_to_run
if GPIO.input(18) and not GPIO.input(16):
pump_on()
elif GPIO.input(16):
pump_off()
def pump_on():
WTR.config(text="Water", bg="green")
GPIO.output(16, GPIO.HIGH)
# added to turn the pump off manualy
def pump_off():
WTR.config(text="Water", bg="grey")
GPIO.output(16, GPIO.LOW)
def check_pump():
# if the water level goes low turn the pump off
# and show low water level
if not GPIO.input(18):
off()
# if a set time has passed then shut the pump off it it is running
if time_running <= time.time() and GPIO.input(16):
pump_off()
win.after(100, check_pump) # call this function every 100 miliseconds
#Buttons
WTR = Button(win, text="Water", bg="grey", command = wtr, height = 2, width = 8)
WTR.grid(row=1, column=0) #Water Pump Control
check_pump() # must be called once to start the after function
mainloop()

Resources