Unity连接ModbusTcp发送读取寄存器
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); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器