machinateur/phpduino
用于通过USB串行通信在PHP和Arduino之间进行数据传输的用户空间流包装器实现。
Requires
- php: >= 8.1
README
用于通过USB串行通信在PHP和Arduino之间进行数据传输的用户空间流包装器实现。
概念
本软件包定义了一个使用streamWrapper
实现的arduino://
协议处理器。它还提供了一些字节处理实用函数(byte_pack()
和byte_unpack()
)。
请注意,这个库仍在开发中,直到版本1.0
之前,请将其视为不稳定版本。在此之前,几乎任何东西都可能在没有通知的情况下更改,包括补丁版本。
需求
此软件包至少需要PHP 8.1才能运行。无依赖项。
仍需要Arduino IDE上传代码到连接的设备。
用法
// First register the protocol and stream wrapper, ... \Machinateur\Arduino\streamWrapper::register(); // ... then use the protocol in a stream function... $fd = \fopen('arduino://'.$deviceName, 'r+b'); // ... and finally do things with the $fd (fread()/fwrite() ops). // See `./example/echo` for some simple examples.
您可以使用流上下文为连接提供选项
点击此处获取以下选项的参数说明。
/** * The device's name in `/dev/`. * Prefer `cu` if on Mac, use `ls /dev/tty.*` to find the available devices. * * On windows, use the device manager to identify the correct serial (com) port, under the `COM` device group. */ if ('Darwin' === \PHP_OS_FAMILY) { $deviceName = 'tty.usbmodem2101'; $deviceName = 'cu.usbmodem2101'; } elseif ('Windows' === \PHP_OS_FAMILY) { $deviceName = 'COM7'; } else { $deviceName = ''; \trigger_error( \sprintf('No device name specified in "%s" on line "%d"!', __FILE__, __LINE__ - 3), \E_USER_ERROR); } /** * The device's baud rate. * * See Arduino docs at https://docs.arduino.cc/learn/built-in-libraries/software-serial#begin for conventional rates. * * Supported baud rates are: * - ` 300` * - ` 600` * - ` 1200` * - ` 2400` * - ` 4800` * - ` 9600` * - ` 14400` * - ` 19200` * - ` 28800` * - ` 31250` * - ` 38400` * - ` 57600` * - `115200` */ $deviceBaudRate = 9600; /** * The device's parity bit configuration. * * See Arduino docs at https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/. * - `none` = `-1` * - ` odd` = ` 1` * - `even` = ` 0` * * Default is `SERIAL_8N1`, so `N` is the data size. */ $deviceParity = -1; /** * The device's data size configuration. * * See Arduino docs at https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/. * * Default is `SERIAL_8N1`, so `8` is the data size. */ $deviceDataSize = 8; /** * The device's stop bit size configuration. * * See Arduino docs at https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/. * * Default is `SERIAL_8N1`, so `1` is the stop bit size. */ $deviceStopSize = 1; /** * The device custom command. If set, will be yielded in favor of default commands. * * Try to stop the reset on `fclose()`, see https://stackoverflow.com/a/59549518. * - `[-]hup` = send a hangup signal when the last process closes the tty * * Ideally only run with the `-hup` option if not yet set, so there will be no more restarts due to RTS HANGUP. * Only use if desired. */ $deviceCustomCommand = null;
// The stream context configuration. $context = \stream_context_create([ \Machinateur\Arduino\streamWrapper::PROTOCOL_NAME => [ 'baud_rate' => $deviceBaudRate, 'parity' => $deviceParity, 'data_size' => $deviceDataSize, 'stop_size' => $deviceStopSize, 'custom_command' => $deviceCustomCommand, // A safe threshold for the arduino to boot on `fopen()`. 'usleep_s' => 2, ], ]); $fd = \fopen('arduino://'.$deviceName, 'r+b', $context);
安装
composer require machinateur/phpduino
文档
相关链接
以下是一些相关文档、文章和论坛帖子的链接...(点击)
- https://docs.arduino.cc/learn/built-in-libraries/software-serial#begin
- https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/
- https://unix.stackexchange.com/a/138390
- https://stackoverflow.com/a/8632603
- https://playground.arduino.cc/Interfacing/LinuxTTY/
- https://forum.arduino.cc/t/linux-serial-io/38934/2
- https://web.archive.org/web/20110228183102/https://anealkhimani.com/2010/02/08/web-enabled-pantilt-webcam-with-arduino-and-php-part-1/
- https://web.archive.org/web/20110217155443/http://anealkhimani.com/2010/02/20/web-enabled-pantilt-web-came-with-arduino-and-php-part-2/
- https://web.archive.org/web/20110217070336/http://anealkhimani.com/2010/02/21/web-enabled-pantilt-camera-with-arduino-and-php-part-3/
- https://github.com/Xowap/PHP-Serial/blob/develop/src/PhpSerial.php
- https://man7.org/linux/man-pages/man1/stty.1.html
- https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/mode
- https://unix.stackexchange.com/questions/242778/what-is-the-easiest-way-to-configure-serial-port-on-linux
- https://php.ac.cn/manual/en/class.streamwrapper.php
- https://stackoverflow.com/a/9616217
- https://stackoverflow.com/a/59549518
- https://stackoverflow.com/questions/32569611/linux-stty-command-lag-help-needed-on-serial-usb
- https://forum.arduino.cc/t/arduino-auto-resets-after-opening-serial-monitor/850915
- https://forum.arduino.cc/t/using-php-to-control-the-arduino-over-usb-serial-connection/134478/9
- https://raspberrypi.stackexchange.com/questions/36490/stty-command-lag-and-queue-issue
- https://raspberrypi.stackexchange.com/questions/9695/disable-dtr-on-ttyusb0
- https://stackoverflow.com/a/957416
睡眠定时器
即,在打开设备句柄之前睡眠的时间(秒)。流上下文中的选项是usleep_s
。
对于Windows,此超时在流包装器内部打开设备之前应用,但在配置之后,而在Mac/Linux上,超时发生在内部打开设备之后,但在使用命令配置之前。
在我的测试中,我可以用到1.618119
,但低于这个值,成功率开始随机下降。使用的线和端口也可能起作用。
需要超时,因为Linux中必须占用句柄,以避免配置提前重置。同样,在Windows上,配置只能在句柄被占用之前应用,因此在此之后设备处于忙状态。
流块大小
事实证明,这是关键,因为Windows上表示为文件流的串行(com)端口
如果流以缓冲区方式读取,并且它不表示一个普通文件,则最多进行一次读取,读取的字节数最多等于块大小(通常是
8192
);根据先前缓冲的数据,返回的数据大小可能大于块大小。
当移除这个调用并监控在调用 \fread($fd, 1)
时的 streamWrapperAbstract::stream_read()
中的 $count
值时,可能会观察到 streamWrapper API 的未记录行为。 $count
将会是 8192
!
\stream_set_chunk_size($fd, 1);
由于 Windows 上流的始终阻塞特性,您需要使用 \stream_select()
来确定是否有更多数据可以读取或写入。请参阅文档获取说明。否则,读取操作可能会无限期地阻塞,因为似乎设备没有发送任何数据。
因此,任何读取响应的脚本都必须非常小心,以避免阻塞调用。
连接时自动重启
据多个来源称,可以禁用 DTR(数据传输就绪)线路重置,这似乎是一个挂起信号。在我的测试中,我无法在 Windows 或 Mac 上使这些选项正常工作。
对于需要精确且非重置的持续功能的应用程序,可能有一种选择是使用硬件解决方案来确保这一点。
示例
您可以在 Windows 和 Mac 上的 ./example/echo
中找到一个与以下代码一起工作的简单示例。
还有一个更复杂的示例,涉及二进制数据传输,可以在 ./example/echo/echo-binary.php
中找到。
还有一个示例,位于 ./example/echo-nonblocking
中,展示了如何使用 \stream_select()
来避免对连接设备的阻塞读写调用。
Arduino 脚本
byte incomingByte = 0; void setup() { Serial.begin(9600, SERIAL_8N1); } void loop() { if (Serial.available() > 0) { incomingByte = Serial.read(); Serial.print((char)incomingByte); } }
PHP 脚本
// Example program for "echo.ino" communication to/from Arduino via USB serial. use Machinateur\Arduino\streamWrapper; require_once __DIR__ . '/../../vendor/autoload.php'; // Register the protocol and stream wrapper. streamWrapper::register(); if ('Darwin' === \PHP_OS_FAMILY) { $deviceName = 'tty.usbmodem2101'; $deviceName = 'cu.usbmodem2101'; } elseif ('Windows' === \PHP_OS_FAMILY) { $deviceName = 'COM7'; } else { $deviceName = ''; \trigger_error( \sprintf('No device name specified in "%s" on line "%d"!', __FILE__, __LINE__ - 3), \E_USER_ERROR); } $deviceBaudRate = 9600; $deviceParity = -1; $deviceDataSize = 8; $deviceStopSize = 1; // Open the connection. Make sure the device is connected under the configured device name // and the `echo.ino` program is running. $fd = \fopen("arduino://{$deviceName}", 'r+b', false); $input = 'hello world'; $output = ''; \stream_set_chunk_size($fd, 1); \fwrite($fd, $input); do { $output .= \fread($fd, 1); // Comment in the following line to see the progress byte by byte. //echo $output.\PHP_EOL; } while ($output !== $input); echo $output, \PHP_EOL, \PHP_EOL;
打开终端并运行示例,例如 php ./echo.php
。
许可证
这是 MIT 许可。