/php_serial.class.php
PHP | 650 lines | 425 code | 75 blank | 150 comment | 66 complexity | 67ec0cddf101b4855282bbd7b4accbd1 MD5 | raw file
- <?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@geekymedia.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
- * @thanks Jim Wright for OSX cleanup/fixes.
- * @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 to 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 === "osx")
- {
- $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);
- }
- elseif ($this->_os === "osx")
- {
- $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 <= 8)
- * @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 > 8) $int = 8;
- if ($this->_os === "linux")
- {
- $ret = $this->_exec("stty -F " . $this->_device . " cs" . $int, $out);
- }
- elseif ($this->_os === "osx")
- {
- $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);
- }
- elseif ($this->_os === "osx")
- {
- $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);
- elseif ($this->_os === "osx")
- $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;
- }
- elseif ($this->_os === "windows")
- {
- // Windows port reading procedures still buggy
- $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;
- }
- 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}
- //
- }
- ?>