I need to execute a bash-command in a python program on a raspberry pi, which starts a record, which really starts recording if their is the sound above a certain frequency and finishes after 8sec or if there is silence. After a record finished, it starts itself an waits for new sound to record. Each record is labeled with time. That's the code doing I quoted here:
while GPIO.input(26) == False:
timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
process = subprocess.Popen ("rec -c 2 -r 192000 --buffer 50000 -b 32" + filepath + timestamp + ".wav sinc 0.1k silence 1 0.1 0.8% 1 0.4 0.8% vol 6 trim 0 8", shell = True)
process.communicate()
As you can see, to finish the loop, the program waits for a GPIO-input signal (push button). I have an extra code which looks for the name of the subprocess and kills it.
But here is my problem: While the loop runs, it only "looks" for input in the millisecond between one record finishes and a new starts". If I push my button during a record, the loop continues after the record. It only breaks if I push in between.
At first, I thought maybe the while-loop is a bad choice...but if I am not wrong, the problem seems to be the running subprocess.
So here is my question: How can I accomplish this record loop but can stop/kill during a running record through user-input by GPIO. (ctrl+c won't be a viable option)
Many thanks
The issue is that the while loop only checks the condition once each time through the loop. The process.communicate() call waits for the rec command to finish, so you are absolutely right - the button is only checked once, just after the rec and before the timestamp = ....
A solution is to watch for the button in a separate thread, running in parallel with the rec. This answer suggests using event detection from the RPi.GPIO library. Try something like this:
should_exit = False
def my_callback(channel):
if GPIO.input(26):
should_exit = True
GPIO.add_event_detect(26, GPIO.RISING, callback=my_callback, bouncetime=300)
while not should_exit:
timestamp = ...
The my_callback routine should run when the button is pressed, and set the should_exit flag. Once the rec finishes, the while will check should_exit and terminate. (Caution - not tested!)
Related
I have a small script where I have a continuous loop. The loop runs and I expect that the user will give an input whenever he wants. I want to check if the user wrote something in the console, if yes, I will do something extra, if not, the loop continues.
The problem is that when I call the input() function, the program waits for user input.
The code that I will use here will be just a simple version of my code to show better what is my problem.
i=0
while True:
i+=1
if 'user wrote a number':
i+= 'user's input'
The objective is to not stop the loop if the user do not input anything. I believe this is a simple thing but I didn't find an answer for this problem.
Thank you for your time!
You can execute the background task (the while True) on a separate thread, and let the main thread handle the input.
import time
import threading
import sys
def background():
while True:
time.sleep(3)
print('background task')
def handling_input(inp):
print('Got {}'.format(inp))
t = threading.Thread(target=background)
t.daemon = True
t.start()
while True:
inp = input()
handling_input(inp)
if inp == 'q':
print('quitting')
sys.exit()
Sample execution (lines starting with >> are my inputs):
background task
>> a
Got a
>> b
Got b
background task
>> cc
Got cc
background task
background task
>> q
Got q
quitting
Process finished with exit code 0
The only caveat is if the user takes longer than 3 seconds to type (or whatever the value of time.sleep in background is) the input value will be truncated.
I'm don't think you can do that in one single process input(), because Python is a synchronous programming languaje,the execution will be stoped until input() receives a value.
As a final solution I'd recommend you to try implement your functions with parallel processing so that both 'functions' (input and loop) can get executed at the same time, then when the input function gets it's results, it sends the result to the other process (the loop) and finish the execution.
I have written some code to make the turtlebot turn around. The code is working. What I want to know is how fast the turtlebot is running and how I can control it. Forexample, how can I ensure that the turtlebot turns 5 degrees in one minute?
Last part of the question. After pressing Ctrl-C, the turtlebot stops but the script keeps running. Why? and how can I stop that?
this post does not really help.
went through this post .Does that mean that the while loop below runs 5 times a second regardless of the values I put in the for loops? Or does it mean ROS tries its best to make sure that the loop runs 5 times a second to the best of my machine's ability?
Thank you very much.
# 5 HZ
angle = 5
r = rospy.Rate(5);
while not rospy.is_shutdown():
# code to turn
for x in range(0,100):
rospy.loginfo("turn")
turn_cmd.angular.z = radians(angle)
new_angle = (angle + new_angle) % 360
self.cmd_vel.publish(turn_cmd)
r.sleep()
# code to pause
for x in range(0,100):
rospy.loginfo("stop")
turn_cmd.angular.z = radians(0)
self.cmd_vel.publish(turn_cmd)
r.sleep()
def shutdown(self):
# stop turtlebot
rospy.loginfo("Stop turning")
self.cmd_vel.publish(Twist())
rospy.sleep(1)
According to ROS Wiki, the rospy.Rate convenience class makes a best effort to maintain the loop running at the specified frequency by considering the execution time of the loop since the last successful r.sleep(). This means in your case: as long as the code execution time within the loop does not exceed 1/5 seconds, rospy.Rate will make sure the loop runs at 5Hz.
Regarding the script not stopping when pressing Ctrl-C:
KeyboardInterrupt will be handled differently than in normal Python scripts when using rospy.
rospy catches the KeyboardInterrupt signal to set the rospy.is_shutdown() flag to true. This flag is only checked at the end of each loop, therefore if pressing Ctrl-C during the for-loop executions, the script cannot be stopped because the flag is not checked immediately.
A manual way to signal a shutdown of the node is to use rospy.signal_shutdown(). For this, the disable_signals option needs to be set to true when initializing the ROS node (see Section 2.3 here). Note that you will additionally have to manually invoke the correct shutdown routines to ensure a proper cleanup.
Is there a way to have a urwid app to do a sys.exit() after a configurable timeout if no input has been received from the user in more than 30 seconds?
We are facing network outages and that leads to the SSH Session being dropped but the client program keeps running and holds a Database lock and manually killing is the only option for now and hence this requirement.
You can set an alarm in the main loop that will call whatever code you want when it times out. Here I call a function that uses the ExitMainLoop exception, but sys.exit() would also work.
This code cancels the previous alarm (if any) when keyboard input happens, then sets a new alarm. As long as keys are coming in, the alarm should never go off.
Internally, as of late 2020, for alarms urwid seems to use Python's time.time(), which is not guaranteed to only go forward one-second-per-second. The alarm might go off early, exiting the program, if the system clock gets adjusted forward (by NTP?).
import urwid
timeout_time=30
def urwid_exit(loop, user_data):
raise urwid.ExitMainLoop
def handle_input(input):
global txt
global loop
#don't reset the alarm on a mouse click,
# and don't try to display it (the .set_text would error if given a tuple)
if not type(input) is tuple:
if hasattr(handle_input, "last_alarm") and handle_input.last_alarm:
loop.remove_alarm(handle_input.last_alarm)
handle_input.last_alarm = loop.set_alarm_in(timeout_time, urwid_exit)
txt.set_text("key pressed: %s" % input)
txt = urwid.Text("Hello world")
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill, unhandled_input=handle_input)
#call handle input once without any input to get the alarm started
handle_input(None)
loop.run()
A slight variation on StephenK's answer is to use loop.event_loop.enter_idle(callback) instead of unhandled_input. The callback function will be run whenever urwid enters an idle state, including after processing a keypress event. This is somewhat more general: the timer starts after all activity has finished. (Say, the last keypress starts an action that takes many seconds to finish)
The relevant documentation is at
https://urwid.readthedocs.io/en/latest/reference/main_loop.html
import urwid
timeout = 10
txt = urwid.Text(
'This program will exit after '
f'_{timeout}_ seconds of inactivity '
'(no keypresses, etc)\n',
align='center'
)
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill)
alarm_handle = None
def alarm_callback(_loop, _user_data):
raise urwid.ExitMainLoop
def idle_callback():
global alarm_handle
loop.remove_alarm(alarm_handle) # remove old alarm
alarm_handle = loop.set_alarm_in(timeout, alarm_callback)
text = txt.get_text()[0] + f"\nAlarm has been reset _{alarm_handle[1]}_ times"
txt.set_text(text)
loop.event_loop.enter_idle(idle_callback)
loop.run()
My python script is working with a modem, and the first step of the process is to check if there is enough signal. Through AT commands,I can ask the modem for the signal intensity, so I made a loop that constantly asks for the strenght of the signal, and then proceeds if the signal is strong enough.
If there is signal available, it works just fine. The problem is when there is no signal. I've tried increasing the sleep time, but it seems not to work.
Signal = False
while Signal is not True:
x = ser.readline()
y = x.rstrip()
z = y.decode('utf-8')
ser.write(str.encode('AT+CSQ?\r'))
time.sleep(9)
if (z.startswith("+CSQ")):
a = int(z[5])
if a >= 3:
Signal = True
time.sleep(4)
enough_signal_write()
else:
checking_signal
With long loops, like 10 tries to catch signal, it seems to work strangely, like ignoring if the signal is strong enough sometimes, then finally catching up and running. (I can check this through the strange order of the modem outputs with 'print(z)' line)
I'm feeling like I need something to wait for the modem to respond, and then take actions with this, but I'm not sure how I should do it.
Thanks for you time
I wrote a program that reads a text file and runs an .exe for every line in the text file. This results in opening a new command line window for every time i run the .exe. The windows do close on their own once the current task is finished, but the problem is as follows:
If i have 100 lines in the text file, this means that i call the .exe file 100 times. My problem with that is if i want to cancel the run after it already started, i have to click on the red "X" to close every window one after the another.
What i am trying to do is have some sort of a command interrupt the running program and either close all upcoming windows or just stop the for loop from running.
Is it possible to write into the console a command to interrupt the current running code?
Would it be better to use some sort of a key event listener? If so, are there any built-in key listeners in Python? I can't seem to find any. Does that mean that i have to install Pygame just so i can use a key event listener?
Maybe i should try to listen to the command line and detect an exit code on one of the windows that i manually close and that way end the for loop?
There are a few ways you could go about this. But pretty much you have one main issue - you need some sort of flag that can be switched such that the code will know it must stop. For instance, if the code is working in a while-loop, it should check at the start of this loop if the flag is valid, or if the flag is telling the loop to stop...
while flag:
# do code
There are a few ways to implement this flagging like operation for your needs. I will discuss the threading option. First, you need to understand how threading works, and then you need to mold your script such that instead of "running an executable" for each line of the text file, you would read the text file, and put all the lines into a queue, then you would have a few threads that read from that queue, and perform the desired action (like running an executable) but instead of running an external executable, you should mimick this with Python, this thread should be a daemon thread.. and it should have a main loop which checks if a flag that exists in the parent thread is turned on...
Below is an example:
from threading import Thread
from Queue import Queue
import sys
import time
class Performer():
def __init__(self):
self.active = False
self.queue = Queue()
def action(self, line):
pass # your code should be here
def operate(self, text_file, threads=5):
with open(text_file) as f:
for line in f:
self.queue.put(line)
self.active = True
thread_pool = []
for i in range(threads):
t = Thread(target=self.__thread, name=('worker-%d' % i))
t.daemon = True
t.start()
thread_pool.append(t)
while self.active:
try:
if self.queue.empty():
break
except KeyboardInterrupt:
self.active = False
sys.exit('user + keyboard = byebye')
else:
time.sleep(1)
def __thread(self):
while self.active:
if not self.queue.empty():
try:
self.action(self.queue.get())
except Exception:
pass # do something here