You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();

关键注意事项

  1. GPIO导出权限:在运行脚本前,确保你已经导出了目标GPIO(比如执行echo 17 > /sys/class/gpio/export),并且当前用户有访问/sys/class/gpio目录的权限(可以把用户加入gpio组:sudo usermod -aG gpio your_username)。
  2. 为什么原来的脚本会退出:因为你的代码里没有给loop添加任何持续运行的事件,loop启动后没有任务,直接结束。只要添加了定时器或者流监听,loop就会一直运行直到你手动停止,比如Ctrl+C。
  3. 流监听的小细节:读取sysfs GPIO文件后,一定要把文件指针重置到开头,否则下次读取会从末尾开始,读不到新的状态变化。

内容的提问来源于stack exchange,提问作者overflaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:32:05