paulv
Posts: 564
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Triple function button to reboot/halt/reset/restart Pi

Fri Jun 28, 2013 2:58 pm

I have been looking for an example that would help me to differentiate between a short and a long button press. I wanted to use a short button press to reset my Pi, and a long one to halt it.

My Pi is headless and running a program 24X7. In some cases, I want to reset the application, and on others do a controlled shutdown instead of yanking the power cord.

Here is what I came up with, feel free to comment or make suggestions:

Code: Select all

#!/usr/bin/env python2.7

from time import sleep
import subprocess
import RPi.GPIO as GPIO

CHANNEL = 23 # GPIO channel 23 is on pin 16 of connector P1
# it will work on any GPIO channel

GPIO.setmode(GPIO.BCM)
GPIO.setup(CHANNEL, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# setup the channel as input with a 50K Ohm pull up. A push button will ground the pin,
# creating a falling edge.


def system_action(CHANNEL):
    print('Button press = negative edge detected on channel %s'%CHANNEL)
    button_press_timer = 0
    while True:
            if (GPIO.input(CHANNEL) == False) : # while button is still pressed down
                button_press_timer += 1 # keep counting until button is released
            else: # button is released, figure out for how long
                if (button_press_timer > 7) : # pressed for > 7 seconds
                    print "long press > 7 : ", button_press_timer
                    # do what you need to do before halting
                    subprocess.call(['shutdown -h now "System halted by GPIO action" &'], shell=True)
                elif (button_press_timer > 2) : # press for > 2 < 7 seconds
                    print "short press > 2 < 7 : ", button_press_timer
                    # do what you need to do before a reboot
                    subprocess.call(['sudo reboot &'], shell=True)
                button_press_timer = 0
            sleep(1)

GPIO.add_event_detect(CHANNEL, GPIO.FALLING, callback=system_action, bouncetime=200)
# setup the thread, detect a falling edge on channel 23 and debounce it with 200mSec

# assume this is the main code...
try:
    while True:
        # do whatever
        # while "waiting" for falling edge on port 23
        sleep (2)

except KeyboardInterrupt:
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit
The code and comments should explain it well enough to make any changes to fit your application.
If your Pi is in a "noisy" environment, you may want to add a "real" pull-up to the 3V3 rail with a 10K resistor. The internal 50K worked well enough for me.

Just in case the application and/or Debian crashed, you need another method to restart everything. This I did with a hardware solution using P6, to drive the Run/Reset input of the SOC.

Here is the circuit in LTspice:
Run-circuit.png
Run-circuit.png (18.71 KiB) Viewed 16567 times
Note that I added the components that are either on the circuit board of the Pi (R3, C1, D1 and D2), or internal in the SOC (R1).
The circuit works as follows. The same reset button as above is used to create a low for this circuit as well. The button is simulated with V1. When the button is pushed, M2 is released and C2 is charging through R2, providing a time constant. When the voltage over C2 increases, M1 conducts, resulting in a short for the P6 jumper, which is connected to the Run pin of the SOC. When the push button is released, the cycle ends, M1 releases the Run line again which (re)starts the SOC chip.

In this simulation, R2 and C2 have been selected to create a ramp-up of 12 seconds.
If you build this circuit, you may have to tune the value of R2, because there are a number of components that will have an effect on the time constant. Especially the VGS parameters of the MOSFETS, and the toleration of R2 and C2 of course. There is enough of a margin built-in, but it makes sense to stick to the 2, 7 and 12 second intervals with 5 second margins.

So, in the complete setup, pressing the push button anything less than 2 seconds does nothing (to prevent accidental pushes), anything between 2 seconds and less than 7, produces a software reboot. Pressing the button anywhere from 7 seconds to less than 12 produces a system Halt (for a safe power down), and pressing the push button longer than 12 seconds produces a hardware reset.
Below is the simulation:
Run-circuit-sim.png
Run-circuit-sim.png (9.25 KiB) Viewed 16567 times
The Green trace is at the Gate of M1, Blue is at P6-1, the Run signal.
One unfortunate by-product of this circuit is that there is always a current flowing through R2 and M2. Because of the value of R2, it is in the uA range, so we don't steal too much away from the 50mA limit on the 3V3 supply.

Here is one nice side-effect of this circuit. If you halted the Pi with the 7 second option, you can later restart it again by pressing the button for 12+ seconds again. Toggling the Run pin will wake the Pi up from it's beauty sleep.

One final word of caution, Although the software portion has been implemented and tested, I have not built the actual hardware circuit yet, and thus have not tested it. I did test the waking-up from a Halt though, that works fine.
Last edited by paulv on Thu Jul 11, 2013 6:30 am, edited 2 times in total.

paulv
Posts: 564
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Tripple function button to reboot/halt/reset/restart Pi

Sun Jun 30, 2013 5:30 am

In the meantime, I built the circuit, but had to make a small change. It turned out that the NMOS FET's I selected for the simulation had a low Gate-Drain voltage (VGS). By sheer luck that worked in LTspice.

When I built it, it did not work right away. I'm using BS170 MOSFETS, and their VGS is specified to be between 0.8 and 3V. That's too close to the 3V3 supply rail I wanted to use. As you can see on the scope screen below, M1 switches at 2V. The voltage developing over C2 did not get high enough to switch M1. As you can see from the circuit below, I had to use the 5V supply to feed the R/C network and supply enough of a VGS for M1.
Run Circuit.png
Run Circuit.png (17.25 KiB) Viewed 16559 times
Run Scope.jpg
Run Scope.jpg (42.07 KiB) Viewed 16559 times
The result on the scope shows the 12 sec period on the gate of M1 and the voltage on P6-1. Although the turn-on of M1 is rather slow, it does not matter for our purpose, so we can built this nice addition with only 4 components.
Last edited by paulv on Sun Jun 30, 2013 3:01 pm, edited 1 time in total.

paulv
Posts: 564
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Tripple function button to reboot/halt/reset/restart Pi

Sun Jun 30, 2013 12:13 pm

It would be nice to have a visual indication of the action that the button takes while being pressed.
Here is a small example program that will use the PWM function to blink a LED in different frequencies.
The LED is connected to a GPIO channel with a resistor to the 5V (can be the 3V3 too if you keep within the 50mA limits), then the LED, then the GPIO channel. Driving the GPIO channel LOW will turn on the LED.

Code: Select all

#!/usr/bin/env python2.7

from time import sleep
import subprocess
import RPi.GPIO as GPIO

BUTTON = 11 # GPIO channel 11 is on pin 23 of connector P1
# it will work on any GPIO channel
RESET_LED = 9

GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# setup the channel as input with a 50K Ohm pull up. A push button will ground the pin,
# creating a falling edge.
GPIO.setup(RESET_LED, GPIO.OUT)
GPIO.output(RESET_LED, GPIO.HIGH)


def system_action(BUTTON):
    print('Button press = negative edge detected on channel %s'%BUTTON)
    button_press_timer = 0
    while True:
            if (GPIO.input(BUTTON) == False) : # while button is still pressed down
                button_press_timer += 1 # keep counting until button is released
                if button_press_timer == 13:
                    print "h/w reset"
                    Warning = GPIO.PWM(RESET_LED, 30) # 30 Hz
                    Warning.start(50)  # start PWM with a duty cycle of 50%
                elif button_press_timer == 8 :
                    print "halt"
                    Warning = GPIO.PWM(RESET_LED, 10) # 10 Hz
                    Warning.start(50) 
                elif button_press_timer == 6 :
                    print "reboot"
                    Warning = GPIO.PWM(RESET_LED, 5) # 5 Hz
                    Warning.start(50)
                elif button_press_timer == 3 :
                    print "activated"
                    Warning = GPIO.PWM(RESET_LED, 1) # 1 Hz
                    Warning.start(50)
            else: # button is released, figure out for how long
                if (button_press_timer > 7) : # pressed for > 7 seconds
                    print "long press > 7", button_press_timer
                    #subprocess.call(['shutdown -h now "System halted by GPIO action" &'], shell=True)
                elif (button_press_timer > 2) : # press for > 2 < 7 seconds
                    print "short press > 2 < 7", button_press_timer
                    #subprocess.call(['sudo reboot &'], shell=True)
                button_press_timer = 0
                Warning.stop() # stop the PWM
            sleep(1) # 1 sec delay so we can count seconds


GPIO.add_event_detect(BUTTON, GPIO.FALLING, callback=system_action, bouncetime=200)
# setup the thread, detect a falling edge on channel 23 and debounce it with 200mSec

# assume this is the main code...
try:
    while True:
        # do whatever
        # while "waiting" for falling edge on port 23
        sleep (2)

except KeyboardInterrupt:
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit

Here is the schematic:
Run Circuit.png
Run Circuit.png (23.41 KiB) Viewed 15653 times
Note that I no longer relied on the pull-up of the GPIO port to pull the Gate of Q1 high. It was not reliable enough during booting.
Last edited by paulv on Fri Aug 30, 2013 9:25 am, edited 1 time in total.

catmaker
Posts: 50
Joined: Thu May 24, 2012 8:32 am

Re: Triple function button to reboot/halt/reset/restart Pi

Mon Aug 05, 2013 2:39 am

Uh, I' am interested in this because I run my Pi headless too.
I've a peripheral which gives a "heartbeat" pulse (through a current limiting resistor) to GPIO4 but it's functionally useless for now. I would like to double this GPIO4 function input with a button that when pressed for 4 seconds overriding the heartbeat pulse, to put the Pi into shutdown mode.
Thanks. (Will follow up soon).

Prime73
Posts: 2
Joined: Tue Aug 27, 2013 2:02 pm

Re: Triple function button to reboot/halt/reset/restart Pi

Tue Aug 27, 2013 3:33 pm

Trying to build the same circuit. Please correct me if I'm wrong but the circuit assumes i initial High level on GPIO to operate properly. for a some reason on my RPi upon boot I don't see any ports in High state:

Code: Select all

+----------+-Rev2-+------+--------+------+-------+
| wiringPi | GPIO | Phys | Name   | Mode | Value |
+----------+------+------+--------+------+-------+
|      0   |  17  |  11  | GPIO 0 | IN   | Low   |
|      1   |  18  |  12  | GPIO 1 | IN   | Low   |
|      2   |  27  |  13  | GPIO 2 | IN   | Low   |
|      3   |  22  |  15  | GPIO 3 | IN   | Low   |
|      4   |  23  |  16  | GPIO 4 | IN   | Low   |
|      5   |  24  |  18  | GPIO 5 | IN   | Low   |
|      6   |  25  |  22  | GPIO 6 | IN   | Low   |
|      7   |   4  |   7  | GPIO 7 | IN   | Low   |
|      8   |   2  |   3  | SDA    | ALT0 | High  |
|      9   |   3  |   5  | SCL    | ALT0 | High  |
|     10   |   8  |  24  | CE0    | ALT0 | High  |
|     11   |   7  |  26  | CE1    | ALT0 | High  |
|     12   |  10  |  19  | MOSI   | ALT0 | Low   |
|     13   |   9  |  21  | MISO   | ALT0 | Low   |
|     14   |  11  |  23  | SCLK   | ALT0 | Low   |
|     15   |  14  |   8  | TxD    | ALT0 | High  |
|     16   |  15  |  10  | RxD    | ALT0 | High  |
|     17   |  28  |   3  | GPIO 8 | IN   | Low   |
|     18   |  29  |   4  | GPIO 9 | IN   | Low   |
|     19   |  30  |   5  | GPIO10 | IN   | Low   |
|     20   |  31  |   6  | GPIO11 | IN   | Low   |
+----------+------+------+--------+------+-------+
My circuit is connected to GPIO4 (pin 16). Since it's in low state I can't boot with the circuit connected. Please help

paulv
Posts: 564
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Triple function button to reboot/halt/reset/restart Pi

Wed Aug 28, 2013 7:26 am

Just in general, I never use GPIO-4 unless it is for a W1 device, like a DS18B20 temperature sensor.
I also do not use the the UART and i2c interface pins because of their pull-up's, nor GPIO 1, the hardware PWM output.

The Pi should boot normally with the circuit connected.
A pull-up on a GPIO pin only gets activated when your program tells it to.
My circuit is always connected to my Pi when I test and develop code, even if I don't use the program to control the extra functions, because I can always halt the Pi by pushing the button for about 15 seconds.

I suggest you carefully inspect your circuit, and if you can, measure the voltage on J6. It should be close to the 3V3 rail voltage. (D1 on the Pi PCB will keep it at the 3V3 rail level)
If significantly lower, something is wrong with your circuit.
As a precaution, you can pull the Gate of Q1 high with a resistor (10..100K), to make sure it is high, so it drives the Gate of Q2 low, shortening C1 to ground. With the Gate of Q2 close to ground, the Drain of Q2 should be at the 3V3 level.

Make sure that only the drain of Q2 is connected to J6 pin 1 (closest to the edge of the Pi PCB). C2, R3 and D1 and D2 are only in the schematic to show the whole circuit, these 4 parts should not be a part of your circuit.

Finally, are you using BS170 FET's or other devices? You need to watch out for the G-D voltage level since we're working with a fairly low voltage.

Good luck

Prime73
Posts: 2
Joined: Tue Aug 27, 2013 2:02 pm

Re: Triple function button to reboot/halt/reset/restart Pi

Wed Aug 28, 2013 9:03 pm

thanks for the comment. I use BS170 mosfets as well. here is a my version of the schematic.

Image

Initially I didn't have an external pull up resistor R7 and upon boot I had 0V coming in to Q1 which triggers the circuit, voltage on JP6 would just gradually drop to 0 and Pi wouldn't boot. Adding R7 solved the problem and all seems to work as expected.

paulv
Posts: 564
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: Triple function button to reboot/halt/reset/restart Pi

Thu Aug 29, 2013 3:46 am

Well done!
When I have some time later this week I'll update my schematic and description, because you can't rely on the Gate of Q1 "floating" as it now seems.

Thanks for the information!

blokkendoos
Posts: 1
Joined: Tue Oct 18, 2016 6:23 pm

Re: Triple function button to reboot/halt/reset/restart Pi

Tue Oct 18, 2016 6:37 pm

paulv wrote:Well done!
When I have some time later this week I'll update my schematic and description, because you can't rely on the Gate of Q1 "floating" as it now seems.
Thanks for the information!
Thanks for this elegant solution! I use it for my RPi home theater. The design files of which, including the Eagle schematics for the triple state button, can be downloaded from Thingiverse.

EDIflyer
Posts: 20
Joined: Fri May 26, 2017 9:05 pm

Re: Triple function button to reboot/halt/reset/restart Pi

Sun Jul 23, 2017 10:03 pm

Really helpful code, paulv - thanks! Now correctly detected long and short button presses in my script :D

Return to “Automation, sensing and robotics”