i have to create a Modbus RTU slave in c#. the problem is that i am a
total newbie in c#. i have read the examples on nmodbus.com and am
trying to build a modbus rtu slave that send a response after a
request from a master (i use "modbus poll" as master) but it doesn't
work. Can someone please be so kind an give me a little example of a
modbus rtu slave which is able to send a response after receiving a
request?? Thank you!
All the best,
Rene
It is important to have the serial ports configured identically. This
worked for me.
// ModPoll
.\modpoll.exe -m rtu -a 1 -r 100 -p none COM6
// NModbus
using (SerialPort slavePort = new SerialPort("COM5"))
{
// configure serial port
slavePort.BaudRate = 9600;
slavePort.DataBits = 8;
slavePort.Parity = Parity.None;
slavePort.StopBits = StopBits.One;
slavePort.Open();
byte unitID = 1;
// create modbus slave
ModbusSlave slave = ModbusSerialSlave.CreateRtu(unitID, slavePort);
slave.Listen();
}
Scott
It works now. I made a stupid mistake. Thank you for your Help!
Rene
It's me again. The RTU Slave is running now. But there is another
problem. I don't know how i can set the values of the register which
are send by the slave after a request from a master. Can someone
please give me a little example how i can do this or explain me what
to do? Thank you!
Rene
using (SerialPort slavePort = new SerialPort("COM4"))
{
// configure serial port
slavePort.BaudRate = 9600;
slavePort.DataBits = 8;
slavePort.Parity = Parity.None;
slavePort.StopBits = StopBits.One;
slavePort.Open();
byte unitID = 1;
// create modbus slave
ModbusSlave slave = ModbusSerialSlave.CreateRtu(unitID, slavePort);
// use the factory to create Modbus data model
slave.DataStore = DataStoreFactory.CreateDefaultDataStore();
// setup a few individual register values...
slave.DataStore.HoldingRegisters[5] = 45;
slave.Listen();
}
Scott
Testing the following code, the output wouldn't be:
Register 0 = 0
Register 1 = 1
Register 2 = 2
Register 3 = 3
?
I have here:
Register 0 = 1
Register 1 = 2
Register 2 = 3
Register 3 = 0
Thanks
(Nice this feature!)
---
static void Main(string[] args)
{
byte slaveID = 1;
int port = 502;
IPAddress address = new IPAddress(new byte[] { 127, 0, 0,
1 });
//create and start the TCP slave
TcpListener slaveTcpListener = new TcpListener(address,
port);
slaveTcpListener.Start();
ModbusSlave slave = ModbusTcpSlave.CreateTcp(slaveID,
slaveTcpListener);
slave.DataStore =
DataStoreFactory.CreateDefaultDataStore();
slave.DataStore.HoldingRegisters[0] = 0;
slave.DataStore.HoldingRegisters[1] = 1;
slave.DataStore.HoldingRegisters[2] = 2;
slave.DataStore.HoldingRegisters[3] = 3;
Thread slaveThread = new Thread(slave.Listen);
slaveThread.Start();
// create the master
TcpClient masterTcpClient = new
TcpClient(address.ToString(), port);
ModbusTcpMaster master =
ModbusTcpMaster.CreateTcp(masterTcpClient);
ushort numInputs = 4;
ushort startAddress = 0;
// read five register values
ushort[] inputs =
master.ReadHoldingRegisters(1,startAddress, numInputs);
for (int i = 0; i < numInputs; i++)
Console.WriteLine("Register {0}={1}", startAddress +
i, inputs[i]);
// clean up
masterTcpClient.Close();
slaveTcpListener.Stop();
slaveThread.Abort();
Console.ReadKey();
}
>From the spec http://modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
"In the PDU Registers are addressed starting at zero. Therefore
registers numbered 1-16 are addressed as 0-15."
So if you read from address 0, you will get
DataStore.HoldingRegister[1], meaning there is no way through the
protocol to read DataStore.HoldingRegister[0] as it is an invalid
Modbus address.
In other words, the Modbus Data Model has a 1 origin and the .NET
Collection has a 0 origin. Perhaps I should disallow the setting of
DataStore.HoldingRegister[0] altogether, but since this is just for
testing I though I would just leave it for now.
Scott
I think that I am having the same problem.
When using NModbus master I have to use a start address of 0 in
ModbusMaster.ReadHoldingRegisters to read MODBUS address 40001.
Thus the NModbus master uses an address base of 0.
However, when I set up a NModbus slave to listen, if I making a
request for address 40001 I am getting the exception
"System.ArgumentOutOfRangeException: Specified argument was out of the
range of valid values.
Parameter name: Start address was out of range. Must be non-negative
and <= the size of the collection."
My master is correctly making a request for 40001 by setting the
address in the data stream to a value of 0.
However, the DataStore.ReadData is decrementing the start address and
throwing the above error.
The NModbus slave seems to use an address base of 1.
Accoding to the specification you referenced, in section "4.4 Modus
Addressing Model", it states
"The pre-mapping between the MODBUS data model and the device
application is totally
vendor device specific."
The "device application" level is represented in NModus by the
DataStore. (Should this be 1 based or 0 based addressing?)
The "MODBUS data model" uses 1 based addressing (this is conceptual
and need not ever actually be realised).
The "MODBUS PDU address" uses 0 based addressing.
There is currently an inconsistency with how NModbus interprets a
start address for a master or for a slave.
I think that the DataStore.ReadData and DataStore.WriteData should not
perform the decrementation.
This would make the DataStore zero based and in line with the
Collection classes, simplifing things a lot.
Nick Crane
You have very valid points. Please try the beta version available on
the downloads section of the site. I made some changes last week b/c
of the inconsistencies you mentioned.
What we know:
1) In a MODBUS PDU each data is addressed from 0 to 65535 (0 origin)
2) In the MODBUS data Model each element within a data block is
numbered from 1 to n (1 origin)
3) .NET collections are of 0 origin
To resolve this mapping issue I created a custom collection class
ModbusDataCollection which is derived from Collection<T> and overrides
protected methods InsertItem, SetItem, RemoveItem, and ClearItems in
order to simulate a 1 origin collection.
I believe you are suggesting to simplify things by making the NModbus
Data Model 0 origin but I think that goes against the spec (not that
my original version didn't ;) ). I interpret the statement "The pre-
mapping between the MODBUS data model and the device application is
totally vendor device specific." to mean the mapping from
ModbusDataModel [1] -> DeviceSpecific [4001] which is not relevant to
the NModbus slave.
So try the Beta and let me know what you think.
Thanks for the feedback!
Scott