Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows fix for 2 digit com port numbers #6

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,40 @@ There is **lots** of bugs. I know there is. I just don't know which are they.

### Platform support

* **Linux**: the initially supported platform, the one I used. Probably the less
buggy one.
* **MacOS**: although I never tried it on MacOS, it is similar to Linux and some
patches were submitted to me, so I guess it is OK
* **Windows**: it seems to be working for some people, not working for some
others. Theoretically there should be a way to get it done.
Open, read, and write, etc... works on Windows, Linux and Mac OS X.

Using PhpSerial, you can program cross-platform PHP code for Serial Ports without worrying about which OS its run on.

<?php

require_once 'PhpSerial.php';

$serial = new PhpSerial;

// can provide a serial port number here, and it'll figure out the correct name for the serial port on the current operating system
$serial->deviceSet(11);

// you can configure the port
$serial->confBaudRate(115200);
$serial->confParity("none");
$serial->confCharacterLength(8);
$serial->confStopBits(1);
$serial->confFlowControl("none");

// takes care of the correct mode string too
$serial->deviceOpen();

// can use output buffering for messages to write to serial
ob_start();
?>
<html/>
<?php

$serial->sendMessage(ob_get_clean());
// make sure message is written immediately
$serial->serialflush();

?>

### Concerns

Expand Down
93 changes: 74 additions & 19 deletions src/PhpSerial.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
* @thanks Alec Avedisyan for help and testing with reading
* @thanks Jim Wright for OSX cleanup/fixes.
* @copyright under GPL 2 licence
*
*
*
*/
class PhpSerial
{
Expand Down Expand Up @@ -67,26 +70,33 @@ public function PhpSerial()
exit();
}
}

//
// OPEN/CLOSE DEVICE SECTION -- {START}
//

/**
* Device set function : used to set the device name/address.
* Device set function : used to set the device name/address/number.
*
* You can set the number(9, 11, etc..) of the serial port and it'll figure out the name/address for you
* in the format that the current OS accepts (so your app doesn't have to worry about it).
*
* Alternatively, provide the device name in one of these formats
* -> linux : use the device address, like /dev/ttyS0
* -> osx : use the device address, like /dev/tty.serial
* -> windows : use the COMxx device name, like COM1 (can also be used
* with linux)
*
* -> windows : use the COMn device name, like COM1 (can also be used with linux)
* @see $_os - tell current OS
*
* @param string $device the name of the device to be used
* @return bool
*/
public function deviceSet($device)
{
if ($this->_dState !== SERIAL_DEVICE_OPENED) {
if ($this->_os === "linux") {
if (preg_match("@^COM(\\d+):?$@i", $device, $matches)) {
if (is_integer($device)) {
$device = "/dev/ttyS$device";
} else if (preg_match("@^COM(\\d+):?$@i", $device, $matches)) {
$device = "/dev/ttyS" . ($matches[1] - 1);
}

Expand All @@ -97,23 +107,30 @@ public function deviceSet($device)
return true;
}
} elseif ($this->_os === "osx") {
if ($this->_exec("stty -f " . $device) === 0) {
if ($this->_exec("stty -f " . $device) === 0) {
$this->_device = $device;
$this->_dState = SERIAL_DEVICE_SET;

return true;
}
} elseif ($this->_os === "windows") {
if (preg_match("@^COM(\\d+):?$@i", $device, $matches)
and $this->_exec(
exec("mode " . $device . " xon=on BAUD=9600")
) === 0
) {
$this->_winDevice = "COM" . $matches[1];
$this->_device = "\\.com" . $matches[1];
$this->_dState = SERIAL_DEVICE_SET;

return true;
if (is_integer($device)) {
$device = "COM$device";
}
if (preg_match("@^COM(\\d+):?$@i", $device, $matches)) {
// check if port is readable
if ($this->_exec(exec("mode " . $device . " xon=on BAUD=9600"))===0) {
$this->_winDevice = "COM" . $matches[1];
// Using the COMn: notation only works for COM1: through COM9: (not COM10:, COM11: etc...)
// Instead, use the \\.\COMn notation which works for any number
// (I think its a realpath parser bug, this avoids hiting it)
$this->_device = "\\\\.\\COM" . $matches[1];
$this->_dState = SERIAL_DEVICE_SET;

return true;
} else {
trigger_error("Serial port is busy", E_USER_WARNING);
}
}
}

Expand All @@ -136,6 +153,15 @@ public function deviceSet($device)
*/
public function deviceOpen($mode = "r+b")
{
// need to add `b` for Windows to open in binary mode (so Windows won't change any byte values, same behavior as Linux)
// make sure `b` is added if user didn't (user shouldn't have to worry about it)
if ($mode=="r") {
$mode = "rb";
} elseif ($mode=="r+") {
$mode = "r+b";
} elseif ($mode=="w") {
$mode = "wb";
}
if ($this->_dState === SERIAL_DEVICE_OPENED) {
trigger_error("The device is already opened", E_USER_NOTICE);

Expand All @@ -160,10 +186,21 @@ public function deviceOpen($mode = "r+b")
return false;
}

$this->_dHandle = @fopen($this->_device, $mode);
$this->_dHandle = fopen($this->_device, $mode);

if ($this->_dHandle !== false) {
stream_set_blocking($this->_dHandle, 0);

if ($this->_os === "windows") {
// need this on Windows (unknown for Linux, OSX)
//
// without this, have to wait for 8K to fill the buffer before any fread() call returns
//
// Serial-over-USB (few have a genuine serial port anymore) such as the FTDI driver (used in Arduinos, and a bunch of other boards)
// uses 64 byte packets to transport over USB ... therefore reading chunk sizes that aren't divisble by 64 may result in delay
stream_set_chunk_size($this->_dHandle, 64);
}

$this->_dState = SERIAL_DEVICE_OPENED;

return true;
Expand Down Expand Up @@ -562,6 +599,24 @@ public function sendMessage($str, $waitForReply = 0.1)

usleep((int) ($waitForReply * 1000000));
}

/**
* Reads a line from the serial port
*
* @param int $max_length optional maximum number of bytes to read ... will read up to this size, or until end of line or until end of stream
* @param String $delim optional deliminator/end of line char(s)
*
* @return
*/
public function readPortLine($max_length=10000, $delim="\n") {
if ($this->_dState !== SERIAL_DEVICE_OPENED) {
trigger_error("Device must be opened to read it", E_USER_WARNING);

return false;
}

return stream_get_line($this->_dHandle, $max_length, $delim);
}

/**
* Reads the port until no new datas are availible, then return the content.
Expand Down Expand Up @@ -602,7 +657,7 @@ public function readPort($count = 0)
} elseif ($this->_os === "windows") {
// Windows port reading procedures still buggy
$content = ""; $i = 0;

if ($count !== 0) {
do {
if ($i > $count) {
Expand Down