php.serial class whackyness

I've had some trouble over the last few months with getting values back via php. This is on a LAMP setup with an Xbee explorer module plugged into TTYUSB0. Arduino on another XBEE. I just switched to xbee as im getting a little farther along on my long term project. I was having this same issue when I had the arduino plugged directly via USB.

PHP Code:

<?php
/* Script for turning LED on and off based on temperature*/

	// 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/ttyUSB0");
	//Set the serial port parameters. The documentation says 9600 8-N-1, so
	$serial->confBaudRate(9600); //Baud rate: 9600
	$serial->confParity("none");  //Parity (this is the "N" in "8-N-1")
	$serial->confCharacterLength(8); //Character length (this is the "8" in "8-N-1")
	$serial->confStopBits(1);  //Stop bits (this is the "1" in "8-N-1")
	

// Ask Arduino what the Air Temp is.

	$serial->deviceOpen();
	$serial->sendMessage(chr(10)); // start transmission
	$serial->sendMessage(chr(50));
	$serial->sendMessage(chr(13)); // end transmission
	$read = $serial->readPort(); // waiting for reply
	$current_air_temp = $read;
	$current_air_temp = (int)$current_air_temp;
	$serial->deviceClose();//We're done, so close the serial port again

echo $current_air_temp;
//Dry Remote Arduino Dingle Dongle for GroPI5000  (CO2, Temp, Humidity, Flood Alert)
#include <SHT1x.h>
#define sht15dataPin  10
#define sht15clockPin 11
SHT1x sht1x(sht15dataPin, sht15clockPin);
int CO2Pin = 4;                //CO2 Sensor Pin
int CO2SensorValue = 0;        //CO2 Sensor Value
int CO2LevelValue = 0;         //CO2 Level Value
int FloodValue = 0;            //Flood Sensor value
int FloodPin = 5;              //Flood Sensor Pin
volatile int NbTopsFan;        //measuring the rising edges of the signal
int Calc;                               
int hallsensor = 2;            //The pin location of the sensor
int count = 0;
void rpm ()     //This is the function that the interupt calls 
{ 
  NbTopsFan++;  //This function measures the rising and falling edge of the hall effect sensors signal
} 
int incomingByte = -1;
int val = 0; 
char code[10]; 
int bytesread = 0;

void setup()                    // run once, when the sketch starts
{
   Serial.begin(9600);
     pinMode(hallsensor, INPUT); //initializes digital pin 2 as an input
  attachInterrupt(0, rpm, RISING); //and the interrupt is attached
  }

void loop()        {             // run over and over again
  checkSerial();
}

void checkSerial() {
  if(Serial.available() > 0) {          // if data available 
    if((val = Serial.read()) == 10) {   // check for header start 
      bytesread = 0; 
      while(bytesread<1) {              // read 1 digit code 
        if( Serial.available() > 0) { 
          val = Serial.read(); 
          if(val == 13) { // check for header end  
            break;                       // stop reading 
          } 
          code[bytesread] = val;         // add the digit           
          bytesread++;                   // ready to read next digit  
        } 
      } 
      if(bytesread == 1) {              // if 1 digit read is complete 
        incomingByte = int(code[0]);
        doLEDS();
      } 
      bytesread = 0; 
      delay(50);                       // wait for a second 
    } 
  } 


}

void doLEDS()
{
if (incomingByte == 50) { // php is asking for the Air Temp
 float temp_f;                                          
  temp_f = sht1x.readTemperatureF();                                                
 Serial.println(temp_f);                     //printing the result

  }
 
  if (incomingByte == 44) { // php is asking for the CO2
 CO2SensorValue = analogRead(CO2Pin);
CO2LevelValue = map(CO2SensorValue, 0, 1023, 0, 2000);            
  Serial.println(CO2LevelValue);                     //printing the result

  }
  } //END OF MAIN LOOP

If I run the PHP code It sends a request to arduino for data. arduino does its thing and serial.writes it back. PHP then echos it to the page. no big deal

however if I refresh the page it will return a zero. refresh again 0, refresh again and I get good data. different amounts of refreshes fix it. different amounts of refreshs break it
If I wantch the serial monitor on the arduino I can see it send out the correct data but PHP is somehow sometimes missing it.

Anybody seen anything like this? Ideas?

You are sending three bytes to the Arduino to tell it that you are ready for it to send data. You do this as soon as you open the serial port.

Opening the serial port causes the Arduino to reset.It takes some time before the Arduino is ready to receive the data.

A slight pause in the PHP script, after opening the serial port, before sending data, might be useful.

Sending one byte is all that is needed.

When the Arduino does get all the data, it calls a function called doLEDS() which has absolutely nothing to do with LEDs. I'd rename that function, to something more meaningful, like gfhasdg8vSNGD().

lol. Ah this is my simple test script that expanded into something else.. Its named correctly (doDryDingleDongle) in my real script.
I will try the delay, thanks.

Sending one byte is all that is needed.

I'm sending a start command, the command, and an end command. My hope was that will allow it to not get back bad data since it will only do something when there is a real start and stop not random serial noise. (there will be other chatter over the serial)

what do you mean by only one byte is needed?

Its named correctly (doDryDingleDongle) in my real script.

Oh, well, that's a much better name. It's immediately obvious what that function is supposed to do. What is that function supposed to do?

not random serial noise. (there will be other chatter over the serial)

Specific noise, not random noise, huh? This doesn't make much sense. You control both ends of the serial port. Stop the chatter and random noise.

Well since doDryDingleDongle is the only function I could just put it inside the void loop... But doDryDingleDongle makes perfect sense to me:) do whatever the dry dingle dongle needs to do... over and over. I get your point though. maybe ill name it
(checksensorlevelsonalldevicesconnectedtothedrydingledongleandspitouttheresponseviaserial)

By random noise I mean I have a couple other arduinos talking via serial/xbee as well. And since the Xbees seem to just read/write to serial anything that any other xbee says I dont want them to step on each other. I may try just timing them differently but this seems to work for now.

Is there any documentation on this serial class? I am wondering about this line in the above PHP code:

$read = $serial->readPort(); // waiting for reply

Does it really just "sit there" if nothing is waiting in the serial port buffer? That doesn't sound right to me. I would think the proper way it would work would be something like:

while (!$read = $serial->readPort()) {
   // waiting for reply - do something else?
}

// data available in $read - process it

...without having any documentation on the library though, I can't say whether the above would actually work or not. If it does, though, there should be a counter inside the loop for number of retries, so it could at least error out:

$tries = 0;
$max_tries = 100;

while (!$read = $serial->readPort() && $tries < $max_tries) {
  // waiting for reply - do something else?
  $tries++;
}

if ($read) {
  // data available in $read - process it
}
else {
  die("Timeout limit reached - data unavailable.");
}

good call Ill have to check out the library better.
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1185507207/51#51 I believe is the main thread. I think the guy that wrote it is an arduino hobbyest.

Just for further testing I have put a resistor between 3.3v and RST to stop the reset on serial open.
Im getting the same problems.
I thought maybe a dumb browser issue so I ran the code from command line and dumped results to DB.

I then added a dumb amount of sleeps in PHP with same issues.

<?php
$dbhost = 'localhost';
$dbuser = 'root';
$dbpass = 'asdf';
$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die                      ('Error connecting to mysql');
$dbname = 'arduino';
mysql_select_db($dbname);
$sql="select * from levels";
$results= mysql_query($sql);
$row = mysql_fetch_array($results);
?>
<?php
/* Script for Checking all the levels*/

	// 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/ttyUSB0");
	//Set the serial port parameters. The documentation says 9600 8-N-1, so
	$serial->confBaudRate(9600); //Baud rate: 9600
	$serial->confParity("none");  //Parity (this is the "N" in "8-N-1")
	$serial->confCharacterLength(8); //Character length (this is the "8" in "8-N-1")
	$serial->confStopBits(1);  //Stop bits (this is the "1" in "8-N-1")
	

// Ask Arduino what the Air Temp is.

	$serial->deviceOpen();
	sleep(3); //wait for Arduino reset after serial open
	$serial->sendMessage(chr(10)); // start transmission
	$serial->sendMessage(chr(50));
	$serial->sendMessage(chr(13)); // end transmission
		sleep(3); //wait for Arduino reset after serial open

	$read = $serial->readPort(); // waiting for reply
		sleep(3); //wait for Arduino reset after serial open

	$current_air_temp = $read;
	$current_air_temp = (int)$current_air_temp;
	//We're done, so close the serial port again
	$serial->deviceClose();

	mysql_query("INSERT INTO logging (stuff,Sensor) VALUES ($current_air_temp,'Air Temp')");
	?>

I havent debugged your example in any way but get an error Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/php_serial.class.php on line 516
Ill look into it later tonight. I supposed to be doing real work right now :wink:

I do see this in the serial class.

/**
	 * 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;
	}

Test PHP code

<?php
/* Script for Checking all the levels*/

	// 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/ttyUSB0");
	//Set the serial port parameters. The documentation says 9600 8-N-1, so
	$serial->confBaudRate(9600); //Baud rate: 9600
	$serial->confParity("none");  //Parity (this is the "N" in "8-N-1")
	$serial->confCharacterLength(8); //Character length (this is the "8" in "8-N-1")
	$serial->confStopBits(1);  //Stop bits (this is the "1" in "8-N-1")
	

// Ask Arduino what the Air Temp is.

	$serial->deviceOpen();
	$serial->sendMessage(chr(10)); // start transmission
	$serial->sendMessage(chr(50));
	$serial->sendMessage(chr(13)); // end transmission
//	$read = $serial->readPort(); // waiting for reply
$tries = 0;
$max_tries = 100;

while (!$read = $serial->readPort() && $tries < $max_tries) {
  // waiting for reply - do something else?
  $tries++;
}

if ($read) {
    $current_air_temp = $read;
	$current_air_temp = (int)$current_air_temp;
	//We're done, so close the serial port again
	$serial->deviceClose();

	mysql_query("INSERT INTO logging (stuff,Sensor) VALUES ($current_air_temp,'Air Temp')");
}
else {
	$serial->deviceClose();
  die("Timeout limit reached - data unavailable.");
}
	
	?>

I added this "delayer". It doesnt help either. WTF this is driving me nuts. PHP is supposed to be the easy part for me.
Im getting results only about 20% of the time.

function microtime_float()
{
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
}

$read = '';
$theResult = '';
$start = microtime_float();

while ( ($read == '') && (microtime_float() <= $start + 1) ) {
        $read = $serial->readPort();
        if ($read != '') {
                $theResult .= $read;
                $read = '';
        }
}
	$serial->deviceClose();

// etc...

echo "Read data: ".$theResult."
";

plugged the arduino in directly to the pc with same issue. Just had to cut the xbee out for my own sanity.

OK I may be onto something. With Uno plugged into ttyACM0 directly on the Ubuntu 10.4 PC
Using the reset resistor between 3.3v and reset (120ohms i think)
10 second delay after opening the serial port
1.5 second delay after starting to read the data from the port.
using a cron script with no browser involved running for 15 minutes at one minute intervals I got results 100% of the time.

<?php
/* Script for turning LED on and off based on temperature*/

	// Load the serial port class
	require("php_serial.class.php");
function microtime_float()
{
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
}

	//Initialize the class
	$serial = new phpSerial();
	//Specify the serial port to use... in this case COM1
	$serial->deviceSet("/dev/ttyACM0");
	//Set the serial port parameters. The documentation says 9600 8-N-1, so
	$serial->confBaudRate(9600); //Baud rate: 9600
	$serial->confParity("none");  //Parity (this is the "N" in "8-N-1")
	$serial->confCharacterLength(8); //Character length (this is the "8" in "8-N-1")
	$serial->confStopBits(1);  //Stop bits (this is the "1" in "8-N-1")
	$serial->deviceOpen();
	sleep(10); //wait for Arduino reset after serial open
	$serial->sendMessage(chr(10)); // start transmission
	$serial->sendMessage(chr(50));
	$serial->sendMessage(chr(13)); // end transmission
$read = '';
$theResult = '';
$start = microtime_float();

while ( ($read == '') && (microtime_float() <= $start + 1.5) ) {
        $read = $serial->readPort();
        if ($read != '') {
                $theResult .= $read;
                $read = '';
        }
}
	$serial->deviceClose();

// etc...

echo "Read data: ".$theResult."
";
	mysql_query("INSERT INTO logging (stuff,Sensor) VALUES ($theResult,'Air Temp')");

	?>

Now on to the xbees and see what happens.

just to report back. With the xbees is working as well.
Actually Ive found that even without the reset resistor the 10sec pause seems to be enough to let it reset and then combined with the delay before reading its all happy.