====== Creating a CUWIN Modbus RTU Master with NModbus ======
In this document we'll use the CUWIN as a Modbus master, and a CUBLOC-based [[https://comfiletech.com/new-products-for-2014-2015/msb612ra-dc/|MSB612RA-DC]] as a Modbus slave.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:cubloccuwin.png |}}
===== Create the Modbus Slave =====
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:msb612ra-dc.png |}}
Configure the serial settings in MSB Logic.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:msbsetup.png?800 |}}
Nothing else is needed in Ladder Logic except and ''END''ing rung.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:msbladderlogic.png?800 |}}
===== Connecting the CUWIN to the MSB612RA-DC =====
For this exercise we will use the [[https://comfiletech.com/touch-panel-pc/windows-ce-panel-pc/cwv-070br-7-bezel-cortex-a8-1ghz/|CUWIN CWV070]] as the Modbus master.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:cwv070br.png |}}
Connect the CUWIN's RS-232 COM1 port to the MSB612RA-DC RS-232 Ch1 port.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:rs-232connection.png |}}
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:cuwin_cwv_msb_connection.jpg |}}
===== Create the Modbus Master =====
==== NModbus ====
NModbus is a free and open source implementation of the Modbus protocol for the .Net Framework and .Net Compact Framework. It is currently hosted at https://code.google.com/p/nmodbus/ but there are also [[https://github.com/search?utf8=%E2%9C%93&q=nModbus|other forks on GitHub]].
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:nmodbussite.png |}}
Download the NModbus binaries.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:nmodbussite2.png |}}
After downloading and extracting the NModbus binaries from the repository, there will be two folders inside the "bin" folder: //net// and //netcf//. //net// is for the .Net Framework, and //netcf// is for the .Net Compact Framework. The CUWIN is a .Net Compact Framework device, so //netcf// is the appropriate folder.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:netcffolder.png |}}
==== Programming the CUWIN ====
Add a reference to the //Modbus.dll// file in //netcf// folder introduced above.
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:nmodbusaddreference.png |}}
Add a ''using'' declaration to the source file that will contain the Modbus Master code.
using Modbus.Device;
Create a new [[https://msdn.microsoft.com/en-us/library/system.io.ports.serialport%28v=vs.90%29.aspx|SerialPort]] on COM1 with the same serial settings as the Modbus slave.
_serialPort = new SerialPort("COM1", 115200);
_serialPort.Open();
Initialize a new ''ModbusSerialMaster'' with the ''_serialPort'' as its transport.
_modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
_modbusMaster.Transport.ReadTimeout = 500;
_modbusMaster.Transport.WriteTimeout = 500;
_modbusMaster.Transport.Retries = 0;
Then call any of the given methods to query a Modbus slave:
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);
===== Sample Project =====
The following sample project will toggle MSB624RA-DC's relay 32 (at address 32) and read from the MSB624RA-DC's analog input (at address 276). Refer to the following table for the MSB624RA-DCs Modbus memory map.
^ Word (Holding/Input Registers) \\ Function Codes: 3, 4, 6, 16 ^^
^ Address (base 10) ^ Data Region (base 10) ^
| 0 ~ 255 | D Region (D0 ~ D255) |
| 256 ~ 355 | Y Region (Y0 ~ Y99) \\ ADC Result Y20 ~ Y27 (276 ~ 283) |
| 1000 ~ 1255 | T Region (T0 ~ T255) |
| 2000 ~ 2255 | C Region (C0 ~ C255) |
| 3000 ~ 3255 | WM Region (WM0 ~ WM255) |
^ Bit (Coil, Input Status) \\ Function Codes: 1, 2, 4, 15 ^^
^ Address (base 10) ^ Data Region (base 10) ^
| 0 ~ 127 | P Region (P0 ~ P127) |
| 4096 ~ 6143 | M Region (M0 ~ M2047) |
==== C# Source Code ====
{{:cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:cuwin_cwv_modbus.zip|Download the C# Project Files}}
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Modbus.Device;
using System.IO.Ports;
namespace NModbusTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
ModbusSerialMaster _modbusMaster;
SerialPort _serialPort;
const int SLAVE_ADDRESS = 1;
private void Form1_Load(object sender, EventArgs e)
{
_serialPort = new SerialPort("COM1", 115200);
_serialPort.Open();
_modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
_modbusMaster.Transport.ReadTimeout = 500;
_modbusMaster.Transport.WriteTimeout = 500;
_modbusMaster.Transport.Retries = 0;
}
void ReportError(string s)
{
MessageBox.Show(s);
}
private void ReadADC()
{
try
{
ushort[] result = _modbusMaster.ReadInputRegisters(SLAVE_ADDRESS, 276, 1);
_adcLabel.Text = result[0].ToString();
}
catch (Exception ex)
{
ReportError(ex.Message);
}
}
private void ReadDigitalOutput()
{
try
{
bool[] result = _modbusMaster.ReadCoils(SLAVE_ADDRESS, 32, 1);
if (result[0])
{
_digitalOutLabel.Text = "ON";
}
else
{
_digitalOutLabel.Text = "OFF";
}
}
catch (Exception ex)
{
ReportError(ex.Message);
}
}
private void WriteDigitalOutput(bool on)
{
try
{
_modbusMaster.WriteSingleCoil(SLAVE_ADDRESS, 32, on);
}
catch (Exception ex)
{
ReportError(ex.Message);
}
}
private void _digitalOutReadButton_Click(object sender, EventArgs e)
{
ReadDigitalOutput();
}
private void _onButton_Click(object sender, EventArgs e)
{
WriteDigitalOutput(true);
}
private void _offButton_Click(object sender, EventArgs e)
{
WriteDigitalOutput(false);
}
private void _adcReadButton_Click(object sender, EventArgs e)
{
ReadADC();
}
private void Form1_Closing(object sender, CancelEventArgs e)
{
_modbusMaster.Dispose();
_modbusMaster = null;
_serialPort.Close();
_serialPort.Dispose();
_serialPort = null;
}
}
}
==== VB.NET Source Code ====
{{:cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:nmodbustestvb.zip|Download the VB.Net Project Files}}
Imports System
Imports System.Linq
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Imports System.Threading
Imports Modbus.Device
Imports System.IO.Ports
Public Class Form1
Private _modbusMaster As ModbusSerialMaster
Private _serialPort As SerialPort
Const SLAVE_ADDRESS As Integer = 1
Private Sub ReportError(ByVal s As String)
MessageBox.Show(s)
End Sub
Private Sub ReadADC()
Try
Dim result As UShort() = _modbusMaster.ReadInputRegisters(SLAVE_ADDRESS, 276, 1)
_adcLabel.Text = result(0).ToString()
Catch ex As Exception
ReportError(ex.Message)
End Try
End Sub
Private Sub ReadDigitalOutput()
Try
Dim result As Boolean() = _modbusMaster.ReadCoils(SLAVE_ADDRESS, 32, 1)
If result(0) Then
_digitalOutLabel.Text = "ON"
Else
_digitalOutLabel.Text = "OFF"
End If
Catch ex As Exception
ReportError(ex.Message)
End Try
End Sub
Private Sub WriteDigitalOutput(ByVal [on] As Boolean)
Try
_modbusMaster.WriteSingleCoil(SLAVE_ADDRESS, 32, [on])
Catch ex As Exception
ReportError(ex.Message)
End Try
End Sub
Private Sub _digitalOutReadButton_Click(ByVal sender As Object, ByVal e As EventArgs)
ReadDigitalOutput()
End Sub
Private Sub _onButton_Click(ByVal sender As Object, ByVal e As EventArgs)
WriteDigitalOutput(True)
End Sub
Private Sub _offButton_Click(ByVal sender As Object, ByVal e As EventArgs)
WriteDigitalOutput(False)
End Sub
Private Sub _adcReadButton_Click(ByVal sender As Object, ByVal e As EventArgs)
ReadADC()
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As CancelEventArgs)
_modbusMaster.Dispose()
_modbusMaster = Nothing
_serialPort.Close()
_serialPort.Dispose()
_serialPort = Nothing
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
_serialPort = New SerialPort("COM1", 115200)
_serialPort.Open()
_modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort)
_modbusMaster.Transport.ReadTimeout = 500
_modbusMaster.Transport.WriteTimeout = 500
_modbusMaster.Transport.Retries = 0
End Sub
End Class
==== Demonstration Video ====
{{ :cuwin:creating_a_cuwin_modbus_rtu_master_with_nmodbus:nmodbusexample.mp4?700x394 |}}