Modbus是工業控制領域常用的通訊協定標準,像近代的PLC、電表、保護電譯、I/O模組...等,都會支援Modbus,如果你想要寫類似圖控軟體的程式,那Modbus將是必要學習的一門課。這一篇文章介紹如何使用modbus-tcl套件來和Modbus設備通訊,內容會介紹自製的modbus-tcl套件及其的使用方法,也會介紹一個支援Modbus的PLC模擬器,至於Modbus協定的細節,維基百科可以找到完整資訊,所以這邊就不會深入的介紹。
E06.1 Free Modbus PLC Simulator
為了方便我們接下來測試modbus-tcl,這邊需要請大家先安裝一個支援Modbus的PLC模擬器,請先到Free Modbus PLC Simulator Home找到你需要的版本,然後下載它,像下面的圖一樣:圖 E06-1
下載後解壓縮就可以用了。注意哦!!最新版的軟體是需要註冊的,而較舊的版本則有提供註冊檔,請看到圖中紅包框的下方,有註冊檔可以下載。如果沒有註冊的話,程式還是可以正常的執行45分鐘,只是過了45分鐘程式後,程式就會一直提醒你要註冊,但只要重開程式它又會安靜的執行45分鐘。個人覺得我們只用來測試所以45分鐘很夠用了,因此我都下載最新的版本,然後等它跳出提醒再重開它就好。
□ 執行Modbus PLC模擬器
現在請執行模擬器,這個模擬器的使用介面蠻簡單的,只有幾個設定值需要注意。首先「紅色1」指示的位置可以讓你切換PLC內部的記憶體頁面,你可以依需求去設定PLC的接點、線圈或是暫存器的值,例如你要設定PLC的輸出線圈,就可以切到「Coil Outputs」,然後直接在「紅色2」指示的表格中編輯每一個線圈的設定值。「紅色3」可以用來指定這台模擬器聽取通訊埠,因為我沒有真的要透過RS-485來讀取它,所以這邊我選擇Modbus TCP,最後請勾取「紅色4」的方塊,然後用「紅色5」指示的按鈕來切換聽取Modbus TCP或是Commport。圖 E06-2
如果都沒問題的話,當你按上圖「紅色5」指示的按鈕之後模擬器就會顯示目前正在聽取TCP。
圖 E06-3
E06.2 modbus-tcl介紹
modbus-tcl是一個用Tcl實作Modbus協定的套件,目前它支援Modbus RTU及Modbus TCP等兩種方式的通訊,且支援如下的功能:- 01 Read Coil Status (讀線圈)
- 02 Read Input Status (讀輸入接點)
- 03 Read Holding Registers (讀保持型暫存器)
- 04 Read Input Registers (讀輸入暫存器)
- 05 Force Single Coil (設定單一線圈)
- 06 Preset Single Register (設定單一保持型暫存器)
- 15 Force Multiple Coils (設定多個線圈)
- 16 Preset Multiple Registers (設定多個保持型暫存器)
大家可以在它的官方網站,也就是...本站下載它~
下載modbus-tcl套件
□ 安裝modbus-tcl套件
下載完成後請把它解壓縮,然後放在你的Tcl library資料夾裡面。注意哦!! modbus-tcl需要tcllib裡crc16套件,所以你可能需要先用teacup安裝它,像下面:teacup install crc16
接下來我們可以開始使用它了。
E06.2 設定及讀取線圈
現在讓我們先測試讀取線圈的值,讀取線圈的命令如下:::modbus::cmd 0x01 id start len
上面的命令中,第一個參數永遠是01,因為在Modbus的標準裡讀線圈的Function code就是01,接下來的id可以是數值0~255的值,它用來表示要通訊的設備站號,start是要讀取的起始線圈編號,然後len是要讀取的線圈數量。
下面是一個程式的實例,它透過Modbus TCP去讀取設備站號1,線圈0~5的狀態。
package require modbus
::modbus::configure -mode "TCP" -ip "127.0.0.1" -port 502
puts [::modbus::cmd 0x01 0x01 0x00 0x06]
程式的第1行引入了剛剛下載的modbus-tcl套件,然後程式的第2行設定使用TCP模式(-mode)通訊,然後ip是127.0.0.1(-ip),port是Modbus TCP標準的502(-port)。成功的話程式會輸出6個0或1組成的清單,失敗的話會輸出空字串。這個例子會有類似下方的輸出:
0 1 0 0 1 0
注意哦!!上面的輸出會根據你在PLC模擬器上的設定來決定,所以你可以試試在模擬器上修改編號0~5的線圈,然後再執行上面的程式,看看輸出的結果。
另外,如果你真的有Modbus RTU的設備接在Commport上,那你可以把-mode設定為RTU,然後用-com指定通訊埠,用-settings設定通訊參數,這樣程式就會真的讀出設備線圈的狀態,例如在Windows下可以這樣設定:
::modbus::configure -mode "RTU" -com "com1:" -settings "9600,n,8,1"
或是在Linux下這樣設定:
::modbus::configure -mode "RTU" -com "/dev/ttyUSB0" -settings "9600,n,8,1"
□ 寫入單一圈線狀態
寫單一線圈的語法如下:::modbus::cmd 0x05 id addr value
第一個參數永遠是05,因為在Modbus的標準裡寫單一線圈的Function code就是05,然後接下來的id可以是數值0~255的值,它用來表示要通訊的設備站號,接下來的addr是要寫入的線圈編號,然後value是0或是1用來表示要寫入的狀態。寫入成功的話回傳1,否則回傳0。
下面是一個程式的實例,它會把站號1,線圈5的狀態設定為1。
package require modbus
::modbus::configure -mode "TCP" -ip "127.0.0.1" -port 502
puts [::modbus::cmd 0x05 0x01 0x05 0x01]
□ 讀取輸入接點
讀取輸入接點的方法和讀取線圈是一樣的,唯一的不同點只有Function code,如果要讀取輸入接點的狀態,只要用改用02可以了,語法如下:::modbus::cmd 0x02 id start len
E06.2 設定及讀取保持型暫存器
讀保持型暫存器的語法如下:::modbus::cmd 0x03 id start len
就如同讀取線圈的命令,03是Modbus規定用來讀保持型暫存器的Function code,然後依序是站號(id),開始讀取的位置(start)及讀取長度(len)。命令執行成功的話會以清單的方式回傳每一個暫存器的值,失敗的話會回傳空字串。如下是一個使用的範例:
::modbus::configure -mode "TCP" -ip "127.0.0.1" -port 502
puts [::modbus::cmd 0x03 0x01 0x00 0x03]
因為我在模擬器上的暫存器0和2分別設定了1234和5678,所以我讀回來的值如下:
1234 0 5678
□ 寫入單一保持型暫存器
寫入單一保持型暫存器的語法如下:::modbus::cmd 0x06 id addr value
就如同寫入線圈的命令,06是Modbus規定用來寫入保持型暫存器的Function code,然後依序是站號(id),寫入位置(addr)及寫入值(value)。寫入成功的話回傳1,否則回傳0。如下是使用的範例:
::modbus::configure -mode "TCP" -ip "127.0.0.1" -port 502
puts [::modbus::cmd 0x06 0x01 0x00 0x05]
這個例子會把站號1的設備,編號0的保持型暫存器填為5。
按右上方的「#」號切換側邊欄
16 個意見
米粒 | 2010年6月3日 晚上10:00
我沒有library資料夾,只有lib所以我放在lib裡
執行就出現找不到modbus
::modbus::cmd 0x01 id start len
invalid command name "::modbus::cmd"
哪裡有library資料夾呢?
謝謝~
dai | 2010年6月4日 凌晨12:03
嗯 ~ 最簡單的方法是放在 C:/Tcl/lib 的資料夾裡面
或者是你也可以執行下面的程式
puts $::auto_path
::auto_path變數的功能有點像windows的PATH環境變數
它記錄了套件的搜尋路徑
當你執行package require xxx的時候
Tcl的直譯器就會去$::auto_path存放的路徑裡面找xxx套件
Unknown | 2011年12月30日 下午5:45
不好必思請想問您一下在modbus套件中,在使用各個funCode取得資料後會對rspCmd做binary scan的轉換如這樣binary scan [string range $data datarange datarange] S byte
再存進byte變數內,請問這個rspCmd的格式原本是什麼?再做轉換後S應該是變十進位沒錯吧?若我想做其它格式的輸出(如16進位)有什麼參數可用呢??感謝你
dai | 2011年12月30日 晚上8:58
一個簡單的方法是使用format命令
ex:
format %x $byte
Unknown | 2012年1月2日 上午10:44
感謝您~不過我還有個問題想請教,就是為何在要送reqCmd給機器時需要做binary format轉換呢?如果不先做轉換,要組的指令格式會長什麼樣子呢?
dai | 2012年1月2日 下午2:40
主要是因為送出去的字元有些是鍵盤打不出來的,所以要先做轉換,不然格式就不對了!!
匿名 | 2012年2月8日 下午2:04
記得 http://tibbo.en.alibaba.com/ 這裡面好像有些 ModBus 或是 serial port to Ethernet 的產品歐
匿名 | 2013年5月8日 下午2:00
不好意思,想請教個問題
我想把::modbus::cmd後的參數用entry的方式輸入
卻遇到如下的錯誤
missing operator at _@_
in expression "0x05 _@_0x01 0x01 0x01"
請問有何方法解決??感謝~~
dai | 2013年5月8日 下午6:46
看起來像是程式寫錯了,要看到程式才知道問題~
匿名 | 2013年5月8日 晚上11:38
package require modbus
::modbus::configure -mode "RTU" -com "com1:" -settings "9600,n,8,1"
set str "0x05 0x01 0x01 0x01"
puts [::modbus::cmd $str]
測試的程式碼如上,麻煩了
dai | 2013年5月9日 上午11:18
嗯 ~ 改這樣:
puts [::modbus::cmd {*}$str]
匿名 | 2013年5月9日 中午12:33
可以了,感謝~~
不過這是因為開頭是0x的關係嗎??
dai | 2013年5月9日 下午4:00
請參考這篇文章 http://wiki.tcl.tk/17158
匿名 | 2013年7月15日 下午2:17
哈囉~~請教個問題
package require modbus
::modbus::configure -mode "TCP" -ip "127.0.0.1" -port 502
puts [::modbus::cmd 3 1 1 0x1]
我用上面的程式碼在windows的指令模式下做測試,
連續執行約9次後,就沒有數值顯示出來,
大約要等個四、五分鐘過後再執行才會讀的到值,
這大概是哪方面的問題呢??
dai | 2013年7月15日 下午3:46
這個程式是我初期開發測試用的目前沒在使用它了,但記得早先在使用上倒是不會出現這樣的狀況 ~
也許是TCP 連線的過程出現問題吧!! 不是很確定。
水鏡窯 | 2016年3月16日 晚上11:30
大大你好,請問一下,我用teacup install crc16回應以下東西,是哪裡有問題?
Unable to determine a default architecture for the repository at
"C:/Tcl/lib/teapot". This is because your repository has no
shells at all connected to it. This situation can however be
easily rectified by running the command
teacup.EXE link make "C:/Tcl/lib/teapot" /path/to/your/tclsh
留下您的意見