我创建了一个迷你脚本,只需按一下按钮即可重新启动我的 Raspberry Pi。该脚本只是使用 WiringPi(gpio 命令)将引脚 0(按 Raspberry Pi 标准编号顺序的引脚 17)设置为输入,然后读取值直到为 1(即按下或按住按钮时)。
这是我的脚本:
gpio mode 0 in
while (true)
do
if [ `gpio read 0` -eq 1 ]
then
echo password | sudo -S reboot
break
fi
done &
该脚本运行良好,一切都很好。
然而,对于那些不熟悉 Pi 的人来说,它配备的硬件资源非常有限(包括 512 MB 内存),很容易被我正在使用的循环所消耗。
我在这里想要实现的是找到另一种方法,让 bash 找出值何时从 更改为 ,0
而1
不必为其提供更像无条件循环的功能。这可行吗?请分享您的想法。
答案1
分析和现代解决方案
该脚本是一个繁忙的循环:它不断地一遍又一遍地读取 GPIO 引脚。它不会消耗太多内存,但会让CPU保持忙碌。
您应该将 GPIO 引脚设置为边缘模式。这gpio
公用事业有一个wfi
(等待中断)命令,可用于对边沿触发做出反应。 (gpio wfi
提出问题时不存在。)
set -e
gpio mode 0 in
gpio wfi 0 rising
echo password | sudo -S reboot
Python 解决方案
有一个用于 GPIO 访问的 Python 库,支持边缘模式。这里有一些完全未经测试的 Python 代码,应该可以满足您的要求。
#!/usr/bin/env python
import os
from RPi import GPIO
GPIO.wait_for_edge(0, GPIO.RISING)
system("sudo reboot")
额外的 shell 提示
(true)
可以写成true
.括号创建了一个子进程,这是完全没有必要的。
`gpio read 0`
应该用双引号引起来。如果没有引号,命令的输出将被视为文件名通配符模式的列表。使用双引号时,命令的输出将被视为字符串。始终在命令替换和变量替换两边加上双引号:"$(some_command)"
, "$some_variable"
。另外,您应该使用语法$(…)
而不是`…`
: 它具有完全相同的含义,但是当命令很复杂时,反引号语法有一些解析怪癖。因此:if [ "$(gpio read 0)" -eq 1 ]
不要将 root 密码放入脚本中。如果脚本以 root 身份运行,则根本不需要 sudo。如果脚本不是以 root 身份运行,则授予运行脚本的用户sudo reboot
无需提供密码即可运行的权限。运行visudo
并添加以下行:
userwhorunsthescript ALL = (root) NOPASSWD: /sbin/reboot ""
请注意,如果 sudoers 文件中同一用户的条目需要密码,则该NOPASSWD
条目必须位于后面。
一旦触发重新启动,您不需要打破循环,系统无论如何都会停止。
如果您决定继续使用此 shell 脚本,并且您的版本gpio
太旧而无法使用wfi
子命令,那么这里有一个改进的版本,它仅每秒检查按钮状态。请注意,由于引脚每秒仅读取一次,这意味着您需要按住按钮至少一秒钟以确保事件被拾取。
gpio mode 0 in
while sleep 1; do
if [ "$(gpio read 0)" -eq 1 ]; then
reboot
fi
done &
答案2
你所拥有的被称为忙循环。你的循环几乎不会消耗任何内存,但会消耗大量的CPU。这通常可以通过添加sleep
到循环体来缓解。
while (true)
do
if [ `gpio read 0` -eq 1 ]
then
echo passowrd | sudo -S reboot
break
fi
sleep 1
done &
摆脱繁忙循环将取决于gpio
做什么。有些系统调用(例如select()
)可能会阻塞,直到文件描述符准备就绪。
就效率而言,()
aroundtrue
命令实际上是true
在子 shell 中执行的。这不是必需的,可以用以下方式更好地表达:
while (( $(gpio read 0) != 1 )); do
sleep 1
done
echo passowrd | sudo -S reboot
答案3
请尝试以下操作:
while ! gpio read 0 ; do
sleep 1
done
echo password | sudo -S reboot