====== 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 |}}