CModbus Class Members

class CModbus: public CObject

CModbus implements a subset of modbus protocol functions. This class do not send messages to any device , serial port or TCP/IP network , this is done by derived CModbus classes ( CLocalModbus, CRemoteModbus or CAutoModbus). CModbus works as common interface for sending messages to modbus devices independent the kind of network employed.

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.

 

Member functions:

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).

 

ErrorMessage Return string description of an error code.

TxRxMessage pure virtual function that must be implemented in derived classes and actually send message to the network.

Properties:

Retries number of times the message is sent to a slave before the function return an error code.

ThrowException When this property is TRUE, the CModbus class functions throw CModbusException instead of returning an error code.

CModbus::ReadOutputRegisters

WORD ReadOutputRegisters(WORD nAddr ,WORD nDataStart ,WORD nQtd , CWordArray& anRegValues);

WORD ReadOutputRegisters(WORD nAddr ,WORD nDataStart ,WORD nQtd , CShorArray& anRegValues);

WORD ReadOutputRegisters(WORD nAddr,WORD nDataStart , WORD nQtd ,CFloatArray& anRegValues,WORD wRealType = REAL_NORMAL);

WORD ReadOutputRegisters(WORD nAddr,WORD nDataStart ,WORD nQtd , CDblArray& anRegValues,WORD wRealType = REAL_NORMAL);

Return Value

An error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return a error description.

Parameters

nAddr - Slave Address

nDataStart - Starting Address

nQtd - number of registers to read . When reading 1 float number you are actually reading 2 registers and 1 double means 4 registers.

anRegValues - array with registers read.

wRealType - inverse the order of registers when reading real number REAL_NORMAL (not reversed) REAL_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. CWordArray return the registers on WORD (unsigned 16 bit integer) format. CShortArray is a CArray class ( CArray<short,short>) and intended for devices that use signed integers. The last two forms of this function are used for interpret modbus registers as IEEE floating point numbers. 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 REAL_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. It was defined two new array classes CFloatArray class(CArray<float,float>) and CDblArray class (CArray<double,double>) using MFC template class CArray.

example:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
CWordArray anRegs;
CDblArray adRegValues;


anRegs.SetSize(3);
adRegValues.SetSize(1);

wError = pModbus->ReadOutputRegisters(17,107,3,anRegs) ; //Reads 40108 ...40110 registers from slave 17

wError = pModbus->ReadOutputRegisters(17,107,1,adRegValues,REAL_NORMAL) ; //Reads 40108 ...40111 registers from slave 17 and interpret as one double precision floating point number

 

See also: CModbusExcetion , Error Codes .

 

CModbus::ReadInputRegisters

WORD ReadInputRegisters( WORD nAddr, WORD nDataStart ,WORD nQtd ,CWordArray& anRegValues);

WORD ReadInputRegisters( WORD nAddr, WORD nDataStart ,WORD nQtd ,CShortArray& anRegValues);

WORD ReadInputRegisters(WORD nAddr,WORD nDataStart , WORD nQtd ,CFloatArray& anRegValues,WORD wRealType = REAL_NORMAL);

WORD ReadInputRegisters(WORD nAddr,WORD nDataStart ,WORD nQtd , CDblArray& anRegValues,WORD wRealType = REAL_NORMAL);

Return Value

A error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return a error description.

Parameters

nAddr - Slave Address

nDataStart - Starting Address

nQtd - number of registers to read.When reading 1 float number you are actually reading 2 registers and 1 double means 4 registers.

anRegValues - array with registers read.

wRealType - inverse the order of registers when reading real number REAL_NORMAL (not reversed) REAL_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. CWordArray return the registers on WORD (unsigned 16 bit integer) format. CShortArray is template CArray class ( CArray<short,short>) and intended for devices that use signed integers. The last two forms of this function are used to interpret modbus registers as IEEE floating point numbers. 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 REAL_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. It was defined two new array classes CFloatArray class(CArray<float,float>) and CDblArray class (CArray<double,double>) using MFC template class CArray.

example:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
CWordArray anRegs;
CDblArray adRegValues;

anRegs.SetSize(1);
adRegValues.SetSize(1);

wError = pModbus->ReadInputRegisters(17,8,1,anRegs) ; //Reads 30009 register from slave 17

wError = pModbus->ReadInputRegisters(17,8,1,adRegValues) ; //Read 30009...30012 registers from slave 17 and interpret as one double precision floating point number

See also: CModbusExcetion , Error Codes .

 

CModbus::ReadOutputStatus

WORD ReadOutputStatus( WORD nAddr, WORD nDataStart ,WORD nQtd ,CByteArray& anRegValues);

Return Value

A error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return a error description.

Parameters

nAddr - Slave Address

nDataStart - Starting Address

nQtd - number of registers to read

anRegValues - array with registers 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. CByteArray return the coil on BOOL format (TRUE - On , FALSE - Off).

example:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
CByteArray abCoils;

abCoils.SetSize(37);

wError = pModbus->ReadOutputStatus(17,19,37,abCoils) ; //Reads coils 00020 ... 0056 from slave 17

//abCoils(0) is 00020 , abCoils(0) is 00021 ...

See also: CModbusExcetion , Error Codes .

 

CModbus::ReadInputStatus

WORD ReadOutputStatus( WORD nAddr, WORD nDataStart ,WORD nQtd ,CByteArray& anRegValues);

 

Return Value

A error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return a error description.

Parameters

nAddr - Slave Address

nDataStart - Starting Address

nQtd - number of registers to read

anRegValues - array with registers 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. CByteArray return the input on BOOL format (TRUE - On , FALSE - Off).

example:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
CByteArray abCoils;

abCoils.SetSize(22);

wError = pModbus->ReadIntputStatus(17,196,22,abCoils) ; //Reads inputs 10197 ... 10218 from slave 17

//abCoils(0) is 10197 , abCoils(0) is 10198 ...

See also: CModbusExcetion , Error Codes .

CModbus::PresetSingleRegister

WORD PresetSingleRegister(WORD nAddr, WORD nRegister , WORD nRegValue);

WORD PresetSingleRegister(WORD nAddr,WORD nRegister , short int nRegValue);

WORD PresetSingleRegister(WORD nAddr,WORD nRegister ,float fRegValue,WORD wRealType = REAL_NORMAL);

WORD PresetSingleRegister(WORD nAddr,WORD nRegister , double dRegValue,WORD wRealType = REAL_NORMAL);

Return Value

A error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return an error description.

Parameters

nAddr - Slave Address

nRegister - Register Address

nRegValue - Preset Data

wRealType - inverse the order of registers when reading real number REAL_NORMAL (not reversed) REAL_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:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
wError = pModbus->PresetSingleRegister(17,1,3) ; //preset register 40002 to 03 in slave device 17

See also: CModbusExcetion , Error Codes ,PresetMultipleRegisters ,ReadOutputRegisters

CModbus::ForceSingleCoil

WORD ForceSingleCoil(WORD nAddr, WORD nCoil, BOOL bCoilValue)

Return Value

A error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return an error description.

Parameters

nAddr - Slave Address

nCoil - Coil Address

bCoilValue - 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:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
wError = pModbus->ForceSingleCoil(17,172,TRUE) ; //force coil 173 ON in slave device 17

See also: CModbusExcetion , Error Codes ,PresetMultipleRegisters

CModbus::PresetMultipleRegisters

WORD PresetMultipleRegisters(WORD nAddr, WORD nDataStart , WORD nQtd , CWordArray& anRegValues)

WORD PresetMultipleRegisters(WORD nAddr, WORD nDataStart , WORD nQtd , CShortArray& anRegValues);

WORD PresetMultipleRegisters(WORD nAddr, WORD nDataStart ,WORD nQtd , CDblArray& anRegValues,WORD wRealType = REAL_NORMAL);

WORD PresetMultipleRegisters(WORD nAddr, WORD nDataStart ,WORD nQtd , CFloatArray& afRegValues,WORD wRealType = REAL_NORMAL);

Return Value

Error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return an error description.

Parameters

nAddr - Slave Address

nDataStart - Starting Address

nQtd - number of registers to set. When setting 1 float number you are actually setting 2 registers and 1 double means 4 registers.

anRegValues - Presets values Data

wRealType - inverse the order of registers when reading real number REAL_NORMAL (not reversed) REAL_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:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
CWordArray anRegs;

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

anRegs.SetSize(2);

anRegs(0)=0x000A;
anRegs(1)=0x0102;
wError = pModbus->PresetMultipleRegisters(17,2,anRegs) ;

See also: CModbusExcetion , Error Codes ,PresetSingleRegister

 

CModbus::ForceMultipleCoils

WORD CModbus::ForceMultipleCoils(WORD nAddr, WORD nDataStart , WORD nQtd , CByteArray& abCoilValues)

 

Return Value

Error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return an error description.

Parameters

nAddr - Slave Address

nDataStart - Starting Address

nQtd - number of registers to read

abCoilValues - 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:

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
CWordArray anCoils;

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

anCoils.SetSize(2);

anCoils(0)=TRUE; //ON
anCoils(1)=FALSE;//OFF


wError = pModbus->ForceMultipleCoils(17,19,2,anCoils) ;

See also: CModbusExcetion , Error Codes ,ForceSingleCoil

 

CModbus::ErrorMessage

virtual CString ErrorMessage(WORD wErrorCode);

Return Value

String message with error description.

Parameters

wErrorCode - An Error code returned by CModbus class function.

Remarks

The return string is empty if the wErrorCode is not found.

 

example:

 

extern CModbus* pModbus; // this pointer is created by a derived class.

WORD wError;
wError = pModbus->ForceSingleCoil(17,172,TRUE) ; //force coil 173 ON in slave device 17

if (CModbus::ERR_OK!=wError) {

AfxMessageBox(pModbus->ErrorMessage(wError)); //show error message

}

 

See also: CModbusException , Error Codes

 

 

 

CModbus::TxRxMessage

virtual WORD TxRxMessage(CByteArray& abyQuery, WORD wLengthQuery, CByteArray& abyResponse, WORD wLengthResponse, WORD* pwNumOfBytesRead=NULL)=0;

 

Return Value

Error code if the function failed or CModbus::ERR_OK if succeed. Use ErrorMessage to return an error description.

Parameters

abyQuery - Query message using RTU- Frame format. (input)

wLengthQuery - Query size. (input)

abyResponse - Response from device (output)

wLengthResponse - 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.

pwNumOfBytesRead - pointer of WORD variable the 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 in CModbus class. Suppose 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.

WORD CModbus::LoopbackTest(WORD nAddr) {

static const WORD QUERY_LENGHT=(6);
static const WORD nReplyLength=(6);
CByteArray abyQuery;
CByteArray abyReply;
WORD nError;
int iReg;
int iRetry=0;

abyQuery.SetSize(QUERY_LENGHT);
abyReply.SetSize(nReplyLength);
//new modbus message see modbus protocol on www.modicon.com
abyQuery[0]=(BYTE)nAddr; //ADDR
abyQuery[1]=8; //Function
abyQuery[2]=HIBYTE(0); //diag. code=0
abyQuery[3]=LOBYTE(0);
abyQuery[4]=0xA5;
abyQuery[5]=0x37;

do {
nError=TxRxMessage(abyQuery,abyQuery.GetSize(),abyReply,nReplyLength); //send message to device

if (nError==ERR_OK) { //interpret response ,

for (iReg=0;iReg<QUERY_LENGHT;iReg++){
if (abyReply[iReg]!=abyQuery[iReg]) {
nError=ERR_INV_RESP;
break;
}
}

}

iRetry++;

} while ((iRetry<m_wRetries)&&(ERR_OK!=nError));

ThrowModbusException(nError);

return(nError);

}//end LoopBackTest

See also:

CModbus::Retries

WORD Retries() const ;
void Retries(WORD wRetries);

Remarks

Get/Set number of times the message is sent to a slave before the function return an error code. For instance,

if Retries() = 3 , when a messages is sent to a device and occur an error, that message will be sent again more 2 times.

CModbus::ThrowException

BOOL ThrowException() const;
void ThrowException(BOOL bThrow);

Remarks

When this property is TRUE, the CModbus class functions throw CModbusException instead of returning an error code. By Default this property is FALSE.

example

 

extern CModbus* pModbus; // this pointer is created by a derived class.

pModbus->ThrowException(TRUE);

try {

//It's not needed to verify each call for errors

pModbus->ForceSingleCoil(17,172,TRUE) ; //force coil 173 ON in slave device 17
pModbus->PresetSingleRegister(17,1,3) ; //preset register 40002 to 03 in slave device 17

}

catch(CModbusException* pException) {

TCHAR szCause[255];

CString strFormatted;

pException->GetErrorMessage(szCause, 255);
strFormatted = _T("Modbus Function Error-");

strFormatted += szCause; AfxMessageBox(strFormatted);

AfxMessageBox(strFormatted);

pException->Delete();

}

See also: CModbusException