例:Host Link與FINS從接線到穩(wěn)定讀寫)
簡(jiǎn)介這份資源是面向工業(yè)自動(dòng)化工程師、PLC初學(xué)者及上位機(jī)開發(fā)人員的OMRON串口通訊實(shí)戰(zhàn)示例圍繞RS-232、RS-485等串行標(biāo)準(zhǔn)演示如何與OMRON PLC建立穩(wěn)定數(shù)據(jù)交互解決協(xié)議選擇、參數(shù)配置與指令收發(fā)等常見問題。壓縮包共38個(gè)文件約1.64MB以C#源碼14個(gè)cs和窗體資源4個(gè)resx為主體輔以可執(zhí)行程序、調(diào)試符號(hào)、解決方案與說明文本構(gòu)成一套可直接運(yùn)行的通訊測(cè)試工程。資源中提供了完整的通訊程序框架涵蓋串口參數(shù)設(shè)置、請(qǐng)求發(fā)送、響應(yīng)解析與錯(cuò)誤處理等關(guān)鍵環(huán)節(jié)并配有通訊實(shí)例說明文檔便于讀者對(duì)照代碼理解ASCII、Modbus RTU等協(xié)議的實(shí)際落地方式。目前已有3657人學(xué)習(xí)下載適合希望快速搭建OMRON PLC串口通訊原型、排查通訊異?;蜓a(bǔ)充上位機(jī)開發(fā)經(jīng)驗(yàn)的讀者參考借鑒。1. OMRON串口通訊實(shí)例從一根線到穩(wěn)定讀寫中間隔著多少坑車間里一臺(tái)老設(shè)備還在跑PLC 是 OMRON 的上位機(jī)要拿它的產(chǎn)量和報(bào)警數(shù)據(jù)。你翻出通訊手冊(cè)看到 Host Link、FINS、無協(xié)議幾種模式心里大概有數(shù)但真把線接上、命令發(fā)出去回過來的卻是一串亂碼或者干脆沒響應(yīng)。OMRON 串口通訊實(shí)例這件事難點(diǎn)從來不在協(xié)議本身而在于接線、參數(shù)、時(shí)序、校驗(yàn)這四件事里任何一件對(duì)不上整條鏈路就是黑匣子。這篇筆記面向兩類人一類是第一次用 OMRON PLC 做串口采集、想照著跑通最小閉環(huán)的工程師另一類是已經(jīng)能通、但偶爾丟包或超時(shí)、想搞清楚邊界在哪的熟手。我會(huì)按「先立住原理、再動(dòng)手復(fù)現(xiàn)、最后講坑」的順序把 Host Link 和 FINS 串口這兩條最常見路徑拆開講參數(shù)怎么設(shè)、命令怎么拼、失敗時(shí)看什么都落到可抄作業(yè)的粒度。2. 先把協(xié)議選對(duì)Host Link、FINS 串口、無協(xié)議到底用哪個(gè)選錯(cuò)協(xié)議是新手翻車的第一大來源。OMRON 的串口通訊不是只有一種玩法上位機(jī)主動(dòng)發(fā)命令、PLC 被動(dòng)應(yīng)答這套主從結(jié)構(gòu)下有三條主流路徑適用場(chǎng)景差別很大。先把它們擺清楚再?zèng)Q定你的實(shí)例該怎么寫。2.1 三種模式的本質(zhì)區(qū)別與選型依據(jù)Host Link 是 OMRON 最早、最通用的一套上位機(jī)命令協(xié)議走 ASCII 文本幀命令形如00RD00000001這種可讀字符串PLC 回00RD00...加數(shù)據(jù)和 FCS 校驗(yàn)。它的好處是調(diào)試直觀串口助手直接敲命令就能看到回應(yīng)壞處是 ASCII 傳輸效率低單次讀寫點(diǎn)數(shù)有限適合小批量、低頻采集。FINS 串口是把 FINS 命令幀套在串口上跑幀是二進(jìn)制或 ASCII 可選命令結(jié)構(gòu)比 Host Link 復(fù)雜但尋址能力強(qiáng)能跨網(wǎng)絡(luò)、能訪問 DM/CIO/EM 等各區(qū)域適合數(shù)據(jù)量大、需要結(jié)構(gòu)化訪問的場(chǎng)合。很多中高端機(jī)型同時(shí)支持 Host Link 和 FINS低端機(jī)型可能只給 Host Link。無協(xié)議模式則是 PLC 側(cè)用指令自己收發(fā)原始字節(jié)協(xié)議由你自定義靈活但上位機(jī)要自己處理全部幀格式和校驗(yàn)工作量最大一般只在對(duì)接第三方設(shè)備、協(xié)議已經(jīng)定死時(shí)才用。模式幀格式調(diào)試難度單次數(shù)據(jù)量典型場(chǎng)景Host LinkASCII 文本低小產(chǎn)量計(jì)數(shù)、狀態(tài)讀取FINS 串口二進(jìn)制/ASCII中大多區(qū)域批量采集無協(xié)議自定義高任意對(duì)接既定第三方協(xié)議選型建議很直接能上 Host Link 就先上 Host Link把鏈路跑通、把參數(shù)摸清再考慮換 FINS 提效率。不要一上來就啃 FINS 幀結(jié)構(gòu)那樣連串口通沒通都驗(yàn)證不了。2.2 物理層與串口參數(shù)9600 8N1 不是隨便寫的OMRON PLC 的串口默認(rèn)參數(shù)在多數(shù)機(jī)型上是 9600bps、8 數(shù)據(jù)位、無校驗(yàn)、1 停止位也就是常說的 8N1。這個(gè)默認(rèn)值寫在 PLC 的 DM 區(qū)設(shè)置里不同機(jī)型對(duì)應(yīng)的 DM 地址不一樣常見的是 DM6645 到 DM6648 這一段控制通訊口參數(shù)。如果你改了 PLC 側(cè)參數(shù)上位機(jī)必須同步改否則收到的就是一堆錯(cuò)位字節(jié)。接線是另一個(gè)高頻翻車點(diǎn)。RS-232 和 RS-485 的接法完全不同232 是點(diǎn)對(duì)點(diǎn)TX/RX 要交叉GND 必須共地485 是差分總線A 接 A、B 接 B終端電阻在長(zhǎng)距離時(shí)不能省。很多人用 USB 轉(zhuǎn) 232 的線結(jié)果 PLC 那頭是 485 口物理層就對(duì)不上軟件怎么調(diào)都沒用。提示先用串口助手以 8N1、9600 手動(dòng)發(fā)一條 Host Link 讀命令能收到正確回應(yīng)再寫代碼。這一步能排掉八成「代碼沒問題但就是不通」的情況。2.3 最小可復(fù)現(xiàn)的 Host Link 讀命令Host Link 命令幀結(jié)構(gòu)是起始符 單元號(hào)2 位 命令碼2 位 數(shù)據(jù)區(qū) FCS 校驗(yàn)2 位 結(jié)束符* CR。以讀 DM 區(qū)為例讀 DM0000 開始的 1 個(gè)字# Host Link 讀 DM 區(qū)最小示例 # 幀: 單元號(hào) RD 起始地址(4位) 點(diǎn)數(shù)(4位) FCS * CR unit 00 # PLC 單元號(hào)默認(rèn) 00 cmd RD # 讀 DM 區(qū) addr 0000 # 起始地址 DM0000 count 0001 # 讀 1 個(gè)字 body unit cmd addr count # FCS 計(jì)算對(duì) body 所有字節(jié)做異或 fcs 0 for ch in body: fcs ^ ord(ch) frame body format(fcs, 02X) *\r print(repr(frame)) # 輸出: 00RD00000001 FCS *\r邏輯說明body是不含起始符、FCS、結(jié)束符的純命令體FCS 是對(duì)它逐字節(jié)異或得到的兩位十六進(jìn)制。參數(shù)上unit要和 PLC 的單元號(hào)設(shè)置一致多臺(tái) PLC 掛在同一 485 總線上時(shí)靠它區(qū)分addr是 4 位十六進(jìn)制地址count是 4 位十六進(jìn)制點(diǎn)數(shù)讀多個(gè)字時(shí)改count即可。發(fā)出去后 PLC 回幀格式類似數(shù)據(jù)區(qū)就是你要的值回幀的 FCS 也要校驗(yàn)一遍不通過說明傳輸有誤。3. 用 Python 把 Host Link 讀寫跑通串口配置與幀收發(fā)原理清楚之后落到代碼。這一章給一個(gè)能直接跑的 Python 實(shí)例覆蓋串口打開、命令拼裝、發(fā)送、接收、FCS 校驗(yàn)、超時(shí)處理。用 pyserial 這個(gè)庫(kù)跨平臺(tái)Windows 和 Linux 都能用。3.1 串口打開與參數(shù)配置import serial import time # 打開串口參數(shù)必須與 PLC 側(cè)一致 ser serial.Serial( portCOM3, # Windows 用 COMxLinux 用 /dev/ttyUSB0 baudrate9600, # 與 PLC DM 區(qū)設(shè)置一致 bytesizeserial.EIGHTBITS, parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, timeout1.0 # 讀超時(shí) 1 秒避免死等 ) if not ser.is_open: ser.open()參數(shù)說明timeout是關(guān)鍵設(shè)成 None 會(huì)一直阻塞設(shè)太小又會(huì)把正常慢響應(yīng)誤判為超時(shí)。OMRON PLC 的響應(yīng)時(shí)間通常在幾十毫秒量級(jí)1 秒足夠?qū)捤?。baudrate如果 PLC 側(cè)改過這里必須同步改完記得重新打開串口熱改波特率在部分 USB 轉(zhuǎn)串口芯片上不生效。3.2 命令拼裝與 FCS 校驗(yàn)函數(shù)def build_hostlink(unit, cmd, addr, count): 拼裝 Host Link 命令幀 body f{unit}{cmd}{addr}{count} fcs 0 for ch in body: fcs ^ ord(ch) return f{body}{fcs:02X}*\r def check_fcs(resp): 校驗(yàn) PLC 回幀的 FCS if not resp.startswith() or * not in resp: return False body resp[1:resp.index(*)] recv_fcs body[-2:] calc 0 for ch in body[:-2]: calc ^ ord(ch) return format(calc, 02X) recv_fcs.upper()邏輯說明build_hostlink把單元號(hào)、命令碼、地址、點(diǎn)數(shù)拼成命令體算 FCS 后加上起始符和結(jié)束符。check_fcs從回幀里剝出命令體取最后兩位作為收到的 FCS再對(duì)前面部分重算比對(duì)。這兩個(gè)函數(shù)是整個(gè)通訊的核心寫錯(cuò)一個(gè)字節(jié)PLC 要么不回要么回錯(cuò)誤碼。3.3 發(fā)送、接收與超時(shí)處理def read_dm(ser, addr, count): frame build_hostlink(00, RD, addr, count) ser.reset_input_buffer() # 清掉殘留數(shù)據(jù)避免串幀 ser.write(frame.encode(ascii)) time.sleep(0.05) # 給 PLC 一點(diǎn)響應(yīng)時(shí)間 resp ser.read(64).decode(ascii, errorsignore) if not resp: raise TimeoutError(PLC 無響應(yīng)檢查接線與參數(shù)) if not check_fcs(resp): raise ValueError(fFCS 校驗(yàn)失敗: {resp!r}) # 回幀格式: 00RD00 數(shù)據(jù) FCS * data resp[7:-4] return data # 使用 try: val read_dm(ser, 0000, 0001) print(DM0000 , val) except Exception as e: print(讀取失敗:, e)邏輯說明reset_input_buffer在每次發(fā)送前清空接收緩沖這是防止上一幀殘留導(dǎo)致串幀的關(guān)鍵一步很多人丟包就是因?yàn)槭×诉@行。time.sleep(0.05)是給 PLC 的處理窗口太短會(huì)讀到空太長(zhǎng)影響吞吐?;貛臄?shù)據(jù)區(qū)位置取決于命令碼RD 命令下數(shù)據(jù)從第 7 個(gè)字符開始到 FCS 前結(jié)束實(shí)際項(xiàng)目里建議按命令碼分別解析不要寫死偏移。注意如果連續(xù)讀多個(gè)地址不要每條命令都 sleep 固定值改成讀回幀長(zhǎng)度判斷更穩(wěn)。固定 sleep 在波特率變化或 PLC 負(fù)載高時(shí)會(huì)失效。3.4 寫命令與讀命令的差異寫 DM 區(qū)用WD命令幀結(jié)構(gòu)多一段寫入數(shù)據(jù)def write_dm(ser, addr, value_hex): 寫單個(gè) DM 字value_hex 為 4 位十六進(jìn)制 body f00WD{addr}{value_hex} fcs 0 for ch in body: fcs ^ ord(ch) frame f{body}{fcs:02X}*\r ser.reset_input_buffer() ser.write(frame.encode(ascii)) time.sleep(0.05) resp ser.read(32).decode(ascii, errorsignore) # 寫成功回幀以 00WD00 開頭 return resp.startswith(00WD00)寫命令的回幀只表示「收到并執(zhí)行」不返回寫入值所以判斷成功看回幀的命令碼和結(jié)束碼是否為00。寫操作比讀操作更容易受時(shí)序影響連續(xù)寫多個(gè)地址時(shí)建議每條之間留 20 到 50 毫秒太快 PLC 可能丟命令。4. FINS 串口實(shí)例什么時(shí)候該從 Host Link 換過去Host Link 跑通之后如果發(fā)現(xiàn)單次讀點(diǎn)數(shù)不夠、或者要訪問多個(gè)存儲(chǔ)區(qū)、或者要跨網(wǎng)段就該考慮 FINS。FINS 串口不是另起爐灶而是把 FINS 幀套在串口鏈路上理解它的結(jié)構(gòu)能讓你在數(shù)據(jù)量上來之后不慌。4.1 FINS 幀結(jié)構(gòu)與 Host Link 的本質(zhì)差異FINS 幀由 ICF、RSV、GCT、DNA、DA1、DA2、SNA、SA1、SA2、SID 這十個(gè)字節(jié)的頭部加上命令碼和參數(shù)區(qū)組成。頭部里 DA1/DA2 是目標(biāo)節(jié)點(diǎn)地址SA1/SA2 是源節(jié)點(diǎn)地址串口直連時(shí)通常都填 00。命令碼是兩字節(jié)比如讀 CIO 區(qū)是0101讀 DM 區(qū)是0101加不同區(qū)域碼。和 Host Link 最大的差異在于Host Link 是 ASCII 文本、命令即語義FINS 是二進(jìn)制為主、頭部字段多、需要理解節(jié)點(diǎn)尋址。好處是 FINS 一次能讀的字節(jié)數(shù)遠(yuǎn)大于 Host Link且能訪問所有存儲(chǔ)區(qū)壞處是調(diào)試時(shí)不能靠串口助手直接敲得先算好二進(jìn)制幀。4.2 用 Python 構(gòu)造 FINS 串口讀幀import struct def build_fins_read_dm(start_addr, count): 構(gòu)造 FINS 讀 DM 區(qū)命令幀串口直連節(jié)點(diǎn)地址全 0 # FINS 頭部 10 字節(jié) icf, rsv, gct 0x80, 0x00, 0x02 dna, da1, da2 0x00, 0x00, 0x00 sna, sa1, sa2 0x00, 0x00, 0x00 sid 0x00 header struct.pack(BBBBBBBBBB, icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid) # 命令碼 0101 讀區(qū)域碼 82 DM 區(qū) command struct.pack(HH, 0x0101, 0x0082) # 起始地址 2 字節(jié) 點(diǎn)數(shù) 2 字節(jié) params struct.pack(HH, start_addr, count) return header command params邏輯說明icf0x80表示需要響應(yīng)gct0x02是網(wǎng)關(guān)計(jì)數(shù)串口直連時(shí)固定值。command里0x0101是內(nèi)存區(qū)讀命令0x0082是 DM 區(qū)區(qū)域碼讀 CIO 區(qū)要換成0x00B0之類。params是起始地址和字?jǐn)?shù)都是大端兩字節(jié)。整個(gè)幀發(fā)出去后PLC 回的幀頭部相同命令碼后跟結(jié)束碼和讀到的數(shù)據(jù)。4.3 FINS 回幀解析與結(jié)束碼判斷def parse_fins_response(resp): 解析 FINS 回幀返回 (結(jié)束碼, 數(shù)據(jù)) if len(resp) 14: raise ValueError(回幀過短) # 頭部 10 字節(jié) 命令碼 2 字節(jié) 結(jié)束碼 2 字節(jié) end_code struct.unpack(H, resp[12:14])[0] data resp[14:] return end_code, data # 結(jié)束碼 0x0000 表示正常非零要查手冊(cè)結(jié)束碼是 FINS 排錯(cuò)的核心0x0000正常0x0101之類是命令不支持0x0201是區(qū)域碼錯(cuò)誤0x0301是地址越界。拿到非零結(jié)束碼不要瞎猜對(duì)照 FINS 命令手冊(cè)的結(jié)束碼表逐條查比反復(fù)改代碼快得多。4.4 串口 FINS 與 Host Link 的混用策略實(shí)際項(xiàng)目里不必二選一。低頻狀態(tài)量用 Host Link直觀好維護(hù)高頻批量數(shù)據(jù)用 FINS效率高。兩條路徑可以共用同一個(gè)物理串口只要命令幀不交叉、每次收發(fā)完整PLC 不會(huì)混淆。但要注意混用時(shí)單元號(hào)和節(jié)點(diǎn)地址要理清Host Link 的單元號(hào)和 FINS 的 DA1 不是一回事別把兩套尋址搞混。5. 避坑與排查OMRON 串口通訊最常見的五類翻車這一章是血淚經(jīng)驗(yàn)集中區(qū)。下面五條都是實(shí)際調(diào)試中反復(fù)出現(xiàn)的每條按現(xiàn)象、原因、解決寫照著排查能省大量時(shí)間。5.1 現(xiàn)象發(fā)命令完全無響應(yīng)原因物理層沒通或者串口參數(shù)與 PLC 不一致。最常見的是 USB 轉(zhuǎn)串口線的電平不對(duì)或者 TX/RX 沒交叉或者 PLC 側(cè)通訊口被設(shè)成了無協(xié)議模式而不是 Host Link。解決先用萬用表確認(rèn)線序再用串口助手以 8N1、9600 發(fā)一條最簡(jiǎn)單的 Host Link 讀命令。如果串口助手能通、代碼不通問題在代碼如果串口助手也不通問題在物理層或 PLC 參數(shù)。PLC 側(cè)要確認(rèn) DM 區(qū)里通訊模式設(shè)的是 Host Link 而不是無協(xié)議。5.2 現(xiàn)象能收到回應(yīng)但 FCS 校驗(yàn)失敗原因回幀里混入了上一幀的殘留數(shù)據(jù)或者讀取長(zhǎng)度不對(duì)導(dǎo)致截?cái)?。串口是流式的沒有幀邊界讀的時(shí)候如果沒清緩沖很容易把兩幀粘在一起。解決每次發(fā)送前reset_input_buffer接收時(shí)按回幀的結(jié)束符*和 CR 判斷邊界不要用固定長(zhǎng)度讀。如果波特率較高考慮用read_until(b*\r)代替read(n)。5.3 現(xiàn)象偶爾丟包重發(fā)就好原因發(fā)送間隔太短PLC 還沒處理完上一條命令就來了下一條?;蛘呱衔粰C(jī) sleep 時(shí)間固定但 PLC 負(fù)載波動(dòng)時(shí)響應(yīng)變慢。解決把固定 sleep 改成基于回幀的判斷收到完整回幀再發(fā)下一條。連續(xù)寫操作時(shí)每條之間留足間隔必要時(shí)加重試機(jī)制重試前先清緩沖。5.4 現(xiàn)象讀到的數(shù)據(jù)錯(cuò)位或值不對(duì)原因地址算錯(cuò)或者區(qū)域碼用錯(cuò)或者數(shù)據(jù)解析偏移寫死。Host Link 的地址是十六進(jìn)制但有些手冊(cè)里給的是十進(jìn)制換算錯(cuò)一位整個(gè)數(shù)據(jù)就偏了。解決先用已知值驗(yàn)證比如往 DM0000 寫一個(gè)固定值再讀回來確認(rèn)地址和解析都對(duì)。FINS 的區(qū)域碼要對(duì)照手冊(cè)DM 區(qū)是0x82CIO 區(qū)是0xB0別混。5.5 現(xiàn)象多臺(tái) PLC 掛同一總線時(shí)互相干擾原因單元號(hào)或節(jié)點(diǎn)地址重復(fù)或者 485 總線終端電阻沒接、線太長(zhǎng)導(dǎo)致信號(hào)反射。解決每臺(tái) PLC 的單元號(hào)必須唯一Host Link 靠單元號(hào)區(qū)分FINS 靠 DA1 區(qū)分。485 總線超過一定長(zhǎng)度要在兩端接終端電阻線材用雙絞屏蔽線屏蔽層單端接地。6. 進(jìn)階技巧把串口通訊做成可維護(hù)的采集模塊跑通單條命令只是起點(diǎn)真正上線要的是穩(wěn)定、可觀測(cè)、可恢復(fù)。這一章講幾個(gè)我實(shí)際項(xiàng)目里會(huì)用的技巧幫你把實(shí)例升級(jí)成模塊。第一個(gè)技巧是命令隊(duì)列加狀態(tài)機(jī)。不要在主循環(huán)里直接發(fā)命令而是把讀任務(wù)放進(jìn)隊(duì)列一個(gè) worker 串行處理每條命令帶超時(shí)和重試次數(shù)。這樣即使某條命令失敗也不會(huì)阻塞整個(gè)采集。狀態(tài)機(jī)記錄當(dāng)前處于「發(fā)送中」「等待響應(yīng)」「解析中」哪個(gè)階段出問題時(shí)能直接定位。第二個(gè)技巧是心跳與斷線重連。串口線松動(dòng)、USB 轉(zhuǎn)接器掉驅(qū)動(dòng)都會(huì)導(dǎo)致鏈路斷開代碼里要定期發(fā)一條輕量讀命令當(dāng)心跳連續(xù)失敗就關(guān)閉串口重新打開。重連邏輯要帶退避不要瘋狂重試把 CPU 打滿。第三個(gè)技巧是原始幀日志。把每次發(fā)送和接收的原始字節(jié)按十六進(jìn)制記下來出問題時(shí)翻日志比現(xiàn)場(chǎng)復(fù)現(xiàn)快得多。日志要帶時(shí)間戳和方向方便對(duì)齊 PLC 側(cè)的事件。技巧解決的問題實(shí)現(xiàn)要點(diǎn)命令隊(duì)列狀態(tài)機(jī)單條失敗阻塞全局串行 worker超時(shí)重試心跳重連鏈路意外斷開定期輕量讀退避重連原始幀日志現(xiàn)場(chǎng)難復(fù)現(xiàn)十六進(jìn)制時(shí)間戳方向最后一個(gè)習(xí)慣任何參數(shù)改動(dòng)都先記下來再改。波特率、單元號(hào)、DM 設(shè)置地址這些改之前截圖或抄一遍改完不通能快速回退。我吃過一次虧把 PLC 通訊口參數(shù)改了沒記結(jié)果調(diào)了一下午才發(fā)現(xiàn)是參數(shù)問題有后悔藥的話就是那份記錄。串口通訊這東西玄學(xué)感往往來自沒記錄的變化把變量控制住它就是個(gè)確定性的工程問題。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取