machinateur/phpduino

用于通过USB串行通信在PHP和Arduino之间进行数据传输的用户空间流包装器实现。

0.2.0 2023-12-06 22:47 UTC

This package is auto-updated.

Last update: 2024-09-10 22:41:46 UTC


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

文档

相关链接

以下是一些相关文档、文章和论坛帖子的链接...(点击)

睡眠定时器

即,在打开设备句柄之前睡眠的时间(秒)。流上下文中的选项是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 许可。