#

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

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates