淄博網(wǎng)站建設(shè)服務(wù)商發(fā)帖子最好的幾個網(wǎng)站
一、Modbus TCP通信概述?
MODBUS/TCP是簡單的、中立廠商的用于管理和控制自動化設(shè)備的MODBUS系列通訊協(xié)議的派生產(chǎn)品,顯而易見,它覆蓋了使用TCP/IP協(xié)議的“Intranet”和“Internet”環(huán)境中MODBUS報文的用途。協(xié)議的最通用用途是為諸如PLC,I/O模塊,以及連接其它簡單域總線或I/O模塊的網(wǎng)關(guān)服務(wù)的。
Modbus TCP協(xié)議是在RTU協(xié)議前面添加MBAP報文頭,由于TCP是基于可靠連接的服務(wù),RTU協(xié)議中的CRC校驗碼就不再需要,所以在Modbus TCP協(xié)議中是沒有CRC校驗碼。(使用上的主要區(qū)別)。MBAP報文頭: 識( 2字節(jié) ) 長度( 2字節(jié) ) 單元標識符(1字節(jié) )
目前Modbus TCP/IP協(xié)議主要應(yīng)用領(lǐng)域Internet或Intranet中,而以太網(wǎng)傳輸距離遠、傳輸速度快,使得應(yīng)用范圍廣泛傳輸距離遠、傳輸速度快,使得應(yīng)用范圍廣泛。
?二. Modbus TCP使用的功能代碼
modbus的操作對象有四種:線圈、離散輸入、輸入寄存器、保持寄存器
線圈:PLC的輸出位,開關(guān)量,在MODBUS中可讀可寫
離散量:PLC的輸入位,開關(guān)量,在MODBUS中只讀
輸入寄存器:PLC中只能從模擬量輸入端改變的寄存器,在MODBUS中只讀
保持寄存器:PLC中用于輸出模擬量信號的寄存器,在MODBUS中可讀可寫
根據(jù)對象的不同,modbus的功能碼有:
0x01:讀線圈
0x02:讀離散量輸入
0x03:讀保持寄存器
0x04:讀輸入寄存器
0x05:寫單個線圈
0x06:寫單個保持寄存器
0x10:寫多個保持寄存器
0x0F:寫多個線圈
?三、nmodbus4指南
?NModbus4是一個基于C#的Modbus協(xié)議庫,可用于與Modbus RTU、ASCII、TCP和UDP設(shè)備進行通信。NModbus4中文版相當于對原版進行了翻譯,使得不懂英文的人能夠更方便地使用這個開源庫進行編程。NModbus4是用C#編寫的Modbus通信協(xié)議庫,它支持的Modbus協(xié)議包括Modbus RTU、ASCII、TCP和UDP,可用于編程讀寫Modbus設(shè)備的寄存器和線圈。它完全符合Modbus協(xié)議規(guī)范,同時通過使用的事件調(diào)用機制,能夠?qū)崿F(xiàn)斷線重連的功能。
NModbus4是一個完全開源的庫,可以在GitHub上免費下載和使用
四、Modbus TCP通訊應(yīng)用舉例?
?4.1:搭建西門子博途V15的環(huán)境
搭建西門子仿真環(huán)境,需要先前掌握這些,看本人這些博客
windows10企業(yè)版安裝西門子博途V15---01準備環(huán)境
windows10企業(yè)版安裝西門子博途V15---02安裝軟件
windows10企業(yè)版安裝西門子博途V15---03安裝仿真軟件
windows10企業(yè)版安裝西門子博途V15---04連接測試
?
4.2:熟悉modbusTCP環(huán)境
需要先前掌握這些,看本人這些博客
4.3:創(chuàng)建PLC仿真環(huán)境
4.4:?博途V15創(chuàng)建項目
?本文最后會提供這個項目,只要打開即可
?4.5:創(chuàng)建數(shù)據(jù)塊變量
4.6:創(chuàng)建tcp連接數(shù)據(jù)塊
4.7:創(chuàng)建modbustcp通信模塊
請注意,這里為什么是BYTE 20,是因為變量mf1到mf5共10寄存器,每個寄存器占2個字節(jié),所以是20個字節(jié),編譯完成后,下載到Plc中
4.8:創(chuàng)建監(jiān)控表
?
以上8個步驟就完成了modbustcp服務(wù)器,接下來搞程序,來讀寫Plc中的浮點數(shù)
4.9:創(chuàng)建winform項目
打開VS2019,創(chuàng)建窗體項目,布局很簡單,4個button按鈕
?
4.10:添加nmodbus4庫
?
4.11:編寫“nmodbus4讀取一個float”代碼
/// <summary>/// nmodbus4讀取一個float/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button4_Click(object sender, EventArgs e){//nmodbus4讀取到的數(shù)據(jù)都是ushort類型tcpClient = new TcpClient();tcpClient.Connect("192.168.1.199", 6800);//連接到主機master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站 byte slaveAddr = byte.Parse("1");//從站地址//ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);表示讀保持寄存器//slaveAddress從站地址(默認為1,通常也是1)//startAddress寄存器開始地址(這個地址是modbus的地址,不是Plc變量地址)//numberOfPoints寄存器數(shù)量(real類型占2個寄存器數(shù)量)ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("0"), ushort.Parse("2"));byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);//ushort數(shù)組轉(zhuǎn)byte數(shù)組float[] floats = FloatLib.GetFloatArrayFromByteArray(t);//byte數(shù)組轉(zhuǎn)float數(shù)組string fw1a = string.Join(",", floats);float fw1b = FloatLib.GetFloatFromByteArray(t, 0);//byte數(shù)組轉(zhuǎn)floatMessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("2"), ushort.Parse("2"));t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);floats = FloatLib.GetFloatArrayFromByteArray(t);fw1a = string.Join(",", floats);fw1b = FloatLib.GetFloatFromByteArray(t, 0);MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("4"), ushort.Parse("2"));t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);floats = FloatLib.GetFloatArrayFromByteArray(t);fw1a = string.Join(",", floats);fw1b = FloatLib.GetFloatFromByteArray(t, 0);MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("6"), ushort.Parse("2"));t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);floats = FloatLib.GetFloatArrayFromByteArray(t);fw1a = string.Join(",", floats);fw1b = FloatLib.GetFloatFromByteArray(t, 0);MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("8"), ushort.Parse("2"));t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);floats = FloatLib.GetFloatArrayFromByteArray(t);fw1a = string.Join(",", floats);fw1b = FloatLib.GetFloatFromByteArray(t, 0);MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());master.Dispose();tcpClient.Dispose();}
運行效果
?全部讀取到了PLC中的數(shù)據(jù)
?nmodbus4讀取到的數(shù)據(jù)都是ushort類型
?ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);表示讀保持寄存器
? ? ? ? ? ? slaveAddress從站地址(默認為1,通常也是1)
? ? ? ? ? ? startAddress寄存器開始地址(這個地址是modbus的地址,不是Plc變量地址)
? ? ? ? ? ? numberOfPoints寄存器數(shù)量(real類型占2個寄存器數(shù)量)
?ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("0"), ushort.Parse("2"));
這個意思是讀取從站地址1中的從0開始的2個寄存器數(shù)據(jù),即%DB4.DBD0中的數(shù)據(jù),結(jié)果是1.1
很多人搞不清楚,這個為何是開始地址0,數(shù)量是2,這就需要明白PLC中的地址與MODBUS地址的關(guān)系,另外nmodbus4讀取到的數(shù)據(jù)都是ushort類型,因此需要進行類型轉(zhuǎn)換,將ushort數(shù)組轉(zhuǎn)byte數(shù)組,再將byte數(shù)組轉(zhuǎn)float數(shù)組
4.12:編寫“nmodbus4讀取全部float”代碼
/// <summary>/// nmodbus4讀取全部/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button5_Click(object sender, EventArgs e){tcpClient = new TcpClient();tcpClient.Connect("192.168.1.188", 6800);//連接到主機master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站 byte slaveAddr = byte.Parse("1");ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("5"), ushort.Parse("10"));byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);float[] floats = FloatLib.GetFloatArrayFromByteArray(t);string fw1a = string.Join(",", floats);MessageBox.Show(fw1a.ToString());master.Dispose();tcpClient.Dispose();}
運行效果
4.13:編寫“nmodbus4寫入單個浮點”代碼
/// <summary>/// nmodbus4寫入單個浮點/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button7_Click(object sender, EventArgs e){tcpClient = new TcpClient();tcpClient.Connect("192.168.1.199", 6800);//連接到主機master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站 //從站地址byte slaveAddr = byte.Parse("1");開始地址ushort startAddr = ushort.Parse("0");數(shù)據(jù)的值string vals = ("12.625");float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);//void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data);//寫入多保持寄存器,意思是指向多個寄存器地址寫入數(shù)據(jù),也就是指同時向多個寄存器寫入數(shù)據(jù)//slaveAddress表示從站地址,通常為1,默認也為1//startAddress表示寄存器開始地址,必須是ushort類型//data表示寫入的具體數(shù)值,必須是ushort數(shù)組master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);//向第一個寄存器(地址是0)寫入數(shù)據(jù)12.625MessageBox.Show("【 通過多保持寄存器】寫入正數(shù)成功!");float floatValue = 12.625f;startAddr = ushort.Parse("2");byte[] byteArray = ByteArrayLib.GetByteArrayFromFloat(floatValue);//將字節(jié)數(shù)組中第0個開始的2個字節(jié)轉(zhuǎn)換成ushort類型,即0,1ushort ua = UShortLib.GetUShortFromByteArray(byteArray, 0, DataFormat.ABCD);master.WriteSingleRegister(slaveAddr, startAddr, ua);//向從站地址1中的第3個寄存器(地址為2)寫入數(shù)據(jù)ua//將字節(jié)數(shù)組中第2個開始的2個字節(jié)轉(zhuǎn)換成ushort類型,即2,3 ushort ub = UShortLib.GetUShortFromByteArray(byteArray, 2, DataFormat.ABCD);startAddr = ushort.Parse("3");master.WriteSingleRegister(slaveAddr, startAddr, ub);//向從站地址1中的第4個寄存器(地址為3)寫入數(shù)據(jù)ubMessageBox.Show("【 通過單保持寄存器】寫入正數(shù)成功!"); vals = ("-18.326");startAddr = ushort.Parse("8");uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);ushorts = UShortLib.GetUShortArrayFromByteArray(y);master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);MessageBox.Show("【 通過多保持寄存器】寫入負數(shù)成功!");master.Dispose();tcpClient.Dispose();}
?運行效果
?4.14:編寫“nmodbus4寫入多個浮點"代碼
/// <summary>/// nmodbus4寫入多個浮點/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button6_Click(object sender, EventArgs e){tcpClient = new TcpClient();tcpClient.Connect("192.168.1.199", 6800);//連接到主機master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站 //從站地址byte slaveAddr = byte.Parse("1");//開始地址ushort startAddr = ushort.Parse("0");//數(shù)據(jù)的值string vals = ("4.9635,6.9635,-1.28,67,-902");float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);MessageBox.Show("【 多保持寄存器】寫入成功!");master.Dispose();tcpClient.Dispose();}
?運行效果
浮點數(shù)包括整數(shù),小數(shù),也包括正數(shù)或負數(shù),所以正整數(shù),負整數(shù),正小數(shù),負小數(shù)都可以寫入
五:modbustcp協(xié)議小結(jié)
MODBUS TCP 結(jié)合了以太網(wǎng)物理網(wǎng)絡(luò)和網(wǎng)絡(luò)標準 TCP/IP 以及以 MODBUS 作為應(yīng)用協(xié)議標準的數(shù)據(jù)表示方法。MODBUS TCP 通信報文被封裝于以太網(wǎng) TCP/IP 數(shù)據(jù)包中,MODBUS 協(xié)議規(guī)范一幀數(shù)據(jù)的最大長度為 256 個字節(jié)。
MODBUS TCP/IP 的通信系統(tǒng)中有兩種類型的設(shè)備:MODBUS TCP/IP 客戶端和服務(wù)器設(shè)備。
1.MODBUS 客戶端
客戶端(TCP Client)主動向服務(wù)器(TCP Server)發(fā)起連接請求,連接建立成功,僅允許客戶端主動發(fā)起通訊請求。
以太網(wǎng)機型作為 MODBUS TCP 客戶端時,通過 S_OPEN 指令建立 TCP 連接,通過 M_TCP 指令發(fā)起 MODBUS 請求。
2.MODBUS 服務(wù)器
服務(wù)器主動監(jiān)聽 502 端口,等待客戶端連接請求,連接建立成功,響應(yīng)符合 Modbus TCP 協(xié)議規(guī)范的數(shù)據(jù)通訊請求。
3.優(yōu)勢?
優(yōu)勢: 免費、簡單、容易使用,Modbus協(xié)議是現(xiàn)在國內(nèi)工業(yè)領(lǐng)域應(yīng)用最多的協(xié)議,不只PLC設(shè)備,各種終端設(shè)備,比如水控機、水表、電表、工業(yè)秤、各種采集設(shè)備,
4.特點
-
采用主從問答方式進行通信
-
Modbus TCP是應(yīng)用層協(xié)議,基于傳輸層TCP協(xié)議實現(xiàn)
-
Modbus TCP端口號默認為502,但在本案例中修改成6800,當然你也可以改成別的
六:代碼下載
?鏈接:https://pan.baidu.com/s/1mARLDATOBphLKbecj4sW8g?
提取碼:lggv??
?