ReactPHP:如何实现永久循环以监控sysfs GPIO输入?
解决ReactPHP监控sysfs GPIO时$loop->run()直接退出的问题
嘿,这个问题我之前也碰到过——ReactPHP的事件循环(loop)是驱动型的,它只会在有事件需要处理的时候保持运行。如果你的脚本里没有注册任何持续的事件(比如定时器、流监听、信号回调),$loop->run()一启动就会发现“没事干”,直接退出了。
要实现永久循环监控GPIO,核心就是给loop添加一个持续的事件源,让它一直有事情可做。下面给你两种靠谱的实现方式:
方式一:定时轮询GPIO状态(简单易上手)
这种方式适合入门,通过ReactPHP的定时器每隔一段时间读取一次GPIO的value文件,既能监控状态变化,又能让loop保持运行。
示例代码:
<?php require 'vendor/autoload.php'; use React\EventLoop\Factory; $loop = Factory::create(); // 替换成你的GPIO路径,比如gpio17的value文件 $gpioValuePath = '/sys/class/gpio/gpio17/value'; // 检查文件是否存在,避免报错 if (!file_exists($gpioValuePath)) { die("GPIO value file not found! Make sure GPIO is exported.\n"); } // 定义轮询间隔(单位:秒,这里是0.1秒=100毫秒) $pollInterval = 0.1; // 存储上一次的GPIO状态,用于检测变化 $lastState = null; $loop->addPeriodicTimer($pollInterval, function () use ($gpioValuePath, &$lastState) { // 读取GPIO当前状态 $currentState = trim(file_get_contents($gpioValuePath)); // 如果状态发生变化,触发处理逻辑 if ($currentState !== $lastState) { echo "GPIO state changed: " . ($currentState == 1 ? "HIGH" : "LOW") . "\n"; $lastState = $currentState; // 这里可以添加你的业务逻辑,比如发送通知、触发其他操作 } }); echo "Starting GPIO monitor... Press Ctrl+C to exit.\n"; $loop->run();
方式二:使用流监听GPIO文件(更高效)
sysfs的GPIO文件支持通过文件描述符监听变化,类似inotify,可以用ReactPHP的Stream组件来实现非阻塞监听,避免轮询的开销。
示例代码:
<?php require 'vendor/autoload.php'; use React\EventLoop\Factory; use React\Stream\ReadableResourceStream; $loop = Factory::create(); $gpioValuePath = '/sys/class/gpio/gpio17/value'; if (!file_exists($gpioValuePath)) { die("GPIO value file not found! Make sure GPIO is exported.\n"); } // 以非阻塞模式打开GPIO文件 $fd = fopen($gpioValuePath, 'r'); stream_set_blocking($fd, false); // 创建可读流 $stream = new ReadableResourceStream($fd, $loop); // 存储上一次状态 $lastState = null; // 监听流的data事件(当文件内容变化时触发) $stream->on('data', function ($data) use (&$lastState, $fd) { $currentState = trim($data); if ($currentState !== $lastState) { echo "GPIO state changed: " . ($currentState == 1 ? "HIGH" : "LOW") . "\n"; $lastState = $currentState; } // 重置文件指针到开头,因为sysfs文件读取后指针会停在末尾,下次读不到新内容 fseek($fd, 0); }); // 监听流的错误事件 $stream->on('error', function ($error) { echo "Stream error: " . $error->getMessage() . "\n"; }); // 监听进程终止信号,确保资源被正确释放 $loop->addSignal(SIGINT, function () use ($loop, $stream, $fd) { echo "\nStopping GPIO monitor...\n"; $stream->close(); fclose($fd); $loop->stop(); }); echo "Starting GPIO monitor (stream mode)... Press Ctrl+C to exit.\n"; $loop->run();
关键注意事项
- GPIO导出权限:在运行脚本前,确保你已经导出了目标GPIO(比如执行
echo 17 > /sys/class/gpio/export),并且当前用户有访问/sys/class/gpio目录的权限(可以把用户加入gpio组:sudo usermod -aG gpio your_username)。 - 为什么原来的脚本会退出:因为你的代码里没有给loop添加任何持续运行的事件,loop启动后没有任务,直接结束。只要添加了定时器或者流监听,loop就会一直运行直到你手动停止,比如Ctrl+C。
- 流监听的小细节:读取sysfs GPIO文件后,一定要把文件指针重置到开头,否则下次读取会从末尾开始,读不到新的状态变化。
内容的提问来源于stack exchange,提问作者overflaw
相关产品推荐
相关产品推荐

