From 48c5f59300990e770a04441bb58c4857d2121e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Val=C3=AD=C4=8Dek?= Date: Thu, 24 Nov 2016 23:45:21 +0100 Subject: [PATCH] Skripty pro ladeni spinelu pod linuxem /dev/ttyUSB0 --- install | 12 + lib/Colors.php | 112 ++++++++ lib/PhpSerial.php | 704 ++++++++++++++++++++++++++++++++++++++++++++++ rundump | 9 + setty | 4 + spineldump | 164 +++++++++++ 6 files changed, 1005 insertions(+) create mode 100755 install create mode 100644 lib/Colors.php create mode 100644 lib/PhpSerial.php create mode 100755 rundump create mode 100755 setty create mode 100755 spineldump diff --git a/install b/install new file mode 100755 index 0000000..1f882df --- /dev/null +++ b/install @@ -0,0 +1,12 @@ +#!/bin/bash + +dir=$(dirname $(realpath $0)) + +I="setty spineldump rundump lib" + +for i in $I +do + scp -r $dir/$i root@teplomer.juricekpavel.net: +done + +ssh root@teplomer.juricekpavel.net touch /tmp/reloadspineldump diff --git a/lib/Colors.php b/lib/Colors.php new file mode 100644 index 0000000..fa7e53c --- /dev/null +++ b/lib/Colors.php @@ -0,0 +1,112 @@ + '0;30', + 'dark_gray' => '1;30', + 'blue' => '0;34', + 'light_blue' => '1;34', + 'green' => '0;32', + 'light_green' => '1;32', + 'cyan' => '0;36', + 'light_cyan' => '1;36', + 'red' => '0;31', + 'light_red' => '1;31', + 'purple' => '0;35', + 'light_purple' => '1;35', + 'brown' => '0;33', + 'yellow' => '1;33', + 'light_gray' => '0;37', + 'white' => '1;37', + ]; + } + + + static function getBackgrounds() + { + + return [ + 'black' => '40', + 'red' => '41', + 'green' => '42', + 'yellow' => '43', + 'blue' => '44', + 'magenta' => '45', + 'cyan' => '46', + ]; + } + + + static function nl() + { + + echo "\n"; + } + + static function output($text, $fg = "white", $bg = "black") + { + + $c = self::getColors(); + $b = self::getBackgrounds(); + + if (isset($c[$fg])) { + echo "\033[" . $c[$fg] . "m"; + } + if (isset($b[$bg])) { + echo "\033[" . $b[$bg] . "m"; + } + echo $text . "\033[0m"; + } + + + static function red($text, $bg = "black") + { + self::output($text, "red", $bg); + } + + static function lred($text, $bg = "black") + { + self::output($text, "light_red", $bg); + } + + static function green($text, $bg = "black") + { + self::output($text, "green", $bg); + } + + static function blue($text, $bg = "black") + { + self::output($text, "blue", $bg); + } + + static function lblue($text, $bg = "black") + { + self::output($text, "light_blue", $bg); + } + + static function yellow($text, $bg = "black") + { + self::output($text, "yellow", $bg); + } + + static function cyan($text, $bg = "black") + { + self::output($text, "cyan", $bg); + } + + static function white($text, $bg = "black") + { + self::output($text, "white", $bg); + } + + + +} \ No newline at end of file diff --git a/lib/PhpSerial.php b/lib/PhpSerial.php new file mode 100644 index 0000000..1d253d5 --- /dev/null +++ b/lib/PhpSerial.php @@ -0,0 +1,704 @@ + + * @author Rizwan Kassim + * @thanks Aurélien Derouineau for finding how to open serial ports with windows + * @thanks Alec Avedisyan for help and testing with reading + * @thanks Jim Wright for OSX cleanup/fixes. + * @copyright under GPL 2 licence + */ +class PhpSerial +{ + public $_device = null; + public $_winDevice = null; + public $_dHandle = null; + public $_dState = SERIAL_DEVICE_NOTSET; + public $_buffer = ""; + public $_os = ""; + + /** + * This var says if buffer should be flushed by sendMessage (true) or + * manually (false) + * + * @var bool + */ + public $autoFlush = true; + + /** + * Constructor. Perform some checks about the OS and setserial + * + * @return PhpSerial + */ + public function PhpSerial() + { + setlocale(LC_ALL, "en_US"); + + $sysName = php_uname(); + + if (substr($sysName, 0, 5) === "Linux") { + $this->_os = "linux"; + + if ($this->_exec("stty") === 0) { + register_shutdown_function(array($this, "deviceClose")); + } else { + trigger_error( + "No stty availible, unable to run.", + E_USER_ERROR + ); + } + } elseif (substr($sysName, 0, 6) === "Darwin") { + $this->_os = "osx"; + register_shutdown_function(array($this, "deviceClose")); + } elseif (substr($sysName, 0, 7) === "Windows") { + $this->_os = "windows"; + register_shutdown_function(array($this, "deviceClose")); + } else { + trigger_error("Host OS is neither osx, linux nor windows, unable " . + "to run.", E_USER_ERROR); + exit(); + } + } + + // + // OPEN/CLOSE DEVICE SECTION -- {START} + // + + /** + * Device set function : used to set the device name/address. + * -> linux : use the device address, like /dev/ttyS0 + * -> osx : use the device address, like /dev/tty.serial + * -> windows : use the COMxx device name, like COM1 (can also be used + * with linux) + * + * @param string $device the name of the device to be used + * @return bool + */ + public function deviceSet($device) + { + if ($this->_dState !== SERIAL_DEVICE_OPENED) { + 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; + } + } elseif ($this->_os === "osx") { + if ($this->_exec("stty -f " . $device) === 0) { + $this->_device = $device; + $this->_dState = SERIAL_DEVICE_SET; + + return true; + } + } elseif ($this->_os === "windows") { + if (preg_match("@^COM(\\d+):?$@i", $device, $matches) + and $this->_exec( + exec("mode " . $device . " xon=on BAUD=9600") + ) === 0 + ) { + $this->_winDevice = "COM" . $matches[1]; + $this->_device = "\\.com" . $matches[1]; + $this->_dState = SERIAL_DEVICE_SET; + + return true; + } + } + + trigger_error("Specified serial port is not valid", E_USER_WARNING); + + return false; + } else { + trigger_error("You must close your device before to set an other " . + "one", E_USER_WARNING); + + return false; + } + } + + /** + * Opens the device for reading and/or writing. + * + * @param string $mode Opening mode : same parameter as fopen() + * @return bool + */ + public function deviceOpen($mode = "r+b") + { + if ($this->_dState === SERIAL_DEVICE_OPENED) { + trigger_error("The device is already opened", E_USER_NOTICE); + + return true; + } + + if ($this->_dState === SERIAL_DEVICE_NOTSET) { + trigger_error( + "The device must be set before to be open", + E_USER_WARNING + ); + + return false; + } + + if (!preg_match("@^[raw]\\+?b?$@", $mode)) { + trigger_error( + "Invalid opening mode : ".$mode.". Use fopen() modes.", + E_USER_WARNING + ); + + return false; + } + + $this->_dHandle = @fopen($this->_device, $mode); + + if ($this->_dHandle !== false) { + stream_set_blocking($this->_dHandle, 0); + $this->_dState = SERIAL_DEVICE_OPENED; + + return true; + } + + $this->_dHandle = null; + trigger_error("Unable to open the device", E_USER_WARNING); + + return false; + } + + /** + * Closes the device + * + * @return bool + */ + public function deviceClose() + { + if ($this->_dState !== SERIAL_DEVICE_OPENED) { + return true; + } + + if (fclose($this->_dHandle)) { + $this->_dHandle = null; + $this->_dState = SERIAL_DEVICE_SET; + + return true; + } + + trigger_error("Unable to close the device", E_USER_ERROR); + + return false; + } + + // + // OPEN/CLOSE DEVICE SECTION -- {STOP} + // + + // + // CONFIGURE SECTION -- {START} + // + + /** + * Configure the Baud Rate + * Possible rates : 110, 150, 300, 600, 1200, 2400, 4800, 9600, 38400, + * 57600 and 115200. + * + * @param int $rate the rate to set the port in + * @return bool + */ + public function confBaudRate($rate) + { + if ($this->_dState !== SERIAL_DEVICE_SET) { + trigger_error("Unable to set the baud rate : the device is " . + "either not set or opened", E_USER_WARNING); + + return false; + } + + $validBauds = array ( + 110 => 11, + 150 => 15, + 300 => 30, + 600 => 60, + 1200 => 12, + 2400 => 24, + 4800 => 48, + 9600 => 96, + 19200 => 19, + 38400 => 38400, + 57600 => 57600, + 115200 => 115200 + ); + + if (isset($validBauds[$rate])) { + if ($this->_os === "linux") { + $ret = $this->_exec( + "stty -F " . $this->_device . " " . (int) $rate, + $out + ); + } elseif ($this->_os === "osx") { + $ret = $this->_exec( + "stty -f " . $this->_device . " " . (int) $rate, + $out + ); + } elseif ($this->_os === "windows") { + $ret = $this->_exec( + "mode " . $this->_winDevice . " BAUD=" . $validBauds[$rate], + $out + ); + } else { + return false; + } + + if ($ret !== 0) { + trigger_error( + "Unable to set baud rate: " . $out[1], + E_USER_WARNING + ); + + return false; + } + + return true; + } else { + return false; + } + } + + /** + * Configure parity. + * Modes : odd, even, none + * + * @param string $parity one of the modes + * @return bool + */ + public function confParity($parity) + { + if ($this->_dState !== SERIAL_DEVICE_SET) { + trigger_error( + "Unable to set parity : the device is either not set or opened", + E_USER_WARNING + ); + + return false; + } + + $args = array( + "none" => "-parenb", + "odd" => "parenb parodd", + "even" => "parenb -parodd", + ); + + if (!isset($args[$parity])) { + trigger_error("Parity mode not supported", E_USER_WARNING); + + return false; + } + + if ($this->_os === "linux") { + $ret = $this->_exec( + "stty -F " . $this->_device . " " . $args[$parity], + $out + ); + } elseif ($this->_os === "osx") { + $ret = $this->_exec( + "stty -f " . $this->_device . " " . $args[$parity], + $out + ); + } else { + $ret = $this->_exec( + "mode " . $this->_winDevice . " PARITY=" . $parity{0}, + $out + ); + } + + if ($ret === 0) { + return true; + } + + trigger_error("Unable to set parity : " . $out[1], E_USER_WARNING); + + return false; + } + + /** + * Sets the length of a character. + * + * @param int $int length of a character (5 <= length <= 8) + * @return bool + */ + public function confCharacterLength($int) + { + if ($this->_dState !== SERIAL_DEVICE_SET) { + trigger_error("Unable to set length of a character : the device " . + "is either not set or opened", E_USER_WARNING); + + return false; + } + + $int = (int) $int; + if ($int < 5) { + $int = 5; + } elseif ($int > 8) { + $int = 8; + } + + if ($this->_os === "linux") { + $ret = $this->_exec( + "stty -F " . $this->_device . " cs" . $int, + $out + ); + } elseif ($this->_os === "osx") { + $ret = $this->_exec( + "stty -f " . $this->_device . " cs" . $int, + $out + ); + } else { + $ret = $this->_exec( + "mode " . $this->_winDevice . " DATA=" . $int, + $out + ); + } + + if ($ret === 0) { + return true; + } + + trigger_error( + "Unable to set character length : " .$out[1], + E_USER_WARNING + ); + + return false; + } + + /** + * Sets the length of stop bits. + * + * @param float $length the length of a stop bit. It must be either 1, + * 1.5 or 2. 1.5 is not supported under linux and on + * some computers. + * @return bool + */ + public function confStopBits($length) + { + if ($this->_dState !== SERIAL_DEVICE_SET) { + trigger_error("Unable to set the length of a stop bit : the " . + "device is either not set or opened", E_USER_WARNING); + + return false; + } + + if ($length != 1 + and $length != 2 + and $length != 1.5 + and !($length == 1.5 and $this->_os === "linux") + ) { + trigger_error( + "Specified stop bit length is invalid", + E_USER_WARNING + ); + + return false; + } + + if ($this->_os === "linux") { + $ret = $this->_exec( + "stty -F " . $this->_device . " " . + (($length == 1) ? "-" : "") . "cstopb", + $out + ); + } elseif ($this->_os === "osx") { + $ret = $this->_exec( + "stty -f " . $this->_device . " " . + (($length == 1) ? "-" : "") . "cstopb", + $out + ); + } else { + $ret = $this->_exec( + "mode " . $this->_winDevice . " STOP=" . $length, + $out + ); + } + + if ($ret === 0) { + return true; + } + + trigger_error( + "Unable to set stop bit length : " . $out[1], + E_USER_WARNING + ); + + return false; + } + + /** + * Configures the flow control + * + * @param string $mode Set the flow control mode. Availible modes : + * -> "none" : no flow control + * -> "rts/cts" : use RTS/CTS handshaking + * -> "xon/xoff" : use XON/XOFF protocol + * @return bool + */ + public function confFlowControl($mode) + { + if ($this->_dState !== SERIAL_DEVICE_SET) { + trigger_error("Unable to set flow control mode : the device is " . + "either not set or opened", E_USER_WARNING); + + return false; + } + + $linuxModes = array( + "none" => "clocal -crtscts -ixon -ixoff", + "rts/cts" => "-clocal crtscts -ixon -ixoff", + "xon/xoff" => "-clocal -crtscts ixon ixoff" + ); + $windowsModes = array( + "none" => "xon=off octs=off rts=on", + "rts/cts" => "xon=off octs=on rts=hs", + "xon/xoff" => "xon=on octs=off rts=on", + ); + + if ($mode !== "none" and $mode !== "rts/cts" and $mode !== "xon/xoff") { + trigger_error("Invalid flow control mode specified", E_USER_ERROR); + + return false; + } + + if ($this->_os === "linux") { + $ret = $this->_exec( + "stty -F " . $this->_device . " " . $linuxModes[$mode], + $out + ); + } elseif ($this->_os === "osx") { + $ret = $this->_exec( + "stty -f " . $this->_device . " " . $linuxModes[$mode], + $out + ); + } else { + $ret = $this->_exec( + "mode " . $this->_winDevice . " " . $windowsModes[$mode], + $out + ); + } + + if ($ret === 0) { + return true; + } else { + trigger_error( + "Unable to set flow control : " . $out[1], + E_USER_ERROR + ); + + return false; + } + } + + /** + * Sets a setserial parameter (cf man setserial) + * NO MORE USEFUL ! + * -> No longer supported + * -> Only use it if you need it + * + * @param string $param parameter name + * @param string $arg parameter value + * @return bool + */ + public function setSetserialFlag($param, $arg = "") + { + if (!$this->_ckOpened()) { + return false; + } + + $return = exec( + "setserial " . $this->_device . " " . $param . " " . $arg . " 2>&1" + ); + + if ($return{0} === "I") { + trigger_error("setserial: Invalid flag", E_USER_WARNING); + + return false; + } elseif ($return{0} === "/") { + trigger_error("setserial: Error with device file", E_USER_WARNING); + + return false; + } else { + return true; + } + } + + // + // CONFIGURE SECTION -- {STOP} + // + + // + // I/O SECTION -- {START} + // + + /** + * Sends a string to the device + * + * @param string $str string to be sent to the device + * @param float $waitForReply time to wait for the reply (in seconds) + */ + public function sendMessage($str, $waitForReply = 0.1) + { + $this->_buffer .= $str; + + if ($this->autoFlush === true) { + $this->serialflush(); + } + + usleep((int) ($waitForReply * 1000000)); + } + + /** + * Reads the port until no new datas are availible, then return the content. + * + * @param int $count Number of characters to be read (will stop before + * if less characters are in the buffer) + * @return string + */ + public 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") { + // Windows port reading procedures still buggy + $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; + } + + return false; + } + + /** + * Flushes the output buffer + * Renamed from flush for osx compat. issues + * + * @return bool + */ + public function serialflush() + { + if (!$this->_ckOpened()) { + return false; + } + + if (fwrite($this->_dHandle, $this->_buffer) !== false) { + $this->_buffer = ""; + + return true; + } else { + $this->_buffer = ""; + trigger_error("Error while sending message", E_USER_WARNING); + + return false; + } + } + + // + // I/O SECTION -- {STOP} + // + + // + // INTERNAL TOOLKIT -- {START} + // + + public function _ckOpened() + { + if ($this->_dState !== SERIAL_DEVICE_OPENED) { + trigger_error("Device must be opened", E_USER_WARNING); + + return false; + } + + return true; + } + + public function _ckClosed() + { + if ($this->_dState === SERIAL_DEVICE_OPENED) { + trigger_error("Device must be closed", E_USER_WARNING); + + return false; + } + + return true; + } + + public function _exec($cmd, &$out = null) + { + $desc = array( + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + + $proc = proc_open($cmd, $desc, $pipes); + + $ret = stream_get_contents($pipes[1]); + $err = stream_get_contents($pipes[2]); + + fclose($pipes[1]); + fclose($pipes[2]); + + $retVal = proc_close($proc); + + if (func_num_args() == 2) $out = array($ret, $err); + return $retVal; + } + + // + // INTERNAL TOOLKIT -- {STOP} + // +} diff --git a/rundump b/rundump new file mode 100755 index 0000000..771ad52 --- /dev/null +++ b/rundump @@ -0,0 +1,9 @@ +#!/bin/bash + +./setty + +while true +do + ./spineldump + [ -f /tmp/reloadspineldump ] && rm /tmp/reloadspineldump || exit +done diff --git a/setty b/setty new file mode 100755 index 0000000..942d6c8 --- /dev/null +++ b/setty @@ -0,0 +1,4 @@ +#!/bin/bash + +stty -F /dev/ttyUSB0 speed 38400 -icrnl -imaxbel -opost -onlcr -isig -icanon -echo + diff --git a/spineldump b/spineldump new file mode 100755 index 0000000..063924c --- /dev/null +++ b/spineldump @@ -0,0 +1,164 @@ +#!/usr/bin/env php +deviceSet("/dev/ttyUSB0"); +// We can change the baud rate, parity, length, stop bits, flow control +$serial->confBaudRate(38400); +$serial->confParity("none"); +$serial->confCharacterLength(8); +$serial->confStopBits(1); +$serial->confFlowControl("none"); + +$serial->deviceOpen(); + +$reqTimeout = 2; +$reqOpened = FALSE; +$reqPos = 0; + + +$rForm = 0; +$rLen = 999; +$rAddr = 0; +$rSig = 0; +$thrownAway = 0; + +$mtStart = mt(); +$mtEnd = mt(); + +// nekonečná smyčka +while (TRUE) { + // exit + checkExit(); + // timeout + if ($reqOpened && (($mtStart + $reqTimeout) < mt())) { + + C::red(C::BOLD . " Timeout reached... restarting transaction\n"); + $reqOpened = FALSE; + } + + // načtení bajtu + $str = $serial->readPort(1); + if (strlen($str) == 0) { + // nenačetl, tak pokračujeme + continue; + } + + for ($i = 0; $i < strlen($str); $i++) { + $s = substr($str, $i, 1); + $thrownAway++; + + if ((!$reqOpened) && ($s == '*')) { + C::nl(); + C::green(C::BOLD . "*"); + $reqOpened = TRUE; + $reqPos = 1; + $mtStart = mt(); + + $rForm = 0; + $rLen = 999; + $rAddr = 0; + continue; + } + + if (!$reqOpened) { + continue; + } + + switch ($reqPos) { + // format + case 1: + $rForm = ord($s); + C::lblue(C::BOLD . $rForm); + $reqPos++; + break; + // prvni byte delky + case 2: + $rLen = ord($s); + $rLen *= 256; + $reqPos++; + break; + // druhy byte delky + case 3: + $rLen += ord($s); + echo " L:"; + C::yellow("$rLen"); + $reqPos++; + break; + // adresa + case 4: + $rAddr = bin2hex($s); + echo " A:0x"; + C::lred(C::BOLD . $rAddr); + $reqPos++; + break; + // signature + case 5: + $rSig = ord($s); + echo " S:"; + C::green(C::BOLD . $rSig); + $reqPos++; + break; + + default: + $reqPos++; + dumpbyte($s); + break; + } + + if (ord($s) == 13) { + + $reqOpened = FALSE; + $thrownAway = 0; + C::nl(); + echo "Stats: exp:"; + C::output($rLen, "purple"); + echo " recv:"; + C::output($reqPos - 4, 'light_green'); + $fromLast = round(1000*($mtStart - $mtEnd), 2); + $mtEnd = mt(); + $duration = round(1000*($mtEnd - $mtStart), 2); + echo " DIFF:"; + C::output($fromLast . "ms", 'cyan'); + echo " DT:"; + C::output($duration . "ms", 'light_cyan'); + + C::nl(); + + } + } +} +