Pi IoT In Python Using GPIO Zero - Getting Input
Written by Harry Fairhead & Mike James   
Monday, 03 June 2024
Article Index
Pi IoT In Python Using GPIO Zero - Getting Input
Polling
Interrupts and events
Event Handling

The big question is, how do you deal with input from the switch? It isn’t like output, where your program is in 100% control. The button can be pushed at any time and your program has to respond to it.

The simplest way of dealing with the problem is for your program to wait until the button is pressed or released:

wait_for_press(timeout = None)
wait_for_release(timeout = None)

The timeout can be set to the number of seconds to wait before giving up. The default value of None or 0 means your program will wait forever for the button to be pressed.

The use of press and release refer to the mechanical action of the button or switch. Of course, if the button is wired with a pull-up then press corresponds to a 0 and if it is wired as a pull-down then it corresponds to a 1. To let the software know which is which, you can use the active_state parameter when creating the Button object. If you set this to True then high corresponds to pressed and if False, low corresponds to pressed. If pull_up is anything except None, active_state is set automatically.

By default Button isn’t debounced. What this means is that what looks like a single press to you might be received as a set of press/release signals. If you don’t want to incorporate hardware debounce components then include the bounce_time parameter when you create button. This specifies a wait time in seconds before its state change will be recognized. For example:

button = Button(4,bounce_time=0.25)

creates a Button that can only be pressed 4 times per second and hence is effectively debounced. Another way to look at this is that the button will only respond to the first press in any 0.25s interval.

Polling

The problem with waiting for a button, or whatever else you use as a switch, to be pressed is that your program is frozen and nothing else can happen - it just sits there waiting for the event. Sometimes this is acceptable because there is nothing more for your program to do. More often it is the case that your program has got things to do and simply waiting isn’t an option.

An alternative to waiting for a switch to be pressed is to poll its state. Polling is a standard programming technique where, to detect a change of state, the program repeatedly checks the state at suitable intervals. You can check the state of Button using the is_pressed property or the value property. The difference is that value gives you the state of the GPIO line, i.e. 0 (low) or 1 (high) and what this means depends on the way the switch is wired, whereas is_pressed always gives True when the switch is pressed.

For example:

while True:
    do something 
    if button.is_pressed:
         do something about button pressed

In this case the polling takes the form of an infinite loop and the test for the button being pressed occurs once per loop. You can see that how often the button press is tested depends on the time the loop takes and that how long the loop gets to do something depends on how often the button is pressed and how long it takes to process the changes needed.

The idea of using a polling loop is often regarded as a poor choice, but in practice it is generally the best choice as it uses the processor to respond as fast as possible to the button press while allowing it to get on with other tasks when it isn’t. More sophisticated solutions, such as using an interrupt or event are often just a way of hiding a polling loop and tend to increase inefficiency. Polling loops are generally the best solution, but GPIO Zero does provide an alternative in the form of events.

Events and Interrupts

Interrupts and events do make some aspects of IoT programming easier, if not more efficient. But first what are interrupts and events?

An event is like a latch or a memory that something happened. Imagine that there is a flag that will be automatically set when an input line changes state. The flag is set without the involvement of software, or at least any software that you have control over. It is useful to imagine an entirely hardware-based setting of the flag, even if this is not always the case. With the help of an event, you can avoid missing an input because the polling loop was busy doing something else. Now the polling loop reads the flag rather than the actual state of the input line and hence it can detect if the line has changed since it was last polled. The polling loop resets the event flag and processes the event. Of course, it can’t always know exactly when the event happened, but at least it hasn’t missed it altogether.

A simple event can avoid the loss of a single input, but what if there is more than one input while the polling loop is unavailable? The most common solution is to create an event queue – that is, a FIFO (first in, first out) queue of events as they occur. The polling loop now reads the event at the front of the queue, processes it and reads the next. It continues like this until the queue is empty, when it simply waits for an event. As long as the queue is big enough, an event queue means you don’t miss any input, but input events are not necessarily processed close to the time that they occurred. They should be processed in order, but unless the events are time-stamped the program has no idea when they happened.

An event queue is a common architecture, but to work, or have any advantages, it needs either multiple cores. so that events can always be added to the queue before another occurs, or it needs the help of hardware, usually in the form of interrupts. Notice that an event, or an event queue, cannot increase the program’s throughput or its latency, the time to react to an input. In fact, an event queue decreases throughput and increases latency due to overheads of implementation. All an event system does is to ensure that you do not miss any input and that all input gets processed eventually.



Last Updated ( Saturday, 08 June 2024 )