+ * @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
+ *
+ * do not change this code unless you know what you are getting into. Leaving it as you have received it should work fine!
+ */
+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)) === 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 <= 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);
+ }
+ 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;
+ }
+ elseif ($this->_os === "windows")
+ {
+ /* Do nothing : not implented yet */
+ }
+
+ trigger_error("Reading serial port is not implemented for Windows", E_USER_WARNING);
+ 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}
+ //
+}
+?>
diff --git a/serialledcontrol.php b/serialledcontrol.php
new file mode 100644
index 0000000..7a56dd5
--- /dev/null
+++ b/serialledcontrol.php
@@ -0,0 +1,85 @@
+
+(Thanks you rule!!) to communicate with the Arduino Serial!
+
+*/
+
+
+//check the GET action var to see if an action is to be performed
+if (isset($_GET['action'])) {
+ //Action required
+
+ //Load the serial port class
+ require("php_serial.class.php");
+
+ //Initialize the class
+ $serial = new phpSerial();
+
+ //Specify the serial port to use... in this case COM1
+ $serial->deviceSet("/dev/cu.usbmodem1421"); //SET THIS TO WHATEVER YOUR SERIAL DEVICE HAPPENS TO BE, YOU CAN FIND THIS UNDER THE ARDUINO SOFTWARE'S MENU
+
+ //Set the serial port parameters. The documentation says 9600 8-N-1, so
+ $serial->confBaudRate(115200); //Baud rate: 9600
+ // $serial->confParity("none"); //Parity (this is the "N" in "8-N-1") ******THIS PART OF THE CODE WAS NOT NEEDED
+ // $serial->confCharacterLength(8); //Character length (this is the "8" in "8-N-1") ******THIS PART OF THE CODE WAS NOT NEEDED
+ // $serial->confStopBits(1); //Stop bits (this is the "1" in "8-N-1") ******THIS PART OF THE CODE WAS NOT NEEDED
+ //$serial->confFlowControl("none"); //******THIS PART OF THE CODE WAS NOT NEEDED
+
+
+ //Now we "open" the serial port so we can write to it
+ $serial->deviceOpen();
+
+ //Issue the appropriate command according to the Arduino source code 0=Green On, 1=Green Off, 2=Red On, 3=Red Off.
+ if ($_GET['action'] == "greenon") {
+ error_log('green on');
+ //to turn the GREEN LED ON, we issue the command
+ $serial->sendMessage('12,255');
+
+ } else if ($_GET['action'] == "greenoff") {
+ error_log('green of');
+ //to turn the GREEN LED OFF, we issue this command
+ $serial->sendMessage('12,0');
+ }
+
+ if ($_GET['action'] == "redon") {
+ error_log('red on');
+ //to turn the RED LED ON, we issue the command
+ $serial->sendMessage('11,255');
+
+ } else if ($_GET['action'] == "redoff") {
+ error_log('red of');
+ //to turn the RED LED OFF, we issue this command
+ $serial->sendMessage('11,0');
+ }
+
+ //We're done, so close the serial port again
+ $serial->deviceClose();
+
+}
+
+
+?>
+
+
+
+Arduino Project 1: Serial LED Control
+
+
+
+Arduino Project 1: Serial LED Control
+">
+Click here to turn the GREEN LED on.
+">
+Click here to turn the GREEN LED off.
+">
+Click here to turn the RED LED on.
+">
+Click here to turn the RED LED off.
+
+
\ No newline at end of file