Serial.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <?php
  2. if (!defined('BASEPATH')) {
  3. exit('No direct script access allowed');
  4. }
  5. define("SERIAL_DEVICE_NOTSET", 0);
  6. define("SERIAL_DEVICE_SET", 1);
  7. define("SERIAL_DEVICE_OPENED", 2);
  8. class Serial
  9. {
  10. var $_device = null;
  11. var $_windevice = null;
  12. var $_dHandle = null;
  13. var $_dState = SERIAL_DEVICE_NOTSET;
  14. var $_buffer = "";
  15. var $_os = "";
  16. var $autoflush = true;
  17. function Serial()
  18. {
  19. setlocale(LC_ALL, "en_US");
  20. $sysname = php_uname();
  21. if (substr($sysname, 0, 5) === "Linux") {
  22. $this->_os = "linux";
  23. if ($this->_exec("stty --version") === 0) {
  24. register_shutdown_function(array($this, "deviceClose"));
  25. } else {
  26. trigger_error("No stty availible, unable to run.", E_USER_ERROR);
  27. }
  28. } elseif (substr($sysname, 0, 7) === "Windows") {
  29. $this->_os = "windows";
  30. register_shutdown_function(array($this, "deviceClose"));
  31. } else {
  32. trigger_error("Host OS is neither linux nor windows, unable tu run.", E_USER_ERROR);
  33. exit();
  34. }
  35. }
  36. function deviceSet($device)
  37. {
  38. if ($this->_dState !== SERIAL_DEVICE_OPENED) {
  39. if ($this->_os === "linux") {
  40. if (preg_match("@^COM(\d+):?$@i", $device, $matches)) {
  41. $device = "/dev/ttyS" . ($matches[1] - 1);
  42. }
  43. if ($this->_exec("stty -F " . $device) === 0) {
  44. $this->_device = $device;
  45. $this->_dState = SERIAL_DEVICE_SET;
  46. return true;
  47. }
  48. } elseif ($this->_os === "windows") {
  49. if (preg_match("@^COM(\d+):?$@i", $device, $matches) and $this->_exec(exec("mode " . $device)) === 0) {
  50. $this->_windevice = "COM" . $matches[1];
  51. $this->_device = "\\.\com" . $matches[1];
  52. $this->_dState = SERIAL_DEVICE_SET;
  53. return true;
  54. }
  55. }
  56. //trigger_error("Specified serial port is not valid", E_USER_WARNING);
  57. return false;
  58. } else {
  59. //trigger_error("You must close your device before to set an other one", E_USER_WARNING);
  60. return false;
  61. }
  62. }
  63. function deviceOpen($mode = "r+b")
  64. {
  65. if ($this->_dState === SERIAL_DEVICE_OPENED) {
  66. //trigger_error("The device is already opened", E_USER_NOTICE);
  67. return true;
  68. }
  69. if ($this->_dState === SERIAL_DEVICE_NOTSET) {
  70. //trigger_error("The device must be set before to be open", E_USER_WARNING);
  71. return false;
  72. }
  73. if (!preg_match("@^[raw]\+?b?$@", $mode)) {
  74. //trigger_error("Invalid opening mode : " . $mode . ". Use fopen() modes.", E_USER_WARNING);
  75. return false;
  76. }
  77. $this->_dHandle = @fopen($this->_device, $mode);
  78. if ($this->_dHandle !== false) {
  79. stream_set_blocking($this->_dHandle, 0);
  80. $this->_dState = SERIAL_DEVICE_OPENED;
  81. return true;
  82. }
  83. $this->_dHandle = null;
  84. trigger_error("Unable to open the device", E_USER_WARNING);
  85. return false;
  86. }
  87. function deviceClose()
  88. {
  89. if ($this->_dState !== SERIAL_DEVICE_OPENED) {
  90. return true;
  91. }
  92. if (fclose($this->_dHandle)) {
  93. $this->_dHandle = null;
  94. $this->_dState = SERIAL_DEVICE_SET;
  95. return true;
  96. }
  97. trigger_error("Unable to close the device", E_USER_ERROR);
  98. return false;
  99. }
  100. function confBaudRate($rate)
  101. {
  102. if ($this->_dState !== SERIAL_DEVICE_SET) {
  103. trigger_error("Unable to set the baud rate : the device is either not set or opened", E_USER_WARNING);
  104. return false;
  105. }
  106. $validBauds = array(
  107. 110 => 11,
  108. 150 => 15,
  109. 300 => 30,
  110. 600 => 60,
  111. 1200 => 12,
  112. 2400 => 24,
  113. 4800 => 48,
  114. 9600 => 96,
  115. 19200 => 19,
  116. 38400 => 38400,
  117. 57600 => 57600,
  118. 115200 => 115200
  119. );
  120. if (isset($validBauds[$rate])) {
  121. if ($this->_os === "linux") {
  122. $ret = $this->_exec("stty -F " . $this->_device . " " . (int)$rate, $out);
  123. } elseif ($this->_os === "windows") {
  124. $ret = $this->_exec("mode " . $this->_windevice . " BAUD=" . $validBauds[$rate], $out);
  125. } else {
  126. return false;
  127. }
  128. if ($ret !== 0) {
  129. trigger_error("Unable to set baud rate: " . $out[1], E_USER_WARNING);
  130. return false;
  131. }
  132. }
  133. }
  134. function confParity($parity)
  135. {
  136. if ($this->_dState !== SERIAL_DEVICE_SET) {
  137. trigger_error("Unable to set parity : the device is either not set or opened", E_USER_WARNING);
  138. return false;
  139. }
  140. $args = array(
  141. "none" => "-parenb",
  142. "odd" => "parenb parodd",
  143. "even" => "parenb -parodd",
  144. );
  145. if (!isset($args[$parity])) {
  146. trigger_error("Parity mode not supported", E_USER_WARNING);
  147. return false;
  148. }
  149. if ($this->_os === "linux") {
  150. $ret = $this->_exec("stty -F " . $this->_device . " " . $args[$parity], $out);
  151. } else {
  152. $ret = $this->_exec("mode " . $this->_windevice . " PARITY=" . $parity{0}, $out);
  153. }
  154. if ($ret === 0) {
  155. return true;
  156. }
  157. trigger_error("Unable to set parity : " . $out[1], E_USER_WARNING);
  158. return false;
  159. }
  160. function confCharacterLength($int)
  161. {
  162. if ($this->_dState !== SERIAL_DEVICE_SET) {
  163. trigger_error("Unable to set length of a character : the device is either not set or opened", E_USER_WARNING);
  164. return false;
  165. }
  166. $int = (int)$int;
  167. if ($int < 5) $int = 5;
  168. elseif ($int > 8) $int = 8;
  169. if ($this->_os === "linux") {
  170. $ret = $this->_exec("stty -F " . $this->_device . " cs" . $int, $out);
  171. } else {
  172. $ret = $this->_exec("mode " . $this->_windevice . " DATA=" . $int, $out);
  173. }
  174. if ($ret === 0) {
  175. return true;
  176. }
  177. trigger_error("Unable to set character length : " . $out[1], E_USER_WARNING);
  178. return false;
  179. }
  180. function confStopBits($length)
  181. {
  182. if ($this->_dState !== SERIAL_DEVICE_SET) {
  183. trigger_error("Unable to set the length of a stop bit : the device is either not set or opened", E_USER_WARNING);
  184. return false;
  185. }
  186. if ($length != 1 and $length != 2 and $length != 1.5 and !($length == 1.5 and $this->_os === "linux")) {
  187. trigger_error("Specified stop bit length is invalid", E_USER_WARNING);
  188. return false;
  189. }
  190. if ($this->_os === "linux") {
  191. $ret = $this->_exec("stty -F " . $this->_device . " " . (($length == 1) ? "-" : "") . "cstopb", $out);
  192. } else {
  193. $ret = $this->_exec("mode " . $this->_windevice . " STOP=" . $length, $out);
  194. }
  195. if ($ret === 0) {
  196. return true;
  197. }
  198. trigger_error("Unable to set stop bit length : " . $out[1], E_USER_WARNING);
  199. return false;
  200. }
  201. function confFlowControl($mode)
  202. {
  203. if ($this->_dState !== SERIAL_DEVICE_SET) {
  204. trigger_error("Unable to set flow control mode : the device is either not set or opened", E_USER_WARNING);
  205. return false;
  206. }
  207. $linuxModes = array(
  208. "none" => "clocal -crtscts -ixon -ixoff",
  209. "rts/cts" => "-clocal crtscts -ixon -ixoff",
  210. "xon/xoff" => "-clocal -crtscts ixon ixoff"
  211. );
  212. $windowsModes = array(
  213. "none" => "xon=off octs=off rts=on",
  214. "rts/cts" => "xon=off octs=on rts=hs",
  215. "xon/xoff" => "xon=on octs=off rts=on",
  216. );
  217. if ($mode !== "none" and $mode !== "rts/cts" and $mode !== "xon/xoff") {
  218. trigger_error("Invalid flow control mode specified", E_USER_ERROR);
  219. return false;
  220. }
  221. if ($this->_os === "linux")
  222. $ret = $this->_exec("stty -F " . $this->_device . " " . $linuxModes[$mode], $out);
  223. else
  224. $ret = $this->_exec("mode " . $this->_windevice . " " . $windowsModes[$mode], $out);
  225. if ($ret === 0) return true;
  226. else {
  227. trigger_error("Unable to set flow control : " . $out[1], E_USER_ERROR);
  228. return false;
  229. }
  230. }
  231. function setSetserialFlag($param, $arg = "")
  232. {
  233. if (!$this->_ckOpened()) return false;
  234. $return = exec("setserial " . $this->_device . " " . $param . " " . $arg . " 2>&1");
  235. if ($return{0} === "I") {
  236. trigger_error("setserial: Invalid flag", E_USER_WARNING);
  237. return false;
  238. } elseif ($return{0} === "/") {
  239. trigger_error("setserial: Error with device file", E_USER_WARNING);
  240. return false;
  241. } else {
  242. return true;
  243. }
  244. }
  245. function sendMessage($str, $waitForReply = 0.1)
  246. {
  247. $this->_buffer .= $str;
  248. if ($this->autoflush === true) $this->flush();
  249. usleep((int)($waitForReply * 1000000));
  250. }
  251. function readPort($count = 0)
  252. {
  253. if ($this->_dState !== SERIAL_DEVICE_OPENED) {
  254. trigger_error("Device must be opened to read it", E_USER_WARNING);
  255. return false;
  256. }
  257. if ($this->_os === "linux") {
  258. $content = "";
  259. $i = 0;
  260. if ($count !== 0) {
  261. do {
  262. if ($i > $count) $content .= fread($this->_dHandle, ($count - $i));
  263. else $content .= fread($this->_dHandle, 128);
  264. } while (($i += 128) === strlen($content));
  265. } else {
  266. do {
  267. $content .= fread($this->_dHandle, 128);
  268. } while (($i += 128) === strlen($content));
  269. }
  270. return $content;
  271. } elseif ($this->_os === "windows") {
  272. $content = "";
  273. $i = 0;
  274. if ($count !== 0) {
  275. do {
  276. if ($i > $count) {
  277. $content .= fread($this->_dHandle, ($count - $i));
  278. } else {
  279. $content .= fread($this->_dHandle, 128);
  280. }
  281. } while (($i += 128) === strlen($content));
  282. } else {
  283. do {
  284. $content .= @fread($this->_dHandle, 128);
  285. } while (($i += 128) === strlen($content));
  286. }
  287. return $content;
  288. }
  289. trigger_error("Reading serial port is not implemented for Windows", E_USER_WARNING);
  290. return false;
  291. }
  292. function flush()
  293. {
  294. if (!$this->_ckOpened()) return false;
  295. if (fwrite($this->_dHandle, $this->_buffer) !== false) {
  296. $this->_buffer = "";
  297. return true;
  298. } else {
  299. $this->_buffer = "";
  300. trigger_error("Error while sending message", E_USER_WARNING);
  301. return false;
  302. }
  303. }
  304. function _ckOpened()
  305. {
  306. if ($this->_dState !== SERIAL_DEVICE_OPENED) {
  307. trigger_error("Device must be opened", E_USER_WARNING);
  308. return false;
  309. }
  310. return true;
  311. }
  312. function _ckClosed()
  313. {
  314. if ($this->_dState !== SERIAL_DEVICE_CLOSED) {
  315. trigger_error("Device must be closed", E_USER_WARNING);
  316. return false;
  317. }
  318. return true;
  319. }
  320. function _exec($cmd, &$out = null)
  321. {
  322. $desc = array(
  323. 1 => array("pipe", "w"),
  324. 2 => array("pipe", "w")
  325. );
  326. $proc = proc_open($cmd, $desc, $pipes);
  327. $ret = stream_get_contents($pipes[1]);
  328. $err = stream_get_contents($pipes[2]);
  329. fclose($pipes[1]);
  330. fclose($pipes[2]);
  331. $retVal = proc_close($proc);
  332. if (func_num_args() == 2) $out = array($ret, $err);
  333. return $retVal;
  334. }
  335. }