Detecting a Button Press
Created by Franck Albinet with source available here. Also presented by NSRC at APRICOT 2018.
Button: Detecting Pysense board button pressure
Introduction
In this example, we will detect the pressure of the button on the Pysense board. Because the board does not have a keyboard (or mouse), we will use the USB connection between the board and the development PC to detect the pressure of the button.
The Pysense board has one button as shown at the bottom in the picture below.
Learning outcomes
You will learn how to: detect the pressure of the button; implement Polling and Interrupts techniques * tackle iteratively a coding "challenge" (from naive but functioning programs to implementations fostering reusability and modularity)
Required Components
For this example you will need:
- a LoPy module plugged into a Pysense board
- a microUSB cable
- a development PC
The source code is in the src/button
directory. > Make sure you press the button on the Pysense board (highlighted by a red rectangle in image above) and not the one on the LoPy module (reset button).
boot.py
from machine import UART import os uart = UART(0, 115200) os.dupterm(uart)
The boot.py file should always start with the above code, so we can run our python scripts over Serial or Telnet. Newer Pycom boards have this code already in the boot.py file.
Polling button state
Even for simple program like this one, there are numerous possible implementations. We should aim for a straighforward solution first. Premature optimization or abstraction are common pitfalls.
The use case we want to address is the following: 1. when the button of the Pysense board is pressed, display a message "Button pressed" once in Atom's console; 2. when released, display a message "Button released" once.
main.py
from machine import Pin import time button = Pin("P14", mode=Pin.IN, pull=Pin.PULL_UP) is_pressed = False while True: if button() == 1 and not is_pressed: time.sleep(1) elif button() == 0 and not is_pressed: print("Button pressed") is_pressed = True elif button() == 1 and is_pressed: print("Button released") is_pressed = False else: pass
Let's go through the snippet code above:
# we import the Pin `class` from machine Pycom modules from machine import Pin # then we create a `pin` object button = Pin("P14", mode=Pin.IN, pull=Pin.PULL_UP)
You notice that to create a Pin
object we need to specify three arguments:
- pin's
id
"P14"
- see Pysense board manual: https://docs.pycom.io/chapter/firmwareapi/pycom/machine/Pin.html - pin's mode
Pin.IN
specifying that this is an input - pull method
Pin.PULL_UP
specifying we want a pull-up resistor. Loosely speaking, a pull-up or pull-down resistor will ensure that the pin is in either a high or low state, while also using a low amount of current and as a result prevents unknown state of the input. You can consult this blog post for further information https://learn.sparkfun.com/tutorials/pull-up-resistors.
Now that we have a button object, let's read repeatedly the state of the input. In our case when not pressed the button's value button()
should be 1 and 0 when pressed (pull-up resistor).
# flag saving previous state of the button is_pressed = False # we keep reading the input value until the program is terminated while True: # we do nothing until the input value is changed for the first time if button() == 1 and not is_pressed: time.sleep(1) # when pressed and was not previously pressed elif button() == 0 and not is_pressed: print("Button pressed") is_pressed = True # if not pressed and was pressed previously elif button() == 1 and is_pressed: print("Button released") is_pressed = False else: pass
To read the value/state of the button, simply call button()
.
The flag is_pressed
allows to display messages once otherwise the message would be printed as long as you press or release the button which is not the expected use case.
This is important to note that in the implementation above, we keep reading the state of the button potentially every 100μs given that the clock rate is 160MHz. This is most probably a waste of CPU time and energy (remember that low consumption is key in IoT world) given that button pressure might not be a so much frequent event and that we might not require a response time of micro seconds.
A first slight improvement could be to pause the execution for half a second for instance: time.sleep_ms(500)
(insert it somewhere in the while
loop into main.py
).
Exercises
Toggle the LED each time the button is pressed. If the LED is OFF, turn it ON by pressing the button. If the LED is ON, turn it OFF by pressing the button.
Increase a counter every time the button is pressed and visualize it.
Turn the red light on whenever the button is pressed for more than 3 seconds.