산업현장에서 많이 사용하고 있는 MODBUS 프로토콜을 ComfilePi에서 동작시키는 방법을 알아보겠습니다. Nmodbus4 라이브러리를 이용하면 쉽고 간단하게 MODBUS프로토콜을 구현할 수 있습니다.
ComfilePi의 RS232 COM0와 PLC의 RS232 포트에 TX-RX, RX-TX, GND-GND로 연결합니다. ComfilePi와 PLC의 RS232 결선은 아래와 같습니다.
※PLC는 컴파일테크놀로지의 MSB612RA-DC 제품입니다.
NModbus4 라이브러리를 Visual Studio 2017에 추가하는 방법에 대해 알아보겠습니다.
NModbus4의 Function(함수)와 Serial Port설정에 대해 알아 보겠습니다.
string portName = Environment.OSVersion.Platform == PlatformID.Win32NT ? "COM0" : "/dev/serial0"; SerialPort port = new SerialPort(portName, 115200); port.ReadTimeout = 100; port.WriteTimeout = 100; port.Open();
bool[] ReadCoils(byte slaveAddress, ushort startAddress, ushort numberOfPoints); ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints); ushort[] ReadInputRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints); bool[] ReadInputs(byte slaveAddress, ushort startAddress, ushort numberOfPoints); ushort[] ReadWriteMultipleRegisters(byte slaveAddress, ushort startReadAddress, ushort numberOfPointsToRead, ushort startWriteAddress, ushort[] writeData); void WriteMultipleCoils(byte slaveAddress, ushort startAddress, bool[] data); void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data); void WriteSingleCoil(byte slaveAddress, ushort coilAddress, bool value); void WriteSingleRegister(byte slaveAddress, ushort registerAddress, ushort value);
ModbusRTU 프로토콜로, NModbus4를 이용한 예제 프로그램 입니다. ComfilePi를 Master로 PLC(MSB612RA-DC)를 Slave로 작성 되었습니다.
소스코드 다운로드 ☞ simple_modbus_example.zip
using Modbus.Device; using System; using System.IO.Ports; using System.Windows.Forms; namespace SimpleModbusExample { public partial class Form1 : Form { const int SLAVE_ADDRESS = 1; const int COIL_ADDRESS = 32; public Form1() { InitializeComponent(); } SerialPort _port; ModbusSerialMaster _master; private void Form1_Load(object sender, EventArgs e) { // Intialize serial port string portName = Environment.OSVersion.Platform == PlatformID.Win32NT ? "COM0" : "/dev/serial0"; _port = new SerialPort(portName, 115200); _port.ReadTimeout = 100; _port.WriteTimeout = 100; _port.Open(); // Initialize Modbus master _master = ModbusSerialMaster.CreateRtu(_port); // Read the current state of the output ReadState(); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { // Destroy Modbus master _master.Dispose(); _master = null; // Destroy serial port _port.Close(); _port.Dispose(); _port = null; } private void OnButton_Click(object sender, EventArgs e) { // Turn output ON _master.WriteSingleCoil(SLAVE_ADDRESS, COIL_ADDRESS, true); } private void OffButton_Click(object sender, EventArgs e) { // Turn output OFF _master.WriteSingleCoil(SLAVE_ADDRESS, COIL_ADDRESS, false); } void ReadState() { // Read the current state of the output var state = _master.ReadCoils(SLAVE_ADDRESS, COIL_ADDRESS, 1); // Update the UI if (state[0]) { StateLabel.Text = "On"; } else { StateLabel.Text = "Off"; } } private void ReadStateButton_Click(object sender, EventArgs e) { // Read the current state of the output ReadState(); } } }
소스코드 다운로드 ☞ modbus_example.zip
using System; using System.Windows.Forms; using System.IO.Ports; using Modbus.Device; using System.Threading; namespace ModbusExample { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private volatile bool _stopModbus; private Thread _modbusThread; private void RunModbus() { string portName = Environment.OSVersion.Platform == PlatformID.Win32NT ? "COM0" : "/dev/serial0"; SerialPort port = new SerialPort(portName, 115200); port.ReadTimeout = 100; port.WriteTimeout = 100; port.Open(); _stopModbus = false; ModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port); IAsyncResult result = null; while(!_stopModbus) { // Read UI's button states and assign to device's outputs bool[] outputs = new bool[4]; result = BeginInvoke(new Action(() => { outputs[0] = button1.IsOn; outputs[1] = button2.IsOn; outputs[2] = button3.IsOn; outputs[3] = button4.IsOn; })); while (!_stopModbus && !result.IsCompleted) { Thread.Yield(); } if (!_stopModbus) { try { master.WriteMultipleCoils(1, 32, outputs); } catch (Exception ex) { Console.WriteLine(ex.Message); } } // Read inputs and assign to UI's Lamps if (!_stopModbus) { try { var inputs = master.ReadCoils(1, 8, 4); result = BeginInvoke(new Action(() => { lamp8.IsOn = inputs[3]; lamp9.IsOn = inputs[2]; lamp10.IsOn = inputs[1]; lamp11.IsOn = inputs[0]; })); } catch (Exception ex) { Console.WriteLine(ex.Message); } } while (!_stopModbus && !result.IsCompleted) { Thread.Yield(); } } master.Dispose(); port.Close(); port.Dispose(); } private void Form1_Load(object sender, EventArgs e) { _modbusThread = new Thread(RunModbus); _modbusThread.IsBackground = true; _modbusThread.Start(); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { _stopModbus = true; _modbusThread.Join(); } } }