Help with Linux/PHP/Apache

Hello. I need some help please!!

I am running Ubuntu with PHP/Apache. I'm new to Linux and Arduino.

I have a basic PHP script where I can click ON or OFF for LEDs 1 to 4. Just something real simple that I can access from a web browser to control the Arduino.

If I run the script within the command line of Ubuntu, it works fine. When I try it over a web browser, I get errors. As far as I can tell, it seems to be a permissions problem. ie. the web server (Apache) doesn't have permissions to run some stuff). I am using the php serial class. Here is the error:

Warning: Specified serial port is not valid in /var/www/php_serial.class.php on line 112

Warning: Unable to set the baud rate : the device is either not set or opened in /var/www/php_serial.class.php on line 205

Warning: The device must be set before to be open in /var/www/php_serial.class.php on line 138

Warning: Device must be opened in /var/www/php_serial.class.php on line 542

As I said, this only happens over a browser, via the shell it works fine. Any suggestions please?? Thanks.

It's a bit hard to tell exactly what the problem is without knowing what the offending code is. Could you possibly post up some of the source?

Specifically, line 112 looks like it would be quite helpful. The other errors seem to be a result of that particular line.

Note the php serial class it is not my code - lots of people use this code for various projects involving serial ports. The code is too much to paste here. You can view it here:

This is my code on the webserver (it’s very simple right now for testing):

<?php error_reporting(E_ALL); include "php_serial.class.php"; if ($_GET['led']&&$_GET['mode']) { $serial = new phpSerial; $serial->deviceSet("/dev/ttyUSB0"); $serial->confBaudRate(9600); $serial->deviceOpen(); if ($_GET['led']==1&&$_GET['mode']=='on') {$command="a";} if ($_GET['led']==1&&$_GET['mode']=='off') {$command="b";} if ($_GET['led']==2&&$_GET['mode']=='on') {$command="c";} if ($_GET['led']==2&&$_GET['mode']=='off') {$command="d";} if ($_GET['led']==3&&$_GET['mode']=='on') {$command="e";} if ($_GET['led']==3&&$_GET['mode']=='off') {$command="f";} if ($_GET['led']==4&&$_GET['mode']=='on') {$command="g";} if ($_GET['led']==4&&$_GET['mode']=='off') {$command="h";} $serial->sendMessage($command); $serial->deviceClose(); } echo " LED 1: ON | OFF"; echo " LED 2: ON | OFF"; echo " LED 3: ON | OFF"; echo " LED 4: ON | OFF"; ?>

I think I see your problem. In the source code, it is assumed that all tty Serial devices are addressed via /dev/ttySx, at this snip:

                  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;

One solution to getting around this without altering your source code would be creating a link in /dev/ to your ttyUSB0 device, by giving it a name that would match the /dev/ttySx format, eg:

$ ln -s /dev/ttyUSB0 /dev/ttyS99

If that's not an option, you may want to consider modifying the PHP source to allow /dev/ttyUSBx devices as well. I would imagine that they would still work if the device path was allowed.

Let us know how you get on. :slight_smile:

[edit]Having actually read the code this time, it appears to do a (rather nifty) operation to determine whether the serial port argument provided is valid or not.

if ($this->_exec("stty -F " . $device) === 0)

Executes stty (which displays information about terminal lines). If that call returns 0, the application was run successfully, which would indicate that it is a terminal device. If it does not return 0, an error has occurred and the assumption is that it is not a terminal device.

Running stty on my system for two ports yields the following:

vector@Leto:~$ stty -F /dev/ttyUSB0
speed 9600 baud; line = 0;
min = 0; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke

vector@Leto:~$ stty -F /dev/ttyUSBnonexistant
stty: /dev/ttyUSBnonexistant: No such file or directory

I would first suggest running stty -F /dev/ttyUSB0 on your machine to ensure that it is in fact reading as a terminal device. Sorry for the long-winded edit.[/edit]

ross@webserver:/var/www$ stty -F /dev/ttyUSB0
speed 9600 baud; line = 0;
kill = ^H; min = 100; time = 2;
-icrnl -imaxbel
-opost -onlcr
-isig -icanon -echo

This works fine. Also, just to re-iterate, there is no problem with the php serial class. Everything works fine if I run the PHP scripts at the command line. It only fails when running it via a web browser.

So, for example, if I SSH to my linux box and run "php index.php" the code executes fine and controls my arduino fine. If I go via a web browser to then I get the errors posted in the initial post.

Thanks for your help so far - any other ideas?

If you're using ubuntu then you probably have to add the Apache user to the dialout group. This will give you the correct permissions to the ttyUSB* ports created by the Arduino.

Check your httpd.conf to see what user apache is running under.

When you ssh in and run php 'whatever' from the command line you're probably using your normal login account that already has permission on the /dev/ttyUSB* ports. Apache, on the other hand, is set to use a different account.



Good call. This could be confirmed by running

stty -F /dev/ttyUSB0

From a php script directly and echoing the output.

looking at the definition of exec() from

string exec  ( string $command  [, array &$output  [, int &$return_var  ]] )

If you chuck the following script into a php file and execute it, you should get a nice text dump of the output of stty and what is or isn’t working with it, including a return code.

$output = new array();
$retval = null;
exec( "stty -F /dev/ttyUSB0", $output, $retval );
var_dump( $output );
var_dump( $retval );

Thank you so much!!!!

I added the apache user (www-data) to the dialout group and it now works OK!!

Thanks again.