이 글에서 나오는 소스코드는 읽는이의 이해를 돕기 위해 필자가 직접 작성한 "손코딩" 이므로, 동작을 보장하지 않습니다.
net452 를 대상으로 하는 SerialPort(RTU) 를 통한 모드버스 통신을 다음과 같은 라이브러리로 구현을 진행하였습니다.
EasyModbus
EasyModbus 는 간결하게 코드를 작성할 수 있도록 도와주고 있습니다.
public class Modbus_Example {
public Modbus_Example() {
try {
ModbusClient modbusClient = new ModbusClient("COM1");
modbusClient.UnitIdentifier = 2;
modbusClient.Baudrate = 9600;
modbusClient.Parity = System.IO.Ports.Parity.None;
modbusClient.StopBits = System.IO.Ports.StopBits.One;
modbusClient.ConnectionTimeout = 500;
modbusClient.Connect();
if (modbusClient.Connected) {
int[] response_data = modbusClient.ReadHoldingRegisters(0, 0x24);
// 관련 동작 ~~
} else {
// 관련 동작 ~~
}
} catch (Exception ex) {
// 예외처리
}
}
}
대체적으로 이런 느낌입니다.
이 라이브러리를 가지고 2개 이상의 SerialPort, 2개 이상의 SlaveAddress(ID) 를 통신해야 했었는데,
한번 ReadHoldingRegisters 를 사용한 뒤에 다른 장치의 데이터를 가져오기 위해 finally 에 modbusClient.Disconnect() 를 항상 사용하여 구현을 진행했었습니다. (좀 모시깽 함)
그리고 Connect() 를 시도할 때 발생하는 것인지 new ModbusClient() 를 시도할 때 발생하는 것인지
라이브러리를 사용할 때마다 Output 창에 4줄 정도 되는 문구가 나타나, 상당히 로그를 보기 까다롭더라구요..
NModbus4
설명에서 보다시피 NModbus 는 RTU 를 지원하지 않아서 사용할 수 없었습니다.
NModbus4 를 이용하여 개발한 예제
public class Modbus_Example {
public Modbus_Example() {
serialPorts = new SerialPort[2];
modbusSerialMasters = new ModbusSerialMaster[2];
try {
for (int i = 0; i < 2; i++) {
try {
serialPorts[i] = new SerialPort(COMM[i], 9600);
serialPorts[i].Parity = Parity.None;
serialPorts[i].StopBits = StopBits.One;
serialPorts[i].ReadTimeout = 100;
serialPorts[i].WriteTimeout = 100;
serialPorts[i].Open();
modbusSerialMasters[i] = ModbusSerialMaster.CreateRtu(serialPorts[i]);
} catch (Exception ex) {
// 예외처리
}
}
if (modbusClient.Connected) {
int[] response_data = modbusClient.ReadHoldingRegisters(0, 0x24);
// 관련 동작 ~~
} else {
// 관련 동작 ~~
}
} catch (Exception ex) {
// 예외처리
}
}
public void run() {
while (true) {
int currentPortIndex = ~~~
if (serialPorts[currentPortIndex].IsOpen) {
try {
ushort[] response_data = modbusSerialMasters[currentPortIndex].ReadHoldingRegisters(#SlaveAddress#, 0, 19);
// 관련 동작 ~~
} catch (Exception ex) {
// 예외처리
}
} else {
serialPorts[currentPortIndex].Open();
if (serialPorts[currentPortIndex].IsOpen)
modbusSerialMasters[currentPortIndex] = ModbusSerialMaster.CreateRtu(serialPorts[currentPortIndex]);
}
}
Thread.Sleep(500);
}
protected SerialPort[] serialPorts;
protected ModbusSerialMaster[] modbusSerialMasters;
}
대략적인 차이는 다음과 같습니다.
- NModbus4 는 RTU 통신에서 System.IO.Ports 의 SerialPort 를 사용합니다.
- NModbus4 는 ModbusSerialMaster.CreateRtu(System.IO.Ports.SerialPort) 메서드에서 SerialPort 인자를 요구하며 ModbusSerialMaster 로 리턴합니다.
- NModbus4 는 ReadHoldingRegister() 메서드에서 SlaveAddress 를 지정할 수 있습니다.
EasyModbus 는 new ModbusClient() 에서 SlaveAddress 를 지정했었습니다.
이제 따로 Disconnect 할 필요 없이 한 SerialPort 에서 여러 SlaveAddress 에 있는 데이터를 자유롭게 요청할 수 있게 되었습니다. - EasyModbus 는 int[] 로, NModbus4 는 ushort[] 로 리턴합니다.
필자는 처음에 EasyModbus 를 이용해 온도센서 통신을 구현하였었는데요,
이후에 다른 모듈과 통신하기 위해 EasyModbus 를 이용하여 테스트 중에 문제가 발생했었고
해당 문제는 "값이 잘 들어오다가 갑자기 마이너스 값이 들어오는 문제" 였습니다.
확인 끝에 unsigned 를 사용해야 한다는 것을 알게 되었고, 해당 라이브러리는 사용이 불가능했습니다.
그래서 다른 라이브러리를 모색하던 중 NModbus4 를 알게 되었고 EasyModbus 보다 더욱 잘 활용하고 있습니다.
사실 맨 처음 Modbus Library 를 찾을 때에는 NModbus4 를 먼저 찾았었으나
EasyModbus 를 선택한 이유는 예제와 공식 사이트 등에서 자료가 많았기 때문이었던 것 같습니다 :)
위에서 언급된 각 라이브러리에 대한 Github 주소목록
EasyModBus : https://github.com/rossmann-engineering/EasyModbusTCP.NET
NModbus4 : https://github.com/NModbus4/NModbus4
NModbus : https://github.com/NModbus/NModbus