├── README ├── Sms ├── Interface.php ├── Dummy.php ├── Http.php └── Serial.php ├── testDummy.php ├── testHttp.php ├── testSerial.php └── Sms.php /README: -------------------------------------------------------------------------------- 1 | SMS sender/receiver script with a GSM modem using AT+ commands 2 | 3 | Operation modes: 4 | 5 | * Serial (GSM modem conected via Serial interface) 6 | * Http (GSM modem connected trough a Serial-Ethernet adapter) 7 | -------------------------------------------------------------------------------- /Sms/Interface.php: -------------------------------------------------------------------------------- 1 | insertPin($pin) 12 | ->sendSMS(555987654, "test Hi")) { 13 | echo "SMS sent\n"; 14 | } else { 15 | echo "SMS not Sent\n"; 16 | } 17 | -------------------------------------------------------------------------------- /Sms/Dummy.php: -------------------------------------------------------------------------------- 1 | 4 | * GSM Modem AT Dummy interface 5 | * 6 | * THIS PROGRAM COMES WITH ABSOLUTELY NO WARANTIES ! 7 | * USE IT AT YOUR OWN RISKS ! 8 | * 9 | * @author Gonzalo Ayuso 10 | * @copyright under GPL 2 licence 11 | */ 12 | class Sms_Dummy implements Sms_Interface 13 | { 14 | public function deviceOpen() 15 | { 16 | } 17 | 18 | public function deviceClose() 19 | { 20 | } 21 | 22 | public function sendMessage($msg) 23 | { 24 | } 25 | 26 | public function readPort() 27 | { 28 | return array("OK", array()); 29 | } 30 | 31 | private $_validOutputs = array(); 32 | 33 | public function setValidOutputs($validOutputs) 34 | { 35 | $this->_validOutputs = $validOutputs; 36 | } 37 | } -------------------------------------------------------------------------------- /testHttp.php: -------------------------------------------------------------------------------- 1 | insertPin($pin); 13 | 14 | if ($sms->sendSMS(555987654, "test Hi")) { 15 | echo "SMS Sent\n"; 16 | } else { 17 | echo "Sent Error\n"; 18 | } 19 | 20 | // Now read inbox 21 | foreach ($sms->readInbox() as $in) { 22 | echo"tlfn: {$in['tlfn']} date: {$in['date']} {$in['hour']}\n{$in['msg']}\n"; 23 | 24 | // now delete sms 25 | if ($sms->deleteSms($in['id'])) { 26 | echo "SMS Deleted\n"; 27 | } 28 | } 29 | } catch (Exception $e) { 30 | switch ($e->getCode()) { 31 | case Sms::EXCEPTION_NO_PIN: 32 | echo "PIN Not set\n"; 33 | break; 34 | case Sms::EXCEPTION_PIN_ERROR: 35 | echo "PIN Incorrect\n"; 36 | break; 37 | case Sms::EXCEPTION_SERVICE_NOT_IMPLEMENTED: 38 | echo "Service Not implemented\n"; 39 | break; 40 | default: 41 | echo $e->getMessage(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /testSerial.php: -------------------------------------------------------------------------------- 1 | deviceSet("/dev/ttyS0"); 11 | $serial->confBaudRate(9600); 12 | $serial->confParity('none'); 13 | $serial->confCharacterLength(8); 14 | 15 | $sms = Sms::factory($serial)->insertPin($pin); 16 | 17 | if ($sms->sendSMS(555987654, "test Hi")) { 18 | echo "SMS sent\n"; 19 | } else { 20 | echo "Sent Error\n"; 21 | } 22 | 23 | // Now read inbox 24 | foreach ($sms->readInbox() as $in) { 25 | echo"tlfn: {$in['tlfn']} date: {$in['date']} {$in['hour']}\n{$in['msg']}\n"; 26 | 27 | // now delete sms 28 | if ($sms->deleteSms($in['id'])) { 29 | echo "SMS Deleted\n"; 30 | } 31 | } 32 | } catch (Exception $e) { 33 | switch ($e->getCode()) { 34 | case Sms::EXCEPTION_NO_PIN: 35 | echo "PIN Not set\n"; 36 | break; 37 | case Sms::EXCEPTION_PIN_ERROR: 38 | echo "PIN Incorrect\n"; 39 | break; 40 | case Sms::EXCEPTION_SERVICE_NOT_IMPLEMENTED: 41 | echo "Service Not implemented\n"; 42 | break; 43 | default: 44 | echo $e->getMessage(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sms/Http.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright under GPL 2 licence 10 | */ 11 | class Sms_Http implements Sms_Interface 12 | { 13 | private $_host; 14 | private $_port; 15 | 16 | function __construct($host=null, $port=null) 17 | { 18 | $this->_host = $host; 19 | $this->_port = $port; 20 | } 21 | 22 | public function confHost($host) 23 | { 24 | $this->_host = $host; 25 | } 26 | 27 | public function confPort($port) 28 | { 29 | $this->_port = $port; 30 | } 31 | 32 | private $_socket; 33 | 34 | public function deviceOpen() 35 | { 36 | $this->_socket = @fsockopen($this->_host, $this->_port, $errno, $errstr, 30); 37 | 38 | if (!$this->_socket) { 39 | throw new Exception("SOCKET ERROR"); 40 | } else { 41 | socket_set_timeout ($this->_socket, 10); 42 | } 43 | } 44 | 45 | public function deviceClose() 46 | { 47 | fclose($this->_socket); 48 | } 49 | 50 | public function sendMessage($msg) 51 | { 52 | fwrite($this->_socket, $msg); 53 | } 54 | 55 | private $_validOutputs = array(); 56 | 57 | public function setValidOutputs($validOutputs) 58 | { 59 | $this->_validOutputs = $validOutputs; 60 | } 61 | 62 | public function readPort() 63 | { 64 | $last = null; 65 | $buffer = array(); 66 | 67 | if ($this->_socket) { 68 | while (!in_array($last, $this->_validOutputs)) { 69 | $_buffer = trim(fgets($this->_socket)); 70 | $last = strtoupper($_buffer); 71 | $buffer[] = $_buffer; 72 | } 73 | return array($last, $buffer); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Sms.php: -------------------------------------------------------------------------------- 1 | 11 | * @copyright under GPL 2 licence 12 | */ 13 | class Sms 14 | { 15 | private $_serial; 16 | private $debug; 17 | protected $_pinOK = false; 18 | protected $openAT = false; 19 | 20 | const EXCEPTION_PIN_ERROR = 1; 21 | const EXCEPTION_NO_PIN = 2; 22 | const EXCEPTION_SERVICE_NOT_IMPLEMENTED = 3; 23 | /** 24 | * Factory. Creates new instance. Dependency injections with the type os Modem 25 | * valid serial resources: 26 | * Sms_Serial: GSM modem conected via seria interface 27 | * Sms_Http: GSM modem conected via seria/ethernet converter 28 | * Sms_Dummy: Mock for testing 29 | * 30 | * @param Sms_Interface $serial 31 | * @param Boolean $debug 32 | * @return Sms 33 | */ 34 | public static function factory($serial, $debug=false) 35 | { 36 | if (!($serial instanceof Sms_Serial || 37 | $serial instanceof Sms_Http || 38 | $serial instanceof Sms_Dummy 39 | )) { 40 | throw new Exception("NOT IMPLEMENTED", self::EXCEPTION_SERVICE_NOT_IMPLEMENTED); 41 | } 42 | 43 | $serial->setValidOutputs(array( 44 | 'OK', 45 | 'ERROR', 46 | '+CPIN: SIM PIN', 47 | '+CPIN: READY', 48 | '>' 49 | )); 50 | 51 | return new self($serial, $debug); 52 | } 53 | 54 | protected function __construct($serial, $debug=false) 55 | { 56 | $this->_serial = $serial; 57 | $this->_debug = $debug; 58 | } 59 | 60 | private function readPort($returnBufffer = false) 61 | { 62 | $out = null; 63 | list($last, $buffer) = $this->_serial->readPort(); 64 | if ($returnBufffer) { 65 | $out = $buffer; 66 | } else { 67 | $out = strtoupper($last); 68 | } 69 | if ($this->_debug == true) { 70 | echo $out . "\n"; 71 | } 72 | return $out; 73 | } 74 | 75 | private function sendMessage($msg) 76 | { 77 | $this->_serial->sendMessage($msg); 78 | } 79 | 80 | private function deviceOpen() 81 | { 82 | $this->_serial->deviceOpen(); 83 | } 84 | 85 | private function deviceClose() 86 | { 87 | $this->_serial->deviceClose(); 88 | } 89 | 90 | /** 91 | * Delete selected id from SMS SIM 92 | * 93 | * @param unknown_type $id 94 | * @return unknown 95 | */ 96 | public function deleteSms($id) 97 | { 98 | $this->deviceOpen(); 99 | $this->sendMessage("AT+CMGD={$id}\r"); 100 | $out = $this->readPort(); 101 | $this->deviceClose(); 102 | if ($out == 'OK') { 103 | return true; 104 | } else { 105 | return false; 106 | } 107 | } 108 | 109 | /** 110 | * Sends a SMS to a selected tlfn 111 | * @param Integer $tlfn 112 | * @param String $text 113 | * @return Boolean 114 | */ 115 | public function sendSMS($tlfn, $text) 116 | { 117 | if ($this->_pinOK) { 118 | $text = substr($text, 0, 160); 119 | $this->deviceOpen(); 120 | if ($this->openAT === true) { 121 | $this->sendMessage("AT+CMGS=\"{$tlfn}\"\n"); 122 | $out = $this->readPort(); 123 | if ($out == '>') { 124 | $this->sendMessage("{$text}" . chr(26)); 125 | $out = $this->readPort(); 126 | } else { 127 | return false; 128 | } 129 | } else { 130 | $this->sendMessage("AT+CMGS=\"{$tlfn}\"\r{$text}" . chr(26)); 131 | $out = $this->readPort(); 132 | } 133 | 134 | $this->deviceClose(); 135 | if ($out == 'OK') { 136 | return true; 137 | } else { 138 | return false; 139 | } 140 | } else { 141 | throw new Exception("Please insert the PIN", self::EXCEPTION_NO_PIN); 142 | } 143 | } 144 | 145 | public function isPinOk() 146 | { 147 | return $this->_pinOK; 148 | } 149 | 150 | /** 151 | * Inserts the pin number. 152 | * first checks if PIN is set. If it's set nothing happens 153 | * @param Integer $pin 154 | * @return Sms 155 | */ 156 | public function insertPin($pin) 157 | { 158 | $this->deviceOpen(); 159 | 160 | $this->sendMessage("AT+CPIN?\r"); 161 | $out = $this->readPort(); 162 | $this->deviceClose(); 163 | 164 | if ($out == "+CPIN: SIM PIN") { 165 | $this->deviceOpen(); 166 | if (is_null($pin) || $pin == '') { 167 | throw new Exception("PIN ERROR", self::EXCEPTION_PIN_ERROR); 168 | } 169 | $this->sendMessage("AT+CPIN={$pin}\r"); 170 | $out = $this->readPort(); 171 | $this->deviceClose(); 172 | // I don't know why but I need to wait a few seconds until 173 | // start sending SMS. Only after the first PIN 174 | sleep(20); 175 | } 176 | 177 | switch ($out) { 178 | case "+CPIN: READY": 179 | case "OK": 180 | $this->_pinOK = true; 181 | break; 182 | } 183 | 184 | if ($this->_pinOK === true) { 185 | return $this; 186 | } else { 187 | throw new Exception("PIN ERROR ({$out})", self::EXCEPTION_PIN_ERROR); 188 | } 189 | } 190 | 191 | const ALL = "ALL"; 192 | const UNREAD = "REC UNREAD"; 193 | /** 194 | * Read Inbox 195 | * 196 | * @param String $mode ALL | UNREAD 197 | * @return Array 198 | */ 199 | public function readInbox($mode=self::ALL) 200 | { 201 | $inbox = $return = array(); 202 | if ($this->_pinOK) { 203 | $this->deviceOpen(); 204 | $this->sendMessage("AT+CMGF=1\r"); 205 | $out = $this->readPort(); 206 | if ($out == 'OK') { 207 | $this->sendMessage("AT+CMGL=\"{$mode}\"\r"); 208 | $inbox = $this->readPort(true); 209 | } 210 | $this->deviceClose(); 211 | if (count($inbox) > 2) { 212 | array_pop($inbox); 213 | array_pop($inbox); 214 | $arr = explode("+CMGL:", implode("\n", $inbox)); 215 | 216 | for ($i = 1; $i < count($arr); $i++) { 217 | $arrItem = explode("\n", $arr[$i], 2); 218 | 219 | // Header 220 | $headArr = explode(",", $arrItem[0]); 221 | 222 | $fromTlfn = str_replace('"', null, $headArr[2]); 223 | $id = $headArr[0]; 224 | $date = $headArr[4]; 225 | $hour = $headArr[5]; 226 | 227 | // txt 228 | $txt = $arrItem[1]; 229 | 230 | $return[] = array('id' => $id, 'tlfn' => $fromTlfn, 'msg' => $txt, 'date' => $date, 'hour' => $hour); 231 | } 232 | } 233 | return $return; 234 | } else { 235 | throw new Exception("Please insert the PIN", self::EXCEPTION_NO_PIN); 236 | } 237 | } 238 | 239 | public function setOpenAT($openAT) 240 | { 241 | $this->openAT = $openAT; 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /Sms/Serial.php: -------------------------------------------------------------------------------- 1 | ) 5 | * http://www.phpclasses.org/package/3679-PHP-Communicate-with-a-serial-port.html 6 | * I've done a change in readport function, triggering only with a list of 7 | * valid outputs. Windows support of the original class has been removed 8 | * 9 | * THIS PROGRAM COMES WITH ABSOLUTELY NO WARANTIES ! 10 | * USE IT AT YOUR OWN RISKS ! 11 | * 12 | * @author Gonzalo Ayuso 13 | * @author Rémy Sanchez 14 | * 15 | * @copyright under GPL 2 licence 16 | */ 17 | class Sms_Serial implements Sms_Interface 18 | { 19 | const SERIAL_DEVICE_NOTSET = 0; 20 | const SERIAL_DEVICE_SET = 1; 21 | const SERIAL_DEVICE_OPENED = 2; 22 | 23 | private $_device = null; 24 | private $_dHandle = null; 25 | private $_dState = self::SERIAL_DEVICE_NOTSET; 26 | private $_buffer = ""; 27 | private $_os = ""; 28 | /** 29 | * Constructor. Perform some checks about the OS and setserial 30 | * 31 | * @return Sms_Serial 32 | */ 33 | function __construct() 34 | { 35 | setlocale(LC_ALL, "en_US"); 36 | 37 | $sysname = php_uname(); 38 | 39 | if (substr($sysname, 0, 5) === "Linux") { 40 | $this->_os = "linux"; 41 | 42 | if ($this->_exec("stty --version") === 0) { 43 | register_shutdown_function(array($this, "deviceClose")); 44 | } else { 45 | trigger_error("No stty availible, unable to run.", E_USER_ERROR); 46 | } 47 | } else { 48 | trigger_error("Host OS is must be linux, unable tu run.", E_USER_ERROR); 49 | exit(); 50 | } 51 | } 52 | 53 | /** 54 | * Device set function : used to set the device name/address. 55 | * -> linux : use the device address, like /dev/ttyS0 56 | * 57 | * @param string $device the name of the device to be used 58 | * @return bool 59 | */ 60 | function deviceSet($device) 61 | { 62 | if ($this->_dState !== self::SERIAL_DEVICE_OPENED) { 63 | if ($this->_os === "linux") { 64 | if (preg_match("@^COM(\d+):?$@i", $device, $matches)) { 65 | $device = "/dev/ttyS" . ($matches[1] - 1); 66 | } 67 | 68 | if ($this->_exec("stty -F " . $device) === 0) { 69 | $this->_device = $device; 70 | $this->_dState = self::SERIAL_DEVICE_SET; 71 | return true; 72 | } 73 | } 74 | trigger_error("Specified serial port is not valid", E_USER_WARNING); 75 | return false; 76 | } else { 77 | trigger_error("You must close your device before to set an other one", E_USER_WARNING); 78 | return false; 79 | } 80 | } 81 | 82 | /** 83 | * Opens the device for reading and/or writing. 84 | * 85 | * @param string $mode Opening mode : same parameter as fopen() 86 | * @return bool 87 | */ 88 | function deviceOpen($mode = "r+b") 89 | { 90 | if ($this->_dState === self::SERIAL_DEVICE_OPENED) { 91 | trigger_error("The device is already opened", E_USER_NOTICE); 92 | return true; 93 | } 94 | 95 | if ($this->_dState === self::SERIAL_DEVICE_NOTSET) { 96 | trigger_error("The device must be set before to be open", E_USER_WARNING); 97 | return false; 98 | } 99 | 100 | if (!preg_match("@^[raw]\+?b?$@", $mode)) { 101 | trigger_error("Invalid opening mode : " . $mode . ". Use fopen() modes.", E_USER_WARNING); 102 | return false; 103 | } 104 | 105 | $this->_dHandle = @fopen($this->_device, $mode); 106 | 107 | if ($this->_dHandle !== false) { 108 | stream_set_blocking($this->_dHandle, 0); 109 | $this->_dState = self::SERIAL_DEVICE_OPENED; 110 | return true; 111 | } 112 | 113 | $this->_dHandle = null; 114 | trigger_error("Unable to open the device", E_USER_WARNING); 115 | return false; 116 | } 117 | 118 | /** 119 | * Closes the device 120 | * 121 | * @return bool 122 | */ 123 | function deviceClose() 124 | { 125 | if ($this->_dState !== self::SERIAL_DEVICE_OPENED) { 126 | return true; 127 | } 128 | 129 | if (fclose($this->_dHandle)) { 130 | $this->_dHandle = null; 131 | $this->_dState = self::SERIAL_DEVICE_SET; 132 | return true; 133 | } 134 | 135 | trigger_error("Unable to close the device", E_USER_ERROR); 136 | return false; 137 | } 138 | 139 | // 140 | // OPEN/CLOSE DEVICE SECTION -- {STOP} 141 | // 142 | // 143 | // CONFIGURE SECTION -- {START} 144 | // 145 | /** 146 | * Configure the Baud Rate 147 | * Possible rates : 110, 150, 300, 600, 1200, 2400, 4800, 9600, 38400, 148 | * 57600 and 115200. 149 | * 150 | * @param int $rate the rate to set the port in 151 | * @return bool 152 | */ 153 | function confBaudRate($rate) 154 | { 155 | if ($this->_dState !== self::SERIAL_DEVICE_SET) { 156 | trigger_error("Unable to set the baud rate : the device is either not set or opened", E_USER_WARNING); 157 | return false; 158 | } 159 | 160 | $validBauds = array( 161 | 110 => 11, 162 | 150 => 15, 163 | 300 => 30, 164 | 600 => 60, 165 | 1200 => 12, 166 | 2400 => 24, 167 | 4800 => 48, 168 | 9600 => 96, 169 | 19200 => 19, 170 | 38400 => 38400, 171 | 57600 => 57600, 172 | 115200 => 115200 173 | ); 174 | 175 | if (isset($validBauds[$rate])) { 176 | if ($this->_os === "linux") { 177 | $ret = $this->_exec("stty -F " . $this->_device . " " . (int) $rate, $out); 178 | } else { 179 | return false; 180 | } 181 | 182 | if ($ret !== 0) { 183 | trigger_error("Unable to set baud rate: " . $out[1], E_USER_WARNING); 184 | return false; 185 | } 186 | } 187 | } 188 | 189 | /** 190 | * Configure parity. 191 | * Modes : odd, even, none 192 | * 193 | * @param string $parity one of the modes 194 | * @return bool 195 | */ 196 | function confParity($parity) 197 | { 198 | if ($this->_dState !== self::SERIAL_DEVICE_SET) { 199 | trigger_error("Unable to set parity : the device is either not set or opened", E_USER_WARNING); 200 | return false; 201 | } 202 | 203 | $args = array( 204 | "none" => "-parenb", 205 | "odd" => "parenb parodd", 206 | "even" => "parenb -parodd", 207 | ); 208 | 209 | if (!isset($args[$parity])) { 210 | trigger_error("Parity mode not supported", E_USER_WARNING); 211 | return false; 212 | } 213 | 214 | if ($this->_os === "linux") { 215 | $ret = $this->_exec("stty -F " . $this->_device . " " . $args[$parity], $out); 216 | } 217 | 218 | if ($ret === 0) { 219 | return true; 220 | } 221 | 222 | trigger_error("Unable to set parity : " . $out[1], E_USER_WARNING); 223 | return false; 224 | } 225 | 226 | /** 227 | * Sets the length of a character. 228 | * 229 | * @param int $int length of a character (5 <= length <= 8) 230 | * @return bool 231 | */ 232 | function confCharacterLength($int) 233 | { 234 | if ($this->_dState !== self::SERIAL_DEVICE_SET) { 235 | trigger_error("Unable to set length of a character : the device is either not set or opened", E_USER_WARNING); 236 | return false; 237 | } 238 | 239 | $int = (int) $int; 240 | if ($int < 5) 241 | $int = 5; 242 | elseif ($int > 8) 243 | $int = 8; 244 | 245 | if ($this->_os === "linux") { 246 | $ret = $this->_exec("stty -F " . $this->_device . " cs" . $int, $out); 247 | } 248 | 249 | if ($ret === 0) { 250 | return true; 251 | } 252 | 253 | trigger_error("Unable to set character length : " . $out[1], E_USER_WARNING); 254 | return false; 255 | } 256 | 257 | /** 258 | * Sets the length of stop bits. 259 | * 260 | * @param float $length the length of a stop bit. It must be either 1, 261 | * 1.5 or 2. 1.5 is not supported under linux and on some computers. 262 | * @return bool 263 | */ 264 | function confStopBits($length) 265 | { 266 | if ($this->_dState !== self::SERIAL_DEVICE_SET) { 267 | trigger_error("Unable to set the length of a stop bit : the device is either not set or opened", E_USER_WARNING); 268 | return false; 269 | } 270 | 271 | if ($length != 1 and $length != 2 and $length != 1.5 and !($length == 1.5 and $this->_os === "linux")) { 272 | trigger_error("Specified stop bit length is invalid", E_USER_WARNING); 273 | return false; 274 | } 275 | 276 | if ($this->_os === "linux") { 277 | $ret = $this->_exec("stty -F " . $this->_device . " " . (($length == 1) ? "-" : "") . "cstopb", $out); 278 | } 279 | 280 | if ($ret === 0) { 281 | return true; 282 | } 283 | 284 | trigger_error("Unable to set stop bit length : " . $out[1], E_USER_WARNING); 285 | return false; 286 | } 287 | 288 | /** 289 | * Configures the flow control 290 | * 291 | * @param string $mode Set the flow control mode. Availible modes : 292 | * -> "none" : no flow control 293 | * -> "rts/cts" : use RTS/CTS handshaking 294 | * -> "xon/xoff" : use XON/XOFF protocol 295 | * @return bool 296 | */ 297 | function confFlowControl($mode) 298 | { 299 | if ($this->_dState !== self::SERIAL_DEVICE_SET) { 300 | trigger_error("Unable to set flow control mode : the device is either not set or opened", E_USER_WARNING); 301 | return false; 302 | } 303 | 304 | $linuxModes = array( 305 | "none" => "clocal -crtscts -ixon -ixoff", 306 | "rts/cts" => "-clocal crtscts -ixon -ixoff", 307 | "xon/xoff" => "-clocal -crtscts ixon ixoff" 308 | ); 309 | 310 | if ($mode !== "none" and $mode !== "rts/cts" and $mode !== "xon/xoff") { 311 | trigger_error("Invalid flow control mode specified", E_USER_ERROR); 312 | return false; 313 | } 314 | 315 | if ($this->_os === "linux") { 316 | $ret = $this->_exec("stty -F " . $this->_device . " " . $linuxModes[$mode], $out); 317 | } 318 | 319 | if ($ret === 0) { 320 | return true; 321 | } else { 322 | trigger_error("Unable to set flow control : " . $out[1], E_USER_ERROR); 323 | return false; 324 | } 325 | } 326 | 327 | /** 328 | * Sets a setserial parameter (cf man setserial) 329 | * NO MORE USEFUL ! 330 | * -> No longer supported 331 | * -> Only use it if you need it 332 | * 333 | * @param string $param parameter name 334 | * @param string $arg parameter value 335 | * @return bool 336 | */ 337 | function setSetserialFlag($param, $arg = "") 338 | { 339 | if (!$this->_ckOpened()) 340 | return false; 341 | 342 | $return = exec("setserial " . $this->_device . " " . $param . " " . $arg . " 2>&1"); 343 | 344 | if ($return{0} === "I") { 345 | trigger_error("setserial: Invalid flag", E_USER_WARNING); 346 | return false; 347 | } elseif ($return{0} === "/") { 348 | trigger_error("setserial: Error with device file", E_USER_WARNING); 349 | return false; 350 | } else { 351 | return true; 352 | } 353 | } 354 | 355 | /** 356 | * Sends a string to the device 357 | * 358 | * @param string $str string to be sent to the device 359 | * @param float $waitForReply time to wait for the reply (in seconds) 360 | */ 361 | function sendMessage($str, $waitForReply = 0.1) 362 | { 363 | $this->_buffer .= $str; 364 | $this->flush(); 365 | 366 | usleep((int) ($waitForReply * 1000000)); 367 | } 368 | 369 | private $_validOutputs = array(); 370 | public function setValidOutputs($validOutputs) 371 | { 372 | $this->_validOutputs = $validOutputs; 373 | } 374 | 375 | /** 376 | * Reads the port until no new datas are availible, then return the content. 377 | * 378 | * @pararm int $count number of characters to be read (will stop before 379 | * if less characters are in the buffer) 380 | * @return string 381 | */ 382 | function readPort() 383 | { 384 | if ($this->_dState !== self::SERIAL_DEVICE_OPENED) { 385 | trigger_error("Device must be opened to read it", E_USER_WARNING); 386 | return false; 387 | } 388 | if ($this->_os === "linux") { 389 | $last = null; 390 | $buffer = array(); 391 | 392 | if ($this->_dHandle) { 393 | $_buffer = ""; 394 | while (!in_array($last, $this->_validOutputs)) { 395 | $bit = fread($this->_dHandle, 1); 396 | if ($bit == "\n") { 397 | $last = strtoupper(trim(strtoupper($_buffer))); 398 | $buffer[] = $_buffer; 399 | $_buffer = ""; 400 | } else { 401 | $_buffer .= $bit; 402 | 403 | } 404 | } 405 | return array($last, $buffer); 406 | } 407 | } 408 | return false; 409 | } 410 | 411 | /** 412 | * Flushes the output buffer 413 | * 414 | * @return bool 415 | */ 416 | private function flush() 417 | { 418 | if (!$this->_ckOpened()) 419 | return false; 420 | 421 | if (fwrite($this->_dHandle, $this->_buffer) !== false) { 422 | $this->_buffer = ""; 423 | return true; 424 | } else { 425 | $this->_buffer = ""; 426 | trigger_error("Error while sending message", E_USER_WARNING); 427 | return false; 428 | } 429 | } 430 | 431 | private function _ckOpened() 432 | { 433 | if ($this->_dState !== self::SERIAL_DEVICE_OPENED) { 434 | trigger_error("Device must be opened", E_USER_WARNING); 435 | return false; 436 | } 437 | 438 | return true; 439 | } 440 | 441 | private function _ckClosed() 442 | { 443 | if ($this->_dState !== SERIAL_DEVICE_CLOSED) { 444 | trigger_error("Device must be closed", E_USER_WARNING); 445 | return false; 446 | } 447 | 448 | return true; 449 | } 450 | 451 | private function _exec($cmd, &$out = null) 452 | { 453 | $desc = array( 454 | 1 => array("pipe", "w"), 455 | 2 => array("pipe", "w") 456 | ); 457 | 458 | $proc = proc_open($cmd, $desc, $pipes); 459 | 460 | $ret = stream_get_contents($pipes[1]); 461 | $err = stream_get_contents($pipes[2]); 462 | 463 | fclose($pipes[1]); 464 | fclose($pipes[2]); 465 | 466 | $retVal = proc_close($proc); 467 | 468 | if (func_num_args() == 2) 469 | $out = array($ret, $err); 470 | return $retVal; 471 | } 472 | 473 | } --------------------------------------------------------------------------------