Jump to content

PHP Serial Class


Recommended Posts

Hi All,

 

I am currently developing a PHP serial application and require some help with a serial read problem on windows. I am using the following class which has been developed by someone else but provides a fantastic base in which to start, i have made some modifications already and i can send data to a serial(com) port fine with no problems, the problem comes when trying to read data.

 

It seems that when I call the function it does not obtain any data at all.

 

My Call for reading data;

	$read = $serial->readPort();
	echo $read;

 

The whole code;

 

<?php
define ("SERIAL_DEVICE_NOTSET", 0);
define ("SERIAL_DEVICE_SET", 1);
define ("SERIAL_DEVICE_OPENED", 2);

/**
* Serial port control class
*
* THIS PROGRAM COMES WITH ABSOLUTELY NO WARANTIES !
* USE IT AT YOUR OWN RISKS !
*
* Changes added by Rizwan Kassim <rizwank@uwink.com> for OSX functionality
* default serial device for osx devices is /dev/tty.serial for machines with a built in serial device
*
* @author Rémy Sanchez <thenux@gmail.com>
* @thanks Aurélien Derouineau for finding how to open serial ports with windows
* @thanks Alec Avedisyan for help and testing with reading
* @copyright under GPL 2 licence
*/
class phpSerial
{
        var $_device = null;
        var $_windevice = null;
        var $_dHandle = null;
        var $_dState = SERIAL_DEVICE_NOTSET;
        var $_buffer = "";
        var $_os = "";

        /**
         * This var says if buffer should be flushed by sendMessage (true) or manualy (false)
         *
         * @var bool
         */
        var $autoflush = true;

        /**
         * Constructor. Perform some checks about the OS and setserial
         *
         * @return phpSerial
         */
        function phpSerial ()
        {
                setlocale(LC_ALL, "en_US");

                $sysname = php_uname();

                if (substr($sysname, 0, 5) === "Linux")
                {
                        $this->_os = "linux";

                        if($this->_exec("stty --version") === 0)
                        {
                                register_shutdown_function(array($this, "deviceClose"));
                        }
                        else
                        {
                                trigger_error("No stty availible, unable to run.", E_USER_ERROR);
                        }
                }
                elseif (substr($sysname, 0, 6) === "Darwin")
                {
                        $this->_os = "osx";
            // We know stty is available in Darwin. 
            // stty returns 1 when run from php, because "stty: stdin isn't a
            // terminal"
            // skip this check
//                      if($this->_exec("stty") === 0)
//                      {
                                register_shutdown_function(array($this, "deviceClose"));
//                      }
//                      else
//                      {
//                              trigger_error("No stty availible, unable to run.", E_USER_ERROR);
//                      }
                }
                elseif(substr($sysname, 0, 7) === "Windows")
                {
                        $this->_os = "windows";
                        register_shutdown_function(array($this, "deviceClose"));
                }
                else
                {
                        trigger_error("Host OS is neither osx, linux nor windows, unable tu run.", E_USER_ERROR);
                        exit();
                }
        }

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

        /**
         * Device set function : used to set the device name/address.
         * -> 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)
         *
         * @param string $device the name of the device to be used
         * @return bool
         */
        function deviceSet ($device)
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        if ($this->_os === "linux")
                        {
                                if (preg_match("@^COM(\d+):?$@i", $device, $matches))
                                {
                                        $device = "/dev/ttyS" . ($matches[1] - 1);
                                }

                                if ($this->_exec("stty -F " . $device) === 0)
                                {
                                        $this->_device = $device;
                                        $this->_dState = SERIAL_DEVICE_SET;
                                        return true;
                                }
                        }
                        elseif ($this->_os === "osx")
                        {
                                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;
                                }
                        }

                        trigger_error("Specified serial port is not valid", E_USER_WARNING);
                        return false;
                }
                else
                {
                        trigger_error("You must close your device before to set an other one", E_USER_WARNING);
                        return false;
                }
        }

        /**
         * Opens the device for reading and/or writing.
         *
         * @param string $mode Opening mode : same parameter as fopen()
         * @return bool
         */
        function deviceOpen ($mode = "r+b")
        {
                if ($this->_dState === SERIAL_DEVICE_OPENED)
                {
                        trigger_error("The device is already opened", E_USER_NOTICE);
                        return true;
                }

                if ($this->_dState === SERIAL_DEVICE_NOTSET)
                {
                        trigger_error("The device must be set before to be open", E_USER_WARNING);
                        return false;
                }

                if (!preg_match("@^[raw]\+?b?$@", $mode))
                {
                        trigger_error("Invalid opening mode : ".$mode.". Use fopen() modes.", E_USER_WARNING);
                        return false;
                }

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

                if ($this->_dHandle !== false)
                {
                        stream_set_blocking($this->_dHandle, 0);
                        $this->_dState = SERIAL_DEVICE_OPENED;
                        return true;
                }

                $this->_dHandle = null;
                trigger_error("Unable to open the device", E_USER_WARNING);
                return false;
        }

        /**
         * Closes the device
         *
         * @return bool
         */
        function deviceClose ()
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        return true;
                }

                if (fclose($this->_dHandle))
                {
                        $this->_dHandle = null;
                        $this->_dState = SERIAL_DEVICE_SET;
                        return true;
                }

                trigger_error("Unable to close the device", E_USER_ERROR);
                return false;
        }

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

        //
        // CONFIGURE SECTION -- {START}
        //

        /**
         * Configure the Baud Rate
         * Possible rates : 110, 150, 300, 600, 1200, 2400, 4800, 9600, 38400,
         * 57600 and 115200.
         *
         * @param int $rate the rate to set the port in
         * @return bool
         */
        function confBaudRate ($rate)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set the baud rate : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $validBauds = array (
                        110    => 11,
                        150    => 15,
                        300    => 30,
                        600    => 60,
                        1200   => 12,
                        2400   => 24,
                        4800   => 48,
                        9600   => 96,
                        19200  => 19,
                        38400  => 38400,
                        57600  => 57600,
                        115200 => 115200
                );

                if (isset($validBauds[$rate]))
                {
                        if ($this->_os === "linux")
                        {
                $ret = $this->_exec("stty -F " . $this->_device . " " . (int) $rate, $out);
            }
            if ($this->_os === "darwin")
            {
                $ret = $this->_exec("stty -f " . $this->_device . " " . (int) $rate, $out);
            }
            elseif ($this->_os === "windows")
            {
                $ret = $this->_exec("mode " . $this->_windevice . " BAUD=" . $validBauds[$rate], $out);
            }
            else return false;

                        if ($ret !== 0)
                        {
                                trigger_error ("Unable to set baud rate: " . $out[1], E_USER_WARNING);
                                return false;
                        }
                }
        }

        /**
         * Configure parity.
         * Modes : odd, even, none
         *
         * @param string $parity one of the modes
         * @return bool
         */
        function confParity ($parity)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set parity : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $args = array(
                        "none" => "-parenb",
                        "odd"  => "parenb parodd",
                        "even" => "parenb -parodd",
                );

                if (!isset($args[$parity]))
                {
                        trigger_error("Parity mode not supported", E_USER_WARNING);
                        return false;
                }

                if ($this->_os === "linux")
                {
                        $ret = $this->_exec("stty -F " . $this->_device . " " . $args[$parity], $out);
                }
                else
                {
                        $ret = $this->_exec("mode " . $this->_windevice . " PARITY=" . $parity{0}, $out);
                }

                if ($ret === 0)
                {
                        return true;
                }

                trigger_error("Unable to set parity : " . $out[1], E_USER_WARNING);
                return false;
        }

        /**
         * Sets the length of a character.
         *
         * @param int $int length of a character (5 <= length <= 
         * @return bool
         */
        function confCharacterLength ($int)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set length of a character : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $int = (int) $int;
                if ($int < 5) $int = 5;
                elseif ($int >  $int = 8;

                if ($this->_os === "linux")
                {
                        $ret = $this->_exec("stty -F " . $this->_device . " cs" . $int, $out);
                }
                else
                {
                        $ret = $this->_exec("mode " . $this->_windevice . " DATA=" . $int, $out);
                }

                if ($ret === 0)
                {
                        return true;
                }

                trigger_error("Unable to set character length : " .$out[1], E_USER_WARNING);
                return false;
        }

        /**
         * Sets the length of stop bits.
         *
         * @param float $length the length of a stop bit. It must be either 1,
         * 1.5 or 2. 1.5 is not supported under linux and on some computers.
         * @return bool
         */
        function confStopBits ($length)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set the length of a stop bit : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                if ($length != 1 and $length != 2 and $length != 1.5 and !($length == 1.5 and $this->_os === "linux"))
                {
                        trigger_error("Specified stop bit length is invalid", E_USER_WARNING);
                        return false;
                }

                if ($this->_os === "linux")
                {
                        $ret = $this->_exec("stty -F " . $this->_device . " " . (($length == 1) ? "-" : "") . "cstopb", $out);
                }
                else
                {
                        $ret = $this->_exec("mode " . $this->_windevice . " STOP=" . $length, $out);
                }

                if ($ret === 0)
                {
                        return true;
                }

                trigger_error("Unable to set stop bit length : " . $out[1], E_USER_WARNING);
                return false;
        }

        /**
         * Configures the flow control
         *
         * @param string $mode Set the flow control mode. Availible modes :
         *      -> "none" : no flow control
         *      -> "rts/cts" : use RTS/CTS handshaking
         *      -> "xon/xoff" : use XON/XOFF protocol
         * @return bool
         */
        function confFlowControl ($mode)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set flow control mode : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $linuxModes = array(
                        "none"     => "clocal -crtscts -ixon -ixoff",
                        "rts/cts"  => "-clocal crtscts -ixon -ixoff",
                        "xon/xoff" => "-clocal -crtscts ixon ixoff"
                );
                $windowsModes = array(
                        "none"     => "xon=off octs=off rts=on",
                        "rts/cts"  => "xon=off octs=on rts=hs",
                        "xon/xoff" => "xon=on octs=off rts=on",
                );

                if ($mode !== "none" and $mode !== "rts/cts" and $mode !== "xon/xoff") {
                        trigger_error("Invalid flow control mode specified", E_USER_ERROR);
                        return false;
                }

                if ($this->_os === "linux")
                        $ret = $this->_exec("stty -F " . $this->_device . " " . $linuxModes[$mode], $out);
                else
                        $ret = $this->_exec("mode " . $this->_windevice . " " . $windowsModes[$mode], $out);

                if ($ret === 0) return true;
                else {
                        trigger_error("Unable to set flow control : " . $out[1], E_USER_ERROR);
                        return false;
                }
        }

        /**
         * Sets a setserial parameter (cf man setserial)
         * NO MORE USEFUL !
         *      -> No longer supported
         *      -> Only use it if you need it
         *
         * @param string $param parameter name
         * @param string $arg parameter value
         * @return bool
         */
        function setSetserialFlag ($param, $arg = "")
        {
                if (!$this->_ckOpened()) return false;

                $return = exec ("setserial " . $this->_device . " " . $param . " " . $arg . " 2>&1");

                if ($return{0} === "I")
                {
                        trigger_error("setserial: Invalid flag", E_USER_WARNING);
                        return false;
                }
                elseif ($return{0} === "/")
                {
                        trigger_error("setserial: Error with device file", E_USER_WARNING);
                        return false;
                }
                else
                {
                        return true;
                }
        }

        //
        // CONFIGURE SECTION -- {STOP}
        //

        //
        // I/O SECTION -- {START}
        //

        /**
         * Sends a string to the device
         *
         * @param string $str string to be sent to the device
         * @param float $waitForReply time to wait for the reply (in seconds)
         */
        function sendMessage ($str, $waitForReply = 0.1)
        {
                $this->_buffer .= $str;

                if ($this->autoflush === true) $this->serialflush();

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

        /**
         * Reads the port until no new datas are availible, then return the content.
         *
         * @pararm int $count number of characters to be read (will stop before
         *      if less characters are in the buffer)
         * @return string
         */
        function readPort ($count = 0)
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        trigger_error("Device must be opened to read it", E_USER_WARNING);
                        return false;
                }

                if ($this->_os === "linux" || $this->_os === "osx")
                        {
                        // Behavior in OSX isn't to wait for new data to recover, but just grabs what's there!
                        // Doesn't always work perfectly for me in OSX
                        $content = ""; $i = 0;

                        if ($count !== 0)
                        {
                                do {
                                        if ($i > $count) $content .= fread($this->_dHandle, ($count - $i));
                                        else $content .= fread($this->_dHandle, 128);
                                } while (($i += 128) === strlen($content));
                        }
                        else
                        {
                                do {
                                        $content .= fread($this->_dHandle, 128);
                                } while (($i += 128) === strlen($content));
                        }

                        return $content;
                }
				[color=gred]elseif ($this->_os === "windows")
						{
							$content = "";

							if ($count > 0)
							{
								$content = fread($this->_dHandle, $count);
							}

							return $content;
						}[/color]


                return false;
        }

        /**
         * Flushes the output buffer
         * Renamed from flush for osx compat. issues
         *
         * @return bool
         */
        function serialflush ()
        {
                if (!$this->_ckOpened()) return false;

                if (fwrite($this->_dHandle, $this->_buffer) !== false)
                {
                        $this->_buffer = "";
                        return true;
                }
                else
                {
                        $this->_buffer = "";
                        trigger_error("Error while sending message", E_USER_WARNING);
                        return false;
                }
        }

        //
        // I/O SECTION -- {STOP}
        //

        //
        // INTERNAL TOOLKIT -- {START}
        //

        function _ckOpened()
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        trigger_error("Device must be opened", E_USER_WARNING);
                        return false;
                }

                return true;
        }

        function _ckClosed()
        {
                if ($this->_dState !== SERIAL_DEVICE_CLOSED)
                {
                        trigger_error("Device must be closed", E_USER_WARNING);
                        return false;
                }

                return true;
        }

        function _exec($cmd, &$out = null)
        {
                $desc = array(
                        1 => array("pipe", "w"),
                        2 => array("pipe", "w")
                );

                $proc = proc_open($cmd, $desc, $pipes);

                $ret = stream_get_contents($pipes[1]);
                $err = stream_get_contents($pipes[2]);

                fclose($pipes[1]);
                fclose($pipes[2]);

                $retVal = proc_close($proc);

                if (func_num_args() == 2) $out = array($ret, $err);
                return $retVal;
        }

        //
        // INTERNAL TOOLKIT -- {STOP}
        //
}
?>

 

Problem area;

 

<?PHP

elseif ($this->_os === "windows")

{

$content = "";

 

if ($count > 0)

{

$content = fread($this->_dHandle, $count);

}

 

return $content;

}

 

 

                return false;

?>

Link to comment
Share on other sites

What is the weird stuff in your code snippet.  That is not actually in there is it?

 

[color=gred]elseif ($this->_os === "windows")
						{
							$content = "";

							if ($count > 0)
							{
								$content = fread($this->_dHandle, $count);
							}

							return $content;
						}[/color]

 

I'm referring to the color=gred and /color tags that seem to have gotten in there erroneously.

Link to comment
Share on other sites

@samtWilliams: I can't offer help here but maybe this apply in your case... taken from the class description

 

"Once the connection is opened, it can send data to the serial port, and read answers (reading is only implemented for linux).

 

The class may also change connection parameters for the given serial device.

 

==> /!\ WARNING /!\ : it's working with linux for r/w, but with windows i've only been able to make write working. If you're a windows user, try to access the serial port through network with serproxy instead."

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.