顯示具有 hardware 標籤的文章。 顯示所有文章
顯示具有 hardware 標籤的文章。 顯示所有文章

2014年12月19日 星期五

LINUX 插入USB執行某個script

系統:
Arch linux

需求:
重開機後/dev/ttyUSB[0-9] 的設定會跑掉,我的板子頻率要跑9600 8n1 才能溝通

手動設定指令:
# stty -F /dev/ttyUSB1 raw speed 9600 -crtscts cs8 -parenb -cstopb

重開機自動設定:
原本在這篇 arch 開機執行程式 想要用rc.local做,但是stty在rc.local我一直無法設定,印出來的內容是空的,所以改從udev的rules下手
新增檔案 /etc/udev/rules.d/usb.rules ,內容為:
ACTION=="add", KERNEL=="ttyUSB*", RUN+="/usr/bin/bash /your_script_path/set_stty.sh"
功用為:
在/dev/ttyUSBx插入時,會跑set_stty.sh這個程式。

根據 Arch 官方文件 https://wiki.archlinux.org/index.php/udev ,必須命名 .rules 的檔案,而/etc/udev/rules.d/ 和 /usr/lib/udev/rules.d/ (由其他packages自動產生的rules)有同樣名字的rules時,以 /etc/udev/ 下的優先

set_stty.sh內容:
#!/bin/sh
LOGFILE=/tmp/set_stty.log
exec 3>> $LOGFILE && exec >& 3 && exec 2>&1
echo $SEQNUM $SUBSYSTEM $ACTION $DEVNAME
if [ "$SUBSYSTEM" = "tty" -a "$ACTION" = "add" ]
then
    echo ""
    echo "=============================="
    date "+%G-%m-%dT%H:%M:%S %z"
    echo "$SUBSYSTEM $ACTION"
    stty -F $DEVNAME raw speed 9600 -crtscts cs8 -parenb -cstopb
fi
exit $?  # 結束這個 script 並把當時的執行結果傳回(return)給呼叫這個 script 的任何程式。想要省略掉的話,也沒什麼問題,然而我們必須平時就養成撰寫程式的正確習慣,在 UNIX 系統上,任何程式、函式都必須要有傳回值,就算是不知道有誰會去用它。我們不希望看到那一天,當有人突然去用它的時候,就必須面對噩運。這看似小事一件,然而缺乏持續性對小節的正確習慣或態度。 by 翔

$SEQNUM $SUBSYSTEM $ACTION $DEVNAME 這些是uevents ,可以幫助我們在script裡面判斷
例如插入一次接板子的usb,在log中可以看到set_stty.sh跑了兩次
ex.
echo $SEQNUM $SUBSYSTEM $ACTION $DEVNAME 這行印到/tmp/set_stty.log的內容:
2355 usb-serial add "(空)"
2356 tty add /dev/ttyUSB1

實測:
重開機後,呼叫串口的web api ,有正常返回值

參考資料:
http://king70327.blogspot.jp/2011/12/linux-centosusbscript.html

2014年12月17日 星期三

減少PHP-Serial的I/O等待時間

繼前篇 用php透過usb和rs232串連控制MPU電路板
dummy.php:
echo "\ntime 0:".round(microtime(true) * 1000);
include 'PhpSerial.php';

echo "\ntime 1:".round(microtime(true) * 1000);
`stty -F /dev/ttyUSB0 raw speed 9600 -crtscts cs8 -parenb -cstopb`;
echo "\ntime 2:".round(microtime(true) * 1000);
// Let's start the class
$serial = new PhpSerial();
echo "\ntime 3:".round(microtime(true) * 1000);
// 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");
echo "\ntime 4:".round(microtime(true) * 1000);
// We can change the baud rate, parity, length, stop bits, flow control
$serial->confBaudRate(9600);
echo "\ntime 5:".round(microtime(true) * 1000);
$serial->confParity("none");
echo "\ntime 6:".round(microtime(true) * 1000);
$serial->confCharacterLength(8);
echo "\ntime 7:".round(microtime(true) * 1000);
$serial->confStopBits(1);
echo "\ntime 8:".round(microtime(true) * 1000);
$serial->confFlowControl("none");
echo "\ntime 9:".round(microtime(true) * 1000);
// Then we need to open it
$serial->deviceOpen();

// To write into
echo "\ntime A:".round(microtime(true) * 1000);
$serial->sendMessage("O(00,02,1)E");

測試:
$ time sudo php -n dummy.php ( -n 是不走php.ini的設定,因為我之前browscap.ini設定太肥會delay)

time 0:1418786501622
time 1:1418786501623
time 2:1418786501890
time 3:1418786501890
time 4:1418786501890
time 5:1418786502156
time 6:1418786502423
time 7:1418786502690
time 8:1418786502956
time 9:1418786503223
time A:1418786503223
time B:1418786504957 # 後面sleep了一秒,讓燈關掉
real    0m3.373s
user    0m0.007s
sys     0m0.003s

在 PHP-Serial API的底層,每叫一次function ( new PhpSerial()、$serial->deviceSet、$serial->confBaudRate )都會用stty去檢查一次,每次stty檢查都會在IO上delay 0.3秒,所以我們現在假設你的裝置都連結配置好,將多餘的設定和stty檢查拿掉

更改PhpSerial.php:
+++ b/PhpSerial.php
@@ -32,22 +32,27 @@ class PhpSerial
      * @var bool
      */
     public $autoFlush = true;
+    public $_check_stty = true;
 
     /**
      * Constructor. Perform some checks about the OS and setserial
      *
      * @return PhpSerial
      */
-    public function PhpSerial()
+    public function PhpSerial($param)
     {
+        $this->_check_stty = (isset($param['check_stty']))? $param['check_stty'] : $this->_check
         setlocale(LC_ALL, "en_US");
 
         $sysName = php_uname();
         if (substr($sysName, 0, 5) === "Linux") {
             $this->_os = "linux";
+            if (!$this->_check_stty) {
+                register_shutdown_function(array($this, "deviceClose"));
+                return false;
+            }
@@ -86,9 +91,12 @@ class PhpSerial
      */
     public function deviceSet($device)
     {
+        if (!$this->_check_stty) {
+            $this->_device = $device;
+            $this->_dState = SERIAL_DEVICE_SET;
+            return false;
+        }

修改dummy.php
echo "\ntime 0:".round(microtime(true) * 1000);
include 'PhpSerial.php';

echo "\ntime 1:".round(microtime(true) * 1000);
echo "\ntime 2:".round(microtime(true) * 1000);
$serial = new PhpSerial(array('check_stty'=> false));
echo "\ntime 3:".round(microtime(true) * 1000);
$serial->deviceSet("/dev/ttyUSB0");
echo "\ntime 4:".round(microtime(true) * 1000);
$serial->deviceOpen();

echo "\ntime A:".round(microtime(true) * 1000);
$serial->sendMessage("O(00,02,1)E");

測試:
$ time sudo php -n dummy.php

time 0:1418787683165
time 1:1418787683165
time 2:1418787683165
time 3:1418787683165
time 4:1418787683165
time A:1418787683166
time B:1418787684896 # 後面sleep了一秒,讓燈關掉
real    0m1.774s
user    0m0.000s
sys     0m0.007s



2014年12月4日 星期四

JMDM-COM10MR繼電器使用心得

上淘寶買了 10路繼電器輸出模塊 負載220V25A 串口通訊 串口控制器(JMDM-COM10MR,深圳市精敏数字机器有限公司)
内含:
JMDM-COM10MR 繼電器
12V電源適配器(可選24V) - 紅接1 黑接3
RS232 傳輸線 ( 要接USB需另買RS232轉USB傳輸線 )

共有10路輸出:
背面
側面(紅色數字為第幾號開關)
側面 (紅色數字為第幾號開關)
Linux控制方式:
事前設定:( /dev/ttyUSB0非唯一,依你繼電器插入後linux所抓到的device,可能是/dev/ttyUSBx )
# stty -F /dev/ttyUSB0 raw speed 9600 -crtscts cs8 -parenb -cstopb
# chmod 777 /dev/ttyUSB0
接收:
# cat /dev/ttyUSB0

查詢:
# echo -en 'O(00,40,1)E' > /dev/ttyUSB0 # 命令控制器返回第 1 路~第 10 路数据
OA(00,0000000000)E  # cat /dev/ttyUSB0收到的結果

控制:控制成功繼電器會發出開關的聲音,可用萬用表測有沒有迴路,中間和左邊兩個有通為0關閉,中間和右邊兩個有通為1打開)
# echo -en 'O(00,00,1)E' > /dev/ttyUSB0 # 所有继电器输出全開
OA(00,1111111111)E  # 用'O(00,40,1)E'查詢,在cat /dev/ttyUSB0收到的結果
# echo -en 'O(00,00,0)E' > /dev/ttyUSB0 # 所有继电器输出全關
OA(00,0000000000)E  # 用'O(00,40,1)E'查詢,在cat /dev/ttyUSB0收到的結果
# echo -en 'O(00,01,1)E' > /dev/ttyUSB0 # 第 1路继电器输出"开启"
OA(00,1000000000)E  # 用'O(00,40,1)E'查詢,在cat /dev/ttyUSB0收到的結果
# echo -en 'O(00,01,0)E' > /dev/ttyUSB0 # 第 1路继电器输出"關閉"
OA(00,0000000000)E  # 用'O(00,40,1)E'查詢,在cat /dev/ttyUSB0收到的結果
# echo -en 'O(00,02,1)E' > /dev/ttyUSB0 # 第 2路继电器输出"开启"
OA(00,0100000000)E  # 用'O(00,40,1)E'查詢,在cat /dev/ttyUSB0收到的結果
# echo -en 'O(00,02,0)E' > /dev/ttyUSB0 # 第 2路继电器输出"關閉"
OA(00,0100000000)E  # 用'O(00,40,1)E'查詢,在cat /dev/ttyUSB0收到的結果

注意:
1. 勿傳送hex,傳送的是字符(ASCII)
2. 淘宝和pdf文档里 关闭指令:O ( 00,N,0)E 开启指令:O ( 00,N,1)E
O ( 00,N,1)E => "(" 前後多了空白 所以没反应... 要把空白去掉,继电器吃到错误的代码不会报错回来

同時先關再開某個output(同時送兩個指令)到繼電器:
# echo -en 'O(00,00,0)E O(00,01,1)E' > /dev/ttyUSB1 #中間以一個空格隔開,就能同時關閉所有電燈再開某個電燈,而不會為了等serial port而慢一秒亮燈。

Windows(Win7)控制方式:
賣家會提供windows的執行檔(免安裝和安裝板)
安裝板裝好後要另外把他們提供的dll檔放到該目錄下
更新Win 7 的Prolific USB-to-Serial Comm Port 的驅動程式(電腦需連上網路)
( 開始 -> 設備和打印機 -> "未指定"中的Prolific USB-to-Serial Comm Port 右鍵 -> 屬性 -> 硬件 -> 屬性 -> 驅動程序 -> 更新驅動程序 )
設定COM port
開始 -> 設備和打印機 -> "未指定"中的Prolific USB-to-Serial Comm Port 右鍵 -> 屬性 -> 硬件 -> 屬性 -> 端口設置 -> 高級 -> COM端口號選"COM2"
廠商提供的應用程式 通信串口選擇:串口一 ( 注意必須在打開程式時預設選擇串口一,不然報錯後依然跑不動,串口設錯請設定串口後關閉程式重開,勿點擊按鈕讓程式自動關閉 )