DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

while loop question

Wed Nov 15, 2017 6:37 pm

Hi,

I ran into a catch 22 situation, trying to structure my python script running on the Rpi 3.
It boils down to this question:

Can i stop a While ... loop following an event outside the loop ? (in an elegant fashion)

e.g. A timer is running in a while... loop.
Meanwhile the Pi detects a signal, perhaps a button press that comes in via a GPIO pin.
This should stop the timer from running.

(I could put the detection of the button press inside the timer loop,
but that is not entirely satisfactory.)

Is it possible ?

thx,
Paul

Davies
Posts: 150
Joined: Sat Apr 04, 2015 4:24 pm

Re: while loop question

Wed Nov 15, 2017 6:50 pm

sure, i presume you using While True or while 1 or something like that at mo, you just say while the gpio is off/on: instead
eg.

Code: Select all

while not GPIO.input(27):
    Do_stuff()
    count += 1
    time.sleep(1)
.
if you need more help post an example of your current code in the Python section of this forum

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Wed Nov 15, 2017 8:58 pm

OK, i thought of that too, but not too sure if it would do the trick. ;)

But what if there are 4 buttons ?

"Or" statement?

thx,
Paul

User avatar
Paeryn
Posts: 2966
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: while loop question

Wed Nov 15, 2017 9:21 pm

You can also use break to immediately terminate the current loop if you need to do so whilst in the middle of the loop.

Code: Select all

x = 0
while x < 10:
    x = x + 1
    if x == 5:
        break
    print("x = {}".format(x))
print("Goodbye")
This will print the numbers 1 to 4 but when it gets to the if x == 5: when x does equal 5 then the break statement will execute which will terminate the while loop there and then even though the while condition is still true.
She who travels light — forgot something.

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Thu Nov 16, 2017 6:39 am

The if statement inside the loop, is what i am trying to avoid, because i need many,
for different situations.

I seem to observe that inside a loop, response to button-presses is not so snappy.

The "break" idea is something that i am going to follow-up on,
because i can get the different events to produce the same trigger
that stops the while loop.

Let's try that. 8-)
thx,
Paul

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Thu Nov 16, 2017 3:37 pm

Hi,

Bottom Line, it is a catch 22, and i'll have to implement IF statements inside a loop,
wether in a function or directly.

The reason for the fuss is, that i am a little disappointed with the response times,
between pressing the button, getting GPIO input and python realizing that it has to do something.

A short press is not sufficient, you need to keep it pressed almost like ringing a bell.

In don't know if there are tables with reaction times for GPIO pins, i assume they are all the same ;)

Paul

scotty101
Posts: 3958
Joined: Fri Jun 08, 2012 6:03 pm

Re: while loop question

Thu Nov 16, 2017 4:00 pm

Are you using the GPIO event_detected or wait_for_edge functions? They might help you so that you don't 'miss' the button being pressed.
You might register event_detected callbacks for each button and write something to a global variable depending on the event and then monitor that global variable in your while loop.

See the official RPi.GPIO wiki for information on this
https://sourceforge.net/p/raspberry-gpi ... ki/Inputs/
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Thu Nov 16, 2017 4:21 pm

Thanks, no idea what you are talking about.
Got some reading to do.

Paul

User avatar
davidcoton
Posts: 5027
Joined: Mon Sep 01, 2014 2:37 pm
Location: Cambridge, UK
Contact: Website

Re: while loop question

Thu Nov 16, 2017 11:53 pm

Do you have a sleep in your loop? That is what can kill the response time. Otherwise, what is the loop doing? How long does a loop cycle take? The average delay in reading a GPIO button press will be half a loop cycle, varying from almost nothing to a full loop cycle with roughly linear distribution.

Note that a faster (no sleep) loop will respond faster but will load one core more fully, leaving less time for other tasks and introducing more performance uncertainty. Ther trick is is find a sleep value that gives adequate response without fully loading one processor.

More advanced solutions involve multi-thread processing, to separate input handling from the main process loop. The inputs can be interrupt driven. You still have to work out how your main loop responds to a registered interrupt. but the input only has to be held until the interrupt is triggered (quick), not until the main loop responds.

EDIT:typos
Last edited by davidcoton on Fri Nov 17, 2017 2:21 pm, edited 1 time in total.
Signature retired

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Fri Nov 17, 2017 8:11 am

Hi David,

You say "scientifically", what i suspected was happening.
I did notice that sometimes a button responded fast, and other times slower.

As you can read above, i am trying to eliminate everything possible out of the timer loop
that would be (processor) tiime consuming. (If statements ...)

Only to find that they return though the back door. (eg using a def function)
I did not know about the "sleep" issue, but i do not know how to construct a seconds timer
without it.

I'll leave multi-thread procesing etc.. to when i move out of the beginners forum. ;)
Next attempt will be to use this edge function.

Paul

User avatar
davidcoton
Posts: 5027
Joined: Mon Sep 01, 2014 2:37 pm
Location: Cambridge, UK
Contact: Website

Re: while loop question

Fri Nov 17, 2017 2:18 pm

DPaul wrote:
Fri Nov 17, 2017 8:11 am
As you can read above, i am trying to eliminate everything possible out of the timer loop
that would be (processor) tiime consuming. (If statements ...)

Only to find that they return though the back door. (eg using a def function)
I did not know about the "sleep" issue, but i do not know how to construct a seconds timer
without it.
Well noticed, it's not the length of code written in the loop, it's that plus everything it calls. There is no way round that, if the code isn't executed as part of the loop, you can't break out of the loop as a result of what it finds.

I've just re-read your original post. You say a timer is running. Unless you have used a library that implements multi-threading, it is probable that the timer "blocks" the thread until the end of the time interval. The if statements will not run while the timer is running, only after it exits. If you want the GPIO signals to interrupt the timer, you will have to use multi-threading.

Can you post your code (I know it's not always commercially possible or wise)?
You can add debug statements to log (with a timestamp) when the timer starts and stops, and when the GPIO is detected.
Using GPIO edge interrupts to store a flag will make the inputs more responsive (no need to hold them on), but the timer thread will not be exited while the timer is running unless you program the GPIO interrupt handler to abort the timer. So you'll have to promote yourself from "Beginners" to "Advanced users" if you really need to exit while the timer is running.
Signature retired

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Fri Nov 17, 2017 4:34 pm

Thanks for all this advice.

I did implement the GPIO edge-detection instead of the GPIO high and low stuff.
Without being able to prove it, i do feel the whole thing is more responsive.

There are many sample scripts to be found, but none seem to catch the essence of a true stopwatch.
It's not just start and stop and show the elapsed time,
but also start/stop and start again (from that point) ,
it's also intervals(= stop/show but keep counting in the background, and start again at elapsed time )
+ some other nice-to-haves.

For the moment i got this almost working, if a can solve a boolean that is not doing what i want. :?

The next thing will be a pimoroni shim to stop the Rpi altogether when not used. (see other thread)
And build this into a nice case i bought the other day.
(Stopwatches are meant to be used handheld in the field, not on a desk)
Paul

User avatar
davidcoton
Posts: 5027
Joined: Mon Sep 01, 2014 2:37 pm
Location: Cambridge, UK
Contact: Website

Re: while loop question

Fri Nov 17, 2017 8:47 pm

Ahh, a stopwatch! Did you say that before? If so, I missed it.

Clearly for accuracy the buttons must act immediately (or sooner). Raspbian has some inherent limitations for this, it is not designed as a real time system.
You may need (in a future iteration) to investigate using a real time kernel -- or possibly, dare I say it, an Arduino.

But you certainly need to make the GPIO interrupt routines act on the timer immediately. One way is to use the basic system time and store a start value, then subtract that from the current value tio display the elasped time. Stop/Restart could be a bit tricky -- you would need to store the previously accumulated time separately, and add that to the displayed time. Splits and stuff are not too hard once you get the right mode of thinking.
There should be no need for the interrupts to do anything directly to start or stop the display loop -- that just runs, displaying what ever values (running or static) that the current mode requires.
Signature retired

User avatar
bensimmo
Posts: 4623
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: while loop question

Fri Nov 17, 2017 8:52 pm

I've used gpiozero module for a clock timers.

You can also use a button group if I remember correctly.

It's there to make buttons a bit easier for beginners and other alike.
But it can be a bit slower with to default gpio setup.
But it's an easy change.


Another thing to look at, touched on above. Is event driven (as used in gpiozero)

Basically it just sits there until something is pressed and then reacts on this.

Look here for an over view.
http://raspi.tv/2013/how-to-use-interru ... pio-part-3


And for gpiozero https://gpiozero.readthedocs.io/

DPaul
Posts: 97
Joined: Fri Oct 13, 2017 7:39 am

Re: while loop question

Sat Nov 18, 2017 7:12 am

Thanks all.

GPIO edge detection i have, and i have seen something about a GPIO "wait" function.
That, i think, will be useful to replace a while loop, to start the who thing up.

The completed assembly will be good enough for it's purposes, i don't need 1/100 of a second accuracy.

Maybe i'll post a picture when it is ready :-)

Paul

User avatar
bensimmo
Posts: 4623
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: while loop question

Sat Nov 18, 2017 7:56 am

DPaul wrote:
Sat Nov 18, 2017 7:12 am
....

Maybe i'll post a picture when it is ready :-)

Paul
I think that's actually obligatory ;-)

Davies
Posts: 150
Joined: Sat Apr 04, 2015 4:24 pm

Re: while loop question

Sat Nov 18, 2017 3:10 pm

DPaul wrote:
Thu Nov 16, 2017 6:39 am
The if statement inside the loop, is what i am trying to avoid, because i need many,
for different situations.

I seem to observe that inside a loop, response to button-presses is not so snappy.

The "break" idea is something that i am going to follow-up on,
because i can get the different events to produce the same trigger
that stops the while loop.

Let's try that. 8-)
thx,
Paul
looks to me like you need threading
heres an example of threading using a stopwatch in tkinter, you can run this from windows etc.

Code: Select all

from tkinter import *
import threading
import time

master = Tk()
timer = 0
pause_time = 0
paused = 0
start_time = 0
started = 0
pause_began = 0

canvas = Canvas(width=1280, height=720, bg='black')
canvas.grid(rowspan=5, columnspan=8, sticky='W,E,N,S')


def background_math():
    global timer
    while 1:
        if started == 1 and paused == 0:
            now = time.time()
            timer = (now - start_time) - pause_time
        time_label.config(text=timer)
        master.update_idletasks()
        time.sleep(0.1)


def start_timer():
    global start_time, timer, pause_time, started, paused, pause_began
    timethisdef = time.time()
    if started == 0:
        start_time = time.time()
        start_button.config(text="Pause")
        started = 1
    else:
        if started == 1 and paused == 0:
            pause_began = time.time()
            start_button.config(text="Resume")
            paused = 1
        else:
            if started == 1 and paused == 1:
                pause_end = time.time()
                pause_time += (pause_end - pause_began)
                start_button.config(text="Pause")
                paused = 0
    master.update_idletasks()
    deftimefin = time.time()
    mathtime = deftimefin - timethisdef
    print(mathtime)


def stop_timer():
    global start_time, timer, pause_time, started, paused
    timer = 0
    pause_time = 0
    paused = 0
    start_time = 0
    started = 0
    start_button.config(text="Start")
    master.update()


start_button = Button(master, text="Start", command=start_timer, width=5, font=('aharoni', 18, 'bold'),
                      bg='orange', fg='white')
start_button.grid(padx=10, pady=10, row=4, column=0, sticky='W,E,N,S')

stop_button = Button(master, text="Stop", command=stop_timer, width=5, font=('aharoni', 18, 'bold'),
                     bg='orange', fg='white')
stop_button.grid(padx=10, pady=10, row=4, column=2, sticky='W,E,N,S')

time_label = Label(master, text=timer, width=10, font=('aharoni', 18, 'bold'), bg='black', fg='white')
time_label.grid(padx=5, pady=10, row=3, column=0, columnspan=4, sticky='W,E,N,S')

t = threading.Thread(target=background_math)
t.daemon = True
t.start()

master.mainloop()
the timer is worked out within its own thread, then updated to our tkinter label
within the main thread we have tkinter buttons which when pressed start, stop and pause the timer. to create the pause i put in an additional timer which started with pause and stopped with resume, this then added to itself with each pause/resume and was deducted from the run time until stop is issued then all is reset to 0

as you put "The if statement inside the loop, is what i am trying to avoid" the if statement takes hardly no time as long as it doesnt contain time.sleep() or other time delays.
ive added

Code: Select all

timethisdef = time.time()
deftimefin = time.time()
    mathtime = deftimefin - timethisdef
    print(mathtime)
to "def start_timer" so you can see how long an if statement will actually take, on my pc it takes 0.0005 - 0.001 seconds to perform the start/pause/resume script, as our clock is done by time stamp the delay will not effect the clock but could delay time stamping by amount said and the "def start_timer" statement would also delay the tkinter window as they are run within the same thread at mainloop() but the delay is minimal 0.001 secs on a pc

Return to “Beginners”