Skripty pro ladeni spinelu pod linuxem /dev/ttyUSB0

This commit is contained in:
Václav Valíček 2016-11-24 23:45:21 +01:00
commit 48c5f59300
6 changed files with 1005 additions and 0 deletions

12
install Executable file
View File

@ -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

112
lib/Colors.php Normal file
View File

@ -0,0 +1,112 @@
<?php
class C
{
const BOLD = "\033[1m";
static function getColors()
{
return [
'black' => '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);
}
}

704
lib/PhpSerial.php Normal file
View File

@ -0,0 +1,704 @@
<?php
define ("SERIAL_DEVICE_NOTSET", 0);
define ("SERIAL_DEVICE_SET", 1);
define ("SERIAL_DEVICE_OPENED", 2);
/**
* Serial port control class
*
* THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTIES !
* USE IT AT YOUR OWN RISKS !
*
* @author Rémy Sanchez <remy.sanchez@hyperthese.net>
* @author Rizwan Kassim <rizwank@geekymedia.com>
* @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}
//
}

9
rundump Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
./setty
while true
do
./spineldump
[ -f /tmp/reloadspineldump ] && rm /tmp/reloadspineldump || exit
done

4
setty Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
stty -F /dev/ttyUSB0 speed 38400 -icrnl -imaxbel -opost -onlcr -isig -icanon -echo

164
spineldump Executable file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env php
<?php
function checkExit()
{
if (file_exists("/tmp/reloadspineldump")) {
die("Must reload...");
}
}
function mt()
{
return microtime(TRUE);
}
function dumpbyte($b)
{
if (ctype_print($b)) {
C::cyan($b);
} else {
C::white(bin2hex($b));
}
}
echo "Spinel debugger (STDIN)!\n";
include __DIR__ . "/lib/PhpSerial.php";
include __DIR__ . "/lib/Colors.php";
$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/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();
}
}
}