Modbus Server Methods

Modbus server implements a subset of modbus protocol functions. This server works with serial devices (RTU-ASCII) or Open modbus protocol.

One important comment about these functions is that all functions use a zero based index for address. For instance, if you want to set the register 40001 (4x) using PresetSingleRegister function you set the parameter nRegister equal to 0.

Connection methods

OpenConnection - creates a new connection object for use with serial port or tcp/ip network

Connect - This function must be called after setting connection parameters.

CloseConnection - Free a connection resource , for instance , a com port.

CreateTCPServer - Creates a new CTCPIPServer object. This object manages TCP/IP serial port interface.

Modbus Functions:

ReadOutputRegisters Reads the holding registers (4x references).

ReadInputRegisters Reads the input registers (3X references).

ReadOutputStatus Reads the discrete outputs (0x references, coils).

ReadInputStatus Reads the discrete inputs (1x references, coils).

PresetSingleRegister Presets a single holding register (4x reference)

ForceSingleCoil Forces a single coil (0x reference) to either True or False (On/Off).

PresetMultipleRegisters Presets multiple holding registers (4x reference)

ForceMultipleCoils Forces multiple coils(0x reference) to either True or False (On/Off).

TxRxMessage this function can be used for sending custom messages to a slave.

 

OpenConnection

Syntax

mbusrv.OpenConnection( Name,Type);

HRESULT OpenConnection([in] BSTR Name , [in] short Type , [out,retval] VARIANT_BOOL* pbOk);

Return Value

True if function succeeds or False if fails.

Parameters

Name - A string that specifies the connection's name.

Type - Type of connection.
mbLOCAL=1 - connect with using serial port
mbREMOTE=2 - connect using TCP/IP.

Remarks

This method must be called before setting connection properties like baudrate, Parity...

The connection name can be any text , but you can only share connections among apps with the same connection name. Use , for instance , Name = "com2" for opening com2 , "com3" for opening com3 and so on. To connect using TCP/IP you must have a device that use Open Modbus Protocol or enable TCP Serial port interface.

Example

Dim mbusrv AS ModbusSrv

Set mbusrv = New ModbusSrv

'Set parameters as your hardware requirements
mbusrv.OpenConnection "com3", mbLOCAL 'change com port
mbusrv.ComPort = 3 'change com port
mbusrv.BaudRate = 9600
mbusrv.Parity = mbNOPARITY
mbusrv.FlowControl = mbFC_NONE
mbusrv.SilentInterval = 20
mbusrv.TimeOut = 500
mbusrv.TransmissionMode = mbMODE_RTU
mbusrv.ByteSize = 8
mbusrv.StopBits = 2

mbusrv.RaiseError = False

If Not mbusrv.Connect Then
MsgBox "Error connecting with serial port Try Change Parameters of serial port"

End if

See Also: Connect

Connect

Syntax

mbusrv.Connect

HRESULT Connect([out,retval] VARIANT_BOOL* pbConnected);

Return Value

True if function succeeds or False if fails.

Remarks

Calling "Connect" actually try to open a comm port or try to connect with a tcp server. The comm parameters (Stopbits, baudrate..) or TCP Client parameters (Host, Port and Timeout) must be set before calling this method.

Example

'Set m_ModbusServer = New ModbusSrv

'Set parameters as your hardware requirements
m_ModbusServer.OpenConnection "127.0.0.1:502", mbREMOTE
m_ModbusServer.Host = "127.0.0.1"
m_ModbusServer.TcpPort = 502
m_ModbusServer.TimeOut = 2000

If Not m_ModbusServer.Connect Then
MsgBox "Error connecting with TCP/IP Server. Try to change TCP/IP parameters"
end if

See Also:OpenConnection

 

CloseConnection

Syntax

mbusrv.CloseConnection

HRESULT CloseConnection();

Remarks

Free a connection resource , for instance , a com port.

See Also:Connect , OpenConnection

 

CreateTCPServer

Syntax

mbusrv.CreateTCPServer()

HRESULT CreateTCPServer([out,retval] LPDISPATCH* pVal);

Return Value

A CTCPIPServer object correspondent to the local connection of mbusrv. For instance , if mbusrv correspond to a connection with com2 , calling this method you create a CTCPIPServer object and with this last object you can start the server and enable TCP/IP clients send messages to com2.

Remarks

Creates a new CTCPIPServer object. This object manages TCP/IP serial port interface.

 

Example

Dim objTcpServer As Object

'mbusrv must be a local connection created in some other place.

Set objTcpServer = mbusrv.CreateTCPServer

'change this address
objTcpServer.ServerAddress = "127.0.0.1"
objTcpServer.ServerPort = 502

If Not objTcpServer.StartServer Then
MsgBox "Error Starting TCP Serial Port Interface Try. to change server address "
End If

See Also : CTCPIPServer

 

ReadOutputRegisters

Syntax

mbusrv.ReadOutputRegisters(Addr , DataStart ,Quantity ,vRegValues , [vVarType] ,[vRealType])

HRESULT ReadOutputRegisters([in] short Addr , [in] long DataStart , [in] short Quantity , [in,out] VARIANT* vRegValues , [in,optional] VARIANT vVarType , [in,optional] VARIANT vRealType,[out,retval] long* lpRetVal);

Return Value

A error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

DataStart - Starting Address

Qtd - number of registers to read

vRegValues - array with registers read.

vVarType - vbInteger - read values as signed integer numbers.
vbLong - read values as unsigned integer numbers.(default)
vbSingle - read values as floating point single precision numbers.
vbDouble - read values as floating point double precision numbers.

wRealType - inverse the order of registers when reading real number mbREAL_NORMAL(default) (not reversed) mbREAL_REVERSE (reverse order).

 

Remarks

This function reads the holding registers (4x references), the first register 40001 is 0, second 40002 is 1 and so on..When this function is used for interpret modbus registers as IEEE floating point numbers you have to observe two things: First single precision number (float) has 4 bytes , then we need 2 consecutive registers to form a float number. Another thing we have consider is that not all slaves split the float number in the same manner then you can use wRealType parameter as mbREAL_REVERSE to change the order the 2 consecutive registers to see if the float number is interpreted correctly. The same explanation is valid for double precision numbers , but now we need 4 registers instead of 2 to form a number.

example:

Dim vRegs AS Variant

Dim lError As Long

lError = mbusrv.ReadOutputRegisters(17,107,3,vRegs) ; 'Reads 40108 ...40110 registers from slave 17

See also: RaiseError , Error Codes .

 

ReadInputRegisters

Syntax

mbusrv.ReadInputRegisters(Addr , DataStart ,Quantity ,vRegValues , [vVarType] , [vRealType])

HRESULT ReadInputRegisters([in] short Addr , [in] long DataStart , [in] short Quantity , [in,out] VARIANT* vRegValues , [in,optional] VARIANT vVarType , [in,optional] VARIANT vRealType, [out,retval] long* lpRetVal);

Return Value

A error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

DataStart - Starting Address

Qtd - number of registers to read

vRegValues - array with registers read.

vVarType - vbInteger - read values as signed integer numbers.
vbLong - read values as unsigned integer numbers.(default)
vbSingle - read values as floating point single precision numbers.
vbDouble - read values as floating point double precision numbers.

wRealType - inverse the order of registers when reading real number mbREAL_NORMAL(default) (not reversed) mbREAL_REVERSE (reverse order).

Remarks

This function reads the input registers (3x references), the first register 30001 is 0, second 30002 is 1 and so on.When this function is used for interpret modbus registers as IEEE floating point numbers you have to observe two things: First single precision number (float) has 4 bytes , then we need 2 consecutive registers to form a float number. Another thing we have consider is that not all slaves split the float number in the same manner then you can use vRealType parameter as mbREAL_REVERSE to change the order the 2 consecutive registers to see if the float number is interpreted correctly. The same explanation is valid for double precision numbers , but now we need 4 registers instead of 2 to form a number.

example:

Dim vRegs AS Variant

Dim lError As Long

lError = mbusrv.ReadInputRegisters(17,8,1,vRegs) ; 'Reads 30009 register from slave 17

See also: RaiseError , Error Codes .

 

ReadOutputStatus

Syntax

mbusrv.ReadOutputStatus(Addr ,DataStart ,Quantity ,vCoilValues );

HRESULT ReadOutputStatus([in] short Addr , [in] long DataStart , [in] short Quantity , [out] VARIANT* vCoilValues , [out,retval] long* plRetVal);

Return Value

A error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

DataStart - Starting Address

Qtd - number of registers to read

vCoilValues - array with coils read.

Remarks

Reads the status of discrete outputs (0x references, coils) in the slave, the first coil 00001 is 0, second 00002 is 1 and so on. vCoilValues return the coil on Boolean format (True - On , False - Off).

example:

Dim vCoils AS Variant

Dim lError As Long

lError = mbusrv.ReadOutputStatus(17,19,37,vCoils) ; 'Reads coils 00020 ... 0056 from slave 17

See also: RaiseError , Error Codes .

 

ReadInputStatus

mbusrv.ReadInputStatus(Addr ,DataStart ,Quantity ,vCoilValues );

HRESULT ReadInputStatus([in] short Addr , [in] long DataStart , [in] short Quantity , [out] VARIANT* vCoilValues , [out,retval] long* plRetVal);

Return Value

A error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

DataStart - Starting Address

Qtd - number of registers to read

vCoilValues - array with coils read.

Remarks

Reads the status of discrete inputs (1x references) in the slave, the first input 10001 is 0, second 10002 is 1 and so on.vCoilValues return the coil on Boolean format (True - On , False - Off).

example:

Dim vCoils AS Variant

Dim lError As Long

lError = mbusrv.ReadIntputStatus(17,196,22,vCoils) ; 'Reads inputs 10197 ... 10218 from slave 17

See also: RaiseError , Error Codes .

PresetSingleRegister

mbusrv.PresetSingleRegister(Addr, Register , RegValue, [ vRealType] )

HRESULT PresetSingleRegister([in] short Addr , [in] long Register , [in] VARIANT RegValue , [in,optional] VARIANT vRealType , [out,retval] long* lRetval);

 

Return Value

A error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

Register - Register Address

RegValue - Preset Data

vRealType - inverse the order of registers when reading real number mbREAL_NORMAL(default) (not reversed) mbREAL_REVERSE (reverse order).

Remarks

Presets a single holding register (4x reference). , the first register 40001 is 0, second 40002 is 1 and so on.When the data is a float number you are actually setting 2 registers and with a double number 4 registers are set.

example:

Dim lError As Long

lError = mbusrv.PresetSingleRegister(17,1,3) ; 'preset register 40002 to 03 in slave device 17

See also: RaiseError , Error Codes ,PresetMultipleRegisters

ForceSingleCoil

mbusrv.ForceSingleCoil(Addr, Coil, CoilValue)

HRESULT ForceSingleCoil([in] short Addr , [in] long Coil , [in] VARIANT_BOOL CoilValue , [out,retval] long* plRetVal);

Return Value

A error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

Coil - Coil Address

CoilValue - Force Data - TRUE (ON) , FALSE (OFF).

Remarks

Force a single Coil (0x reference). , the first coil 00001 is 0, second 00002 is 1 and so on.

example:

Dim lError As Long

lError = mbusrv.ForceSingleCoil(17,172,TRUE) ; 'force coil 173 ON in slave device 17

See also: RaiseError , Error Codes ,PresetMultipleRegisters

PresetMultipleRegisters

mbusrv.PresetMultipleRegisters(Addr, DataStart , Quantity,vRegValues,[vRealType])

HRESULT PresetMultipleRegisters([in] short Addr , [in] long DataStart , [in] short Quantity , [in] VARIANT vRegValues , [in,optional] VARIANT vRealType , [out,retval] long* plRetVal);

Return Value

Error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

DataStart - Starting Address

Quantity - number of registers to write

vRegValues - Presets values Data

wRealType - inverse the order of registers when reading real number mbREAL_NORMAL(default) (not reversed) mbREAL_REVERSE (reverse order).

Remarks

Presets values of holding registers (4x references) , the first register 40001 is 0, second 40002 is 1 and so on. When the data is a float number you are actually setting 2 registers and with a double number 4 registers are set.

example:

Dim lError As Long

Dim aRegs() as Long

'preset two registers starting at 40002 to 00 0A and 01 02 hex, in slave device 17.

Redim aRegs(0 To 1)

aRegs(0)=&H000A
aRegs(1)=&H0102

lError = mbusrv.PresetMultipleRegisters(17,1,2,CVar(aRegs))

See also: RaiseError , Error Codes , PresetSingleRegister

 

ForceMultipleCoils

mbusrv.ForceMultipleCoils(Addr, DataStart , Quantity , vCoilValues)

HRESULT ForceMultipleCoils([in] short Addr , [in] long DataStart , [in] short Quantity , [in] VARIANT vCoilValues , [out,retval] long* plRetVal);

Return Value

Error code if the function failed or mbErrOk if succeed.

Parameters

Addr - Slave Address

DataStart - Starting Address

Quantity - number of registers to read

vCoilValues - Force values Data True (ON) , False (OFF).

Remarks

Force values of coils (0x references). , the first coil 00001 is 0, second 00002 is 1 and so on.

example:

Dim lError As Long
Dim abCoils() As Boolean;

'force a series of 2 coils starting at coil 00020 in slave device 17.

Redim abCoils(0 to 2)

abCoils(0)=True 'ON
abCoils(1)=False 'OFF


lError = mbusrv.ForceMultipleCoils(17,19,2,CVar(abCoils))

See also: RaiseError , Error Codes ,ForceSingleCoil

 

TxRxMessage

mbusrv.TxRxMessage(vQuery, QueryLength, vResponse, RespLength, [vNumOfBytesRead])

HRESULT TxRxMessage([in] VARIANT vQuery , [in] short QueryLength , [out] VARIANT* vResponse, [in] short RespLength, [out,optional] VARIANT* vNumOfBytesRead , [out,retval] long* plRetVal);

Return Value

Error code if the function failed or mbErrOk if succeed.

Parameters

vQuery - Query message using RTU- Frame format. (input), this must be a byte array.

QueryLength - Query size. (input)

vResponse - Response from device (output). The response is returned in a byte array.

RespLength - Response Size. (input) . This value must be 0 if you do not know the response size, all bytes the slave sent to master will be in abyResponse buffer.

vNumOfBytesRead-A variable that will be set with the number of bytes read from device.

Remarks

This function allow to send general modbus function to a slave or create new modbus functions implemented in a particular device model.

example:

This example show how to create a new modbus function. Supose we want to implement Modbus function 8 (Diagnostics) for checking communication. The function will be called LoopbackTest, it just send message and verify if the device return the same bytes of the query.

'
'Example how to create new modbus function
'
Function LoopbackTest(nAddr As Integer) As Long

Const QUERY_LENGHT = 6
Const nReplyLength = 6
Dim abyQuery() As Byte
Dim vReply As Variant
Dim nError As Long
Dim iReg As Integer
Dim iRetry As Integer

ReDim abyQuery(0 To QUERY_LENGHT)

'new modbus message see modbus protcol on www.modicon.com
abyQuery(0) = nAddr 'ADDR
abyQuery(1) = 8 'Function
abyQuery(2) = 0 'diag. code=0
abyQuery(3) = 0
abyQuery(4) = &HA5
abyQuery(5) = &H37

Do
nError = m_ModbusServer.TxRxMessage(CVar(abyQuery), QUERY_LENGHT, vReply, nReplyLength) 'send message to device
If (nError = mbErrOk) Then 'interpret response ,
For iReg = 0 To QUERY_LENGHT - 1
If (abyQuery(iReg) <> vReply(iReg)) Then
nError = mbErrInvResp
Exit For
End If
Next iReg


End If

iRetry = iRetry + iRetry

Loop While ((iRetry < m_ModbusServer.Retries) And (mbErrOk <> nError))

LoopbackTest = nError

End Function 'end LoopBackTest

See also: Error Codes