Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have created a mini script to reboot my Raspberry Pi upon the push of a button. The script simply uses wiringPi (gpio command) to set pin 0 (pin 17 in Raspberry Pi standard numbering order) to input, and then reads the value until it's one (that's, when the button is pushed or held down).

Here's my script:

gpio mode 0 in

while (true)
do
        if [ `gpio read 0` -eq 1 ]
        then
                echo password | sudo -S reboot
                break
        fi
done &

The script works fine and everything.

However, for those of you not familiar with the Pi, it comes with very limited hardware resources (including 512 MB of memory) which can be easily consumed by a loop like the one I'm using.

What I'm trying to achieve here is to find another way for bash to find out when the value has changed from 0 to 1 without having to dedicate a more like an unconditional loop for it. Is this doable? Please share your ideas.

share|improve this question
3  
Have you considered using interrupts to handle hardware event or is it absolutely impossible in your case? adafruit.com/blog/2013/03/29/… –  lgeorget Jul 17 '13 at 0:19
3  
I would just add a sleep call which should restrict memory consumption –  strugee Jul 17 '13 at 0:23
    
@lgeorget interrupts would be ideal, probably not handled from bash though. –  jordanm Jul 17 '13 at 0:23
    
This loop does not consume memory. –  OrangeDog Jul 17 '13 at 8:41
add comment

2 Answers

up vote 9 down vote accepted

Analysis

The script is a busy loop: it keeps reading the GPIO pins over and over. It doesn't consume much memory but it keeps the CPU busy.

You should set the GPIO pin in edge mode. The WiringPi author is considering adding a wait command to the gpio utility but currently there is no way to react to an edge trigger from a shell script.

A Python solution

There is a Python library for GPIO access, which supports edge mode. Here's some completely untested Python code that should do what you want.

#!/usr/bin/env python
import os
from RPi import GPIO
GPIO.wait_for_edge(0, GPIO.RISING)
system("sudo reboot")

Additional shell tips

(true) could be written just true. The parentheses create a subprocess, which is completely unnecessary.

`gpio read 0` should be in double quotes. Without quotes, the output of the command is treated as a list of file name wildcard patterns. With double quotes, the output of the command is treated as a string. Always put double quotes around command substitutions and variable substitutions: "$(some_command)", "$some_variable". Also, you should use the syntax $(…) rather than `…`: it has exactly the same meaning, but the backquote syntax has some parsing quirks when the command is complex. Thus: if [ "$(gpio read 0)" -eq 1 ]

Don't put the root password in the script. If the script is running as root, you don't need sudo at all. If the script isn't running as root, then give the user running the script the permission to run sudo reboot without supplying a password. Run visudo and add the following line:

userwhorunsthescript ALL = (root) NOPASSWD: /sbin/reboot ""

Note that if there's an entry for the same user in the sudoers file that requires a password, the NOPASSWD entry must come after.

Once you trigger a reboot, you don't need to break the loop, the system will stop anyway.

If you decide to keep using this shell script, here's an improved version which only checks the button state every second. Note that since the pin is only read once per second, this means you need to keep the button pressed for at least one second to be sure that the event is picked up.

gpio mode 0 in
while sleep 1; do
    if [ "$(gpio read 0)" -eq 1 ]; then
        reboot
    fi
done &
share|improve this answer
1  
For your last example, you could sleep for a fraction of a second. Something like 0.1 or maybe 0.2 should be able to detect very short presses and still leave plenty of CPU time for other threads. –  Bob Jul 17 '13 at 4:10
    
@Bob: While portability likely doesn't matter in this case, sleep(1)'s acceptance of a fractional number of seconds is non-standard. –  user43216 Jul 17 '13 at 8:26
add comment

What you have is known as a busy loop. Your loop will not consume hardly any memory, but it will consume plenty of CPU. This is typically mitigated by adding sleep to the body of the loop.

while (true)
do
        if [ `gpio read 0` -eq 1 ]
        then
                echo passowrd | sudo -S reboot
                break
        fi
        sleep 1
done &

Getting rid of the busy loop will depend on what gpio does. There are system calls such as select(), which can block until a file descriptor is ready.

As far as efficiency, the () around the true command actually executes true in a subshell. This is not needed, and can be better expressed with the following:

while (( $(gpio read 0) != 1 )); do
    sleep 1
done
echo passowrd | sudo -S reboot
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.