Serial communication with php

Hi Folks, not sure if I'm posting in the correct location but couldn't find a more suitable section. Anyways, I'm having problems on MAC OS snow leopard accessing the serial port that my adruino is connected to. I know it's sending data as I've installed 'Serial Tools' and can see some stuff sent back. I can also type the following in a terminal 'screen /dev/tty.usbmodem411 9600' and also get data. However, in my simple php script which just calls fopen("tty.usbmodem411", rb), it appears that the connection is never made- some sort of infinite loop. Am wondering if php is blocking and if there's someway to change the behavior. Couldn't find anything in the documentation. Also wrote a simple C function to do the same thing but it doesn't work either, also using fopen. Any other function calls that could give me access so I can read the incoming data? Thanks!

Serial communication doesn't work with this naive approach. (I've been there myself, on Perl.) There are serial libraries for most languages that help you implement serial communication.

Thanks for the heads up, since the php file operations are all based on the equivalent C functions I'm assuming I need to go a little bit deeper in. Looks like I may need to use the dio or Direct IO Functions with PHP, will give it a try tonight.

DIO (latest beta version) works well for me with Arduino, not using Mac however, YMMV.

I just started messing with PHP dio myself a day or two ago, and I've gotten it working against the Arduino serial port (only for reading so far). And I am on a Mac (OS X 10.5).

However, I'm a little perplexed by my inability to save a file descriptor resource (returned by dio_open()) in a PHP session variable in a way that it can be used in a subsequent PHP invocation. What I'd like to do is essentially this:

 <?php
session_start(); /* enable session vars */
if (isset($_GET['action'])) {
  $action = $_GET['action'];
  switch ($action) {
  case 'start':
    /* open the tty, configure the IO */
    $fd = dio_open('/dev/tty.usbserial-A6008mSD',
                   O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (!$fd) {
      /* handle the failure to open the file */
    } else {
      dio_fcntl($fd, F_SETFL, O_SYNC);
      dio_tcsetattr($fd, array('baud' => 9600,
                               'bits' => 8,
                               'stop' => 1,
                               'parity' => 0));
      /* save the file descriptor in a session var */
      $_SESSION['fd'] = $fd;
      $s = dio_read($fd, 100);
    }
    break;
  case 'run':
    $fd = $_SESSION['fd'];
    $s = dio_read($fd, 100);
    break;
  /* other cases */
  }

The initial dio_read(), immediately after the dio_open() in the 'start' branch, works just fine. But when the script executes again and follows the 'run' branch, PHP complains about dio_read() needing a resource as its first argument, but that it's been given an integer instead. (I checked, and the value that I pull out of $_SESSION['fd'] in the 'run' branch is in fact 0.)

So I had to modify my logic to open, configure, read, and close the file each time the script runs -- which is not only ugly, but also seems like a good way to lose data that arrived on the serial port between script invocations.

Anyone seen this before, and know a way to work around it? (Or see something obvious that I'm doing wrong?)

Thanks,

FG

So I had to modify my logic to open, configure, read, and close the file each time the script runs -- which is not only ugly, but also seems like a good way to lose data that arrived on the serial port between script invocations.

The "file" you are referring to appears to be the serial port. Opening and closing the serial port resets the Arduino, so keeping the handle is essential, so you can keep the "file" open.

I checked, and the value that I pull out of $_SESSION['fd'] in the 'run' branch is in fact 0.

This sounds like you are not persisting session data correctly, then. It's been a year or more since I last used PHP, so I don't remember exactly what you need to do to make this happen, but I think it involves correctly configuring PHP and adding some code to the PHP script.

As PaulS suggests, it might be best to check your session stuff separately from the dio.

Here is a session vars check derived from your code which works ok on my system.

<?php
session_start(); /* enable session vars */
if (isset($_GET['action'])) {
  $action = $_GET['action'];
  switch ($action) {
  case 'start':
    /* open the tty, configure the IO */
    $fd = 61;
    $_SESSION['fd'] = $fd;
    echo "Saved $fd";
    break;
  case 'run':
    $fd = $_SESSION['fd'];
    echo "Found $fd";
    break;
  /* other cases */
  }
}

When I do
http://localhost/.....?action=start => Saved 61
http://localhost/.....?action=run => Found 61

What is the result on your system? If ok, then we move on to other issues.

Opening and closing the serial port resets the Arduino, so keeping the handle is essential

Thanks for mentioning that, I wasn't really aware of it before. Of course, it means that finding a solution to this problem is therefore also essential.

it might be best to check your session stuff separately from the dio.

Yes, that had occurred to me before I saw your post, so I constructed a simple test to store and retrieve any value that I can pass in a $_GET[] variable, and it works just as I would expect. Invoking your suggested test script also reports "Found 61", as expected.

I wonder if there's any additional configuration I might need to do on my PHP installation to allow it to store more "exotic" datatypes like FD resources...

Edit: Or maybe I need to serialize the FD resource before squirreling it away in the session variable, and deserialize it after retrieving it? (No time this morning to construct a test for this. And in any event, I've always thought that serializing the values stored in $_SESSION[] was automatically performed by PHP...)

FG

Hmm... I may be screwed. Or not -- at least not yet. I can't really tell, since the evidence I currently have is ambiguous.

I found the following in PHP's online documentation:

Note:
It is currently impossible to register resource variables in a session. For example, you cannot create a connection to a database and store the connection id as a session variable and expect the connection to still be valid the next time the session is restored. PHP functions that return a resource are identified by having a return type of resource in their function definition. A list of functions that return resources are available in the resource types appendix.

If $_SESSION (or $HTTP_SESSION_VARS for PHP 4.0.6 or less) is used, assign values to $_SESSION. For example: $_SESSION['var'] = 'ABC';

Now, what's ambiguous about this is that I found it in the documentation for the session_register() function, which I'm not using. It looks as though the final sentence of this note may be suggesting that $_SESSION will work where session_register() would fail, and I don't see any similar caveat in the documentation for $_SESSION. On the other hand, I would imagine that the same underlying mechanism for persisting a value between script invocations would be used in either case; and the fact that using $_SESSION for storing/retrieving a simple value like 'ABC' works for me, and using it for storing/retrieving a dio FD resource doesn't, leads me to believe that it's just not possible.

@colbec: have you been successful in persisting a dio FD resource via $_SESSION on your system?

@Firegeek - looks like you are right, a resource var did not persist.

Following code produces

http://localhost/.....?action=start => Saved 61 and file pointer Resource id #3
http://localhost/.....?action=run => Found 61

<?php
session_start(); /* enable session vars */
if (isset($_GET['action'])) {
  $action = $_GET['action'];
  switch ($action) {
  case 'start':
    /* open the tty, configure the IO */
    $fd = 61;
    $_SESSION['fd'] = $fd;
    $fp = fopen("index.html","r");
    $_SESSION['fp'] = $fp;
    echo "Saved $fd ";
    if ($fp) echo "and file pointer $fp";
    break;
  case 'run':
    $fd = $_SESSION['fd'];
    $fp = $_SESSION['fp'];
    echo "Found $fd";
    if ($fp) echo "and file pointer $fp";
    break;
  /* other cases */
  }
}

Don't bother yourself when it's already done for you:
http://code.google.com/p/php-serial/

dario111cro:
Don't bother yourself when it's already done for you:
Google Code Archive - Long-term storage for Google Code Project Hosting.

Could you please explain how phpSerial solves the problem any better than the dio extension does?

The phpSerial::deviceOpen() method uses fopen() to open the device (svn/trunk/phpSerial.php:177), and fopen() returns a resource. As colbec's most recent post clearly shows, $_SESSION can't be used to make the resource returned by fopen() persist across PHP script invocations.

Oh. I didn't read all posts, just first one. I got serial communication with Arduino with that class.

After a couple of experiments and a bit more reflection, I'm persuaded that the inability to persist a resource (such as a file descriptor, DB connection, etc.) via the PHP session mechanism is due more to the way Apache (in my case) works than it is to a particular shortcoming of PHP.

So I've moved on to a different approach, using a small daemon process (written in Python) to open and maintain the connection with the Arduino, and a pair of FIFOs (named pipes) for communication between PHP and the daemon. It's a little clunky at the moment (mostly because I'm still re-learning a lot of stuff I forgot years ago!), but so far, so good.

If anyone's been following this thread and wants more detail on this daemon-based approach, let me know and I'll post the code once I've got it working the way I want...

FG

i use the php-serial library and arduino does NOT reset on each connection.
works fine for me

<?php
include('php-serial.class.php');

if (isset($_POST['action'])){

	$serial = new phpSerial;

	// First we must specify the device. This works on both linux and windows (if
	// your linux serial device is /dev/ttyS0 for COM1, etc)
	$serial->deviceSet("/dev/ttyACM0");

	// We can change the baud rate, parity, length, stop bits, flow control
	$serial->confBaudRate(115200);
	$serial->confParity("none");
	$serial->confCharacterLength(8);
	$serial->confStopBits(1);
	$serial->confFlowControl("none");

	// Then we need to open it
	$serial->deviceOpen();

	// To write into
	$serial->sendMessage(chr($_POST['action']));
	$serial->deviceClose();

}
?>
<html>
<head>
</head>
<body>
<style>
.tilt_left{
display:block;
-webkit-transform: rotate(-45deg); 
-moz-transform: rotate(-45deg);	
}.tilt_right{
display:block;
-webkit-transform: rotate(45deg); 
-moz-transform: rotate(45deg);	
}
</style>
<form method='post' action='#' id='ButtonPad'>
	<input type='hidden' name='action' id='action'>
	<script type='text/javascript'>
		function button_press(val){
			document.getElementById('action').value = val ;
			//alert(val);
			document.getElementById('ButtonPad').submit() ;
		}
	</script>
	<center>
	
	<fieldset  style='width:0em'>
		<legend>User Actions</legend>
		<table name='keypad'>
			<tr><td><input type='button' value='1' onclick='button_press("1");'> </td><td><input type='button' value='2' onclick='button_press("2");'> </td><td><input type='button' value='3' onclick='button_press("3");'> </td></tr>
			<tr><td><input type='button' value='4' onclick='button_press("4");'> </td><td><input type='button' value='5' onclick='button_press("5");'> </td><td><input type='button' value='6' onclick='button_press("6");'> </td></tr>
			<tr><td><input type='button' value='7' onclick='button_press("7");'> </td><td><input type='button' value='8' onclick='button_press("8");'> </td><td><input type='button' value='9' onclick='button_press("9");'> </td></tr>
			<tr><td><input type='button' value='#' onclick='button_press("10");'> </td><td><input type='button' value='0' onclick='button_press("0");'> </td><td><input type='button' value='*' onclick='button_press("11");'> </td></tr>
		</table>
	</fieldset>
	
	


	
	<fieldset style='width:0em'>
		<legend>Camera Control</legend>
		<table name='camera'>
			<tr><td><input type='button' value='1' onclick='button_press("50");'> </td><td><input type='button' value='&uarr;' onclick='button_press("51");'> </td><td><input type='button' value='2' onclick='button_press("52");'> </td></tr>
			<tr><td><input type='button' value='&larr;' onclick='button_press("53");'> </td><td><input type='button' value='X' onclick='button_press("54");'> </td><td><input type='button' value='&rarr;' onclick='button_press("55");'> </td></tr>
			<tr><td><input type='button' value='3' onclick='button_press("56");'> </td><td><input type='button' value='&darr;' onclick='button_press("57");'> </td><td><input type='button' value='4' onclick='button_press("58");'> </td></tr>
		</table>
		<table name='camera'>
			<tr><td><input type='button' value='Scan' onclick='button_press("59");'> </td><td><input type='button' value='Sweep' onclick='button_press("60");'> </td></tr>
		</table>
	</fieldset>

	


	
	<fieldset style='width:0em'>
		<legend>Vehicle Control</legend>
		<table name='vehicle' cellpadding='3px'>
			<tr><td><input class='tilt_left' type='button' value='&uarr;' onclick='button_press("70");'> </td><td><input type='button' value='&uarr;' onclick='button_press("71");'> </td><td><input type='button' value='&uarr;' onclick='button_press("72");' class='tilt_right' > </td></tr>
			<tr><td><input type='button' value='&larr;' onclick='button_press("73");'> </td><td><input type='button' value='X' onclick='button_press("74");'> </td><td><input type='button' value='&rarr;' onclick='button_press("75");'> </td></tr>
			<tr><td><input class='tilt_right' type='button' value='&darr;' onclick='button_press("76");'> </td><td><input type='button' value='&darr;' onclick='button_press("77");'> </td><td><input type='button' value='&darr;' onclick='button_press("78");' class='tilt_left' > </td></tr>
		</table>
	</fieldset>
	
	<? echo 'Command: ' . $_POST['action']; ?>
	</center>
</form>
</body>
</html>