unity連接modbus需要有NModbus4.dll文件
文件地址: (下載地址)
1.配置一個modbus主站(MThings測試軟件挺好用的)
2.unity用來連接
下面直接上代碼

using Modbus.Device; using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; public class Concent_ : MonoBehaviour { public ModbusMaster modbusIpMaster; public TcpClient tcpClient; IPAddress address = new IPAddress(new byte[] { 127,0,0,1 }); public int port = 502; public bool conen = false; public bool Reda_White = false; private ushort[] udata = new ushort[]{0x03}; private ushort star=1; Thread mythread; //Thread youthread; public bool isconect = false; // Start is called before the first frame update void Start() { if (Connect (address.ToString (), port)) { Debug.Log("連接成功"); } else { Debug.Log("連接失敗"); } } public bool Connect(string ip, int port) { try { tcpClient = new TcpClient(ip, port); tcpClient.SendTimeout = 1; modbusIpMaster = ModbusIpMaster.CreateIp(tcpClient); //開啟另一個線程(可用可不用) mythread = new Thread(WriteMessageFromClient); //線程開啟 mythread.Start(); conen = true; return true; } catch (Exception ex) { tcpClient.Close(); Debug.LogError(ex.Message); return false; } } public void WriteMessageFromClient() { while (conen) { try { if (Reda_White) { Write_jiChunQi(star, udata); Debug.Log("發送成功"); } if (kuse) { ushort [] msg= modbusIpMaster.ReadHoldingRegisters(0x01,1, 0x01); } } catch { break; } } tcpClient.Close(); } public void Write_jiChunQi(ushort star,ushort[]data) { modbusIpMaster.WriteMultipleRegisters(1,star, data); } /// 10進制轉16進制 private byte GetHex(string msg) { byte hex = Convert.ToByte(msg); return hex; } ///16進制轉10進制 public int GetDex(string msg) { int res = Convert.ToInt32(msg,16); return res; } //退出的時候關閉連接 private void OnApplicationQuit() { tcpClient.Close(); } public bool kuse = false; private void Update() { if (Input.GetMouseButtonDown(0)) { //WriteMultipleRegisters(設備地址,寄存器起始地址, 要寫入的值) modbusIpMaster.WriteMultipleRegisters(0x01,star,udata); Debug.Log("發送成功"); } if (Input.GetKeyDown (KeyCode.Space)) { //ReadHoldingRegisters(設備地址, 寄存器起始地址, 讀取數量); 讀取多個寄存器 ushort[] msg = modbusIpMaster.ReadHoldingRegisters(0x01, 1, 0x06); foreach (var item in msg) { Debug.Log(item); } } } }
讀取AI模塊
public void Button_4() { //讀取AI模塊的值 功能碼04 ReadInputRegisters(設備地址,起始地址,讀取值的地址) ushort[] msg = modbusIpMaster.ReadInputRegisters(0x01, 0x01, 0x01); foreach (var item in msg) { //轉換二進制 item是十進制的 string ooo = Convert.ToString(item, 2); string s= ooo; //因為數據采集模塊是16位的 所以得往前面補0 變成一個16位數 for (int i = 0; i < 16 - ooo.Length; i++) s = s.Insert(0, "0"); //遍歷字符串長度 for (int i =0;i<s.Length;i++) { //看看字符串中哪一位是1 代表數據采集模塊那個對應的孔 是開的狀態 if (s[i].ToString() == "1") { Debug.Log(16-i+ "開"); } } } }
連接PLC1200
using S7.Net; using System; using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; using UnityEngine.UI; using System.Net; using System.Net.Sockets; using Modbus.Device; public class PLC_Controller : MonoBehaviour { public enum PLCTYPE { S71200, S7200smart } Plc PLC1;//= new Plc(CpuType.S71200 ,"192.168.1.1",0,1); //在這里對Plc這類進行新建,public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot),這個函數是對連接的Plc進行一個訪問初始化 //需要的參數有PLC類型、IP地址、插槽號、機架號。其中后兩項在博途CPU屬性中可以查到。 private TcpClient tcpClient; private ModbusMaster modbusIpMaster; public PLCTYPE pLCTYPE = PLCTYPE.S71200; Thread PLCLink,NDATLink; public byte HighSet; public byte result; public double RealSet; public byte ReadValve; public ushort intValve; [Header ("模型cube")] public Cube_Ration Cube_Ration; float UnityHigh; // Start is called before the first frame update void Start() { BtnOpen(); } public void BtnOpen() { try { if (pLCTYPE == PLCTYPE.S71200) { PLC1 = new Plc(CpuType.S71200, "192.168.1.88", 0, 1); //這里的是做了一個外部設置IP地址的操作 PLC1.Open(); } else if (pLCTYPE == PLCTYPE.S7200smart) { PLC1 = new Plc(CpuType.S7200, "192.168.1.88", 0, 1); //這里的是做了一個外部設置IP地址的操作 PLC1.Open(); } //打開與PLC的連接是有兩種方式,一個是Open();另一個是OpenAsync(),兩者之間的功能是一樣的,前者可以返回錯誤信息,比較適合初學者。 if (PLC1.IsConnected) //判斷是否連接 { Debug.Log("Plc is Connected+連接成功"); PLCLink = new Thread(LinkThread); PLCLink.Start(); //這里我是想做一個階段性的中斷,所以選用另起線程,將查詢和寫入PLC的功能放入新的線程,數據處理在主線程。用來防止線程卡死。 } else { Debug.Log("PLC 連接不成功,請檢查IP地址、機架、插槽等是否正確"); } } catch (Exception ex) { Debug.Log(ex); } } public void BtnClose() { try { PLC1.Close(); //關閉與PLC的連接 } catch (Exception ex) { Debug.Log(ex); throw; } } public void BtnRead() { //復位按鍵 try { PLC1.Write("DB1.DBX0.0", true); //我做的是Unity上的仿真PID所以需要一個復位按鍵。 //PLC1.Write("M0.0", false); } catch (Exception ex) { Debug.Log(ex); throw; } } bool db1; public void LinkThread() { while (true) { if (PLC1.IsConnected) { //RealSet = Convert.ToDouble(PLC1.Read("DB1.DBW2")); //讀取PLC的值,這邊我還沒有做的很滿意,大家可以根據S7.Net的說明書和自己的意圖來寫合適的代碼 //result = (byte)PLC1.Read("MB103"); //intValve = (ushort)PLC1.Read("DB3.DBW12"); //Debug.Log("DB1.DBW2:"+RealSet); Thread.Sleep(100); //Debug.Log ( PLC1.Read("DB1.DBX0.1")); RealSet = Convert.ToDouble(PLC1.Read("Q0.0")); Debug.Log(PLC1.Read("M0.0")+" "+ RealSet+" "+ Convert.ToInt16(PLC1.Read("VW0"))); if (Convert.ToBoolean(PLC1.Read("M0.0"))==true|| sderr) { Cube_Ration.ration = true; Debug.Log("觸發不觸發:"+Cube_Ration.ration); } else { Cube_Ration.ration = false; } if (sder ) { //PLC1.Write("DB1.DBW4", 110); //寫入成功 //BtnRead(); //Debug.Log(PLC1.Read("DB1.DBX0.1")); PLC1.Write("Q0.1", 1 ); PLC1.Write("M0.1", 1); Debug.Log("寫入成功"); } //else if (sderr) //{ // PLC1.Write("M0.1", false); //} } } } private void OnApplicationQuit() { BtnClose(); } bool sder = false; bool sderr = false; // Update is called once per frame void Update() { if (Input.GetKeyUp (KeyCode.Space )) { sder = false; } if (Input.GetKeyDown(KeyCode.Space)) { sder = true; } if (Input.GetKeyUp(KeyCode.A)) { sderr = false; } if (Input.GetKeyDown(KeyCode.A)) { sderr = true; } } }
plc_腳本隨便掛在一個物體上面
下面是Cube 測試腳本,接受到PLC信號,cube旋轉測試
using System.Collections; using System.Collections.Generic; using UnityEngine; public enum 旋轉軸 { X,Y,Z,NONE } public class Cube_Ration : MonoBehaviour { [Header ("旋轉速度")] public float rotionspeed = 250; public 旋轉軸 rotationAxis = 旋轉軸.NONE; public bool ration; public Vector3 axisv; public 旋轉軸 rotationAxis_; public GameObject gam; private void Start() { Rotation_(); } public void Rotation_() { switch (rotationAxis) { case 旋轉軸.X: axisv = new Vector3 (1,0,0); break; case 旋轉軸.Y: axisv = new Vector3(0, 1, 0); break; case 旋轉軸.Z: axisv = new Vector3(0, 0, 1); break; case 旋轉軸.NONE: axisv = Vector3.zero; break; default: break; } rotationAxis_ = rotationAxis; } void Update() { if (rotationAxis_!=rotationAxis) { Rotation_(); Debug.Log("不一樣了"); } if (ration) { this.transform.RotateAround(this.transform.localPosition, axisv, Time.deltaTime* rotionspeed); } } }