ModbusLib is a free, open-source Modbus library written in C++. It implements client and server functions for TCP, RTU and ASCII versions of Modbus Protocol. It has interface for plain C language (implements in cModbus.h header file). Also it has optional wrapper to use with Qt (implements in ModbusQt.h header file).
Library implements such Modbus functions as:
1
(0x01
) -READ_COILS
2
(0x02
) -READ_DISCRETE_INPUTS
3
(0x03
) -READ_HOLDING_REGISTERS
4
(0x04
) -READ_INPUT_REGISTERS
5
(0x05
) -WRITE_SINGLE_COIL
6
(0x06
) -WRITE_SINGLE_REGISTER
7
(0x07
) -READ_EXCEPTION_STATUS
8
(0x08
) -DIAGNOSTICS
(since v0.4)11
(0x0B
) -GET_COMM_EVENT_COUNTER
(since v0.4)12
(0x0C
) -GET_COMM_EVENT_LOG
(since v0.4)15
(0x0F
) -WRITE_MULTIPLE_COILS
16
(0x10
) -WRITE_MULTIPLE_REGISTERS
17
(0x11
) -REPORT_SERVER_ID
(since v0.4)22
(0x16
) -MASK_WRITE_REGISTER
(since v0.3)23
(0x17
) -READ_WRITE_MULTIPLE_REGISTERS
(since v0.3)24
(0x18
) -READ_FIFO_QUEUE
(since v0.4)
Library was written in C++ and it is the main language to use it (also C support included).
To start using this library you must include ModbusClientPort.h
(ModbusClient.h
) or
ModbusServerPort.h
header files (of course after add include path to the compiler).
This header directly or indirectly include Modbus.h
main header file.
Modbus.h
header file contains declarations of main data types, functions and class interfaces
to work with the library.
ModbusClientPort
implements Modbus interface directly and can be used very simple:
#include <ModbusClientPort.h>
//...
void main()
{
Modbus::TcpSettings settings;
settings.host = "someadr.plc";
settings.port = Modbus::STANDARD_TCP_PORT;
settings.timeout = 3000;
ModbusClientPort *port = Modbus::createClientPort(Modbus::TCP, &settings, true);
const uint8_t unit = 1;
const uint16_t offset = 0;
const uint16_t count = 10;
uint16_t values[count];
Modbus::StatusCode status = port->readHoldingRegisters(unit, offset, count, values);
if (Modbus::StatusIsGood(status))
{
// process out array `values` ...
}
else
std::cout << "Error: " << port->lastErrorText() << '\n';
delete port;
}
//...
User don't need to create any connection or open any port, library makes it automatically.
User can use ModbusClient
class to simplify Modbus function's interface (don't need to use
unit
parameter):
#include <ModbusClientPort.h>
#include <ModbusClient.h>
//...
void main()
{
//...
ModbusClientPort *port = Modbus::createClientPort(Modbus::TCP, &settings, true);
ModbusClient c1(1, port);
ModbusClient c2(2, port);
ModbusClient c3(3, port);
Modbus::StatusCode s1, s2, s3;
while(1)
{
s1 = c1.readHoldingRegisters(0, 10, values);
s2 = c2.readHoldingRegisters(0, 10, values);
s3 = c3.readHoldingRegisters(0, 10, values);
Modbus::msleep(1);
}
//...
}
//...
In this example 3 clients with unit address 1, 2, 3 are used.
User don't need to manage its common resource port
. Library make it automatically.
First c1
client owns port
, than when finished resource transferred to c2
and so on.
Unlike client the server do not implement ModbusInterface
directly.
It accepts pointer to ModbusInterface
in its constructor as parameter and transfer all requests
to this interface. So user can define by itself how incoming Modbus-request will be processed:
#include <ModbusServerPort.h>
//...
class MyModbusDevice : public ModbusInterface
{
#define MEM_SIZE 16
uint16_t mem4x[MEM_SIZE];
public:
MyModbusDevice() { memset(mem4x, 0, sizeof(mem4x)); }
uint16_t getValue(uint16_t offset) { return mem4x[offset]; }
void setValue(uint16_t offset, uint16_t value) { mem4x[offset] = value; }
Modbus::StatusCode readHoldingRegisters(uint8_t unit,
uint16_t offset,
uint16_t count,
uint16_t *values) override
{
if (unit != 1)
return Modbus::Status_BadGatewayPathUnavailable;
if ((offset + count) <= MEM_SIZE)
{
memcpy(values, &mem4x[offset], count*sizeof(uint16_t));
return Modbus::Status_Good;
}
return Modbus::Status_BadIllegalDataAddress;
}
};
void main()
{
MyModbusDevice device;
Modbus::TcpSettings settings;
settings.port = Modbus::STANDARD_TCP_PORT;
settings.timeout = 3000;
ModbusServerPort *port = Modbus::createServerPort(&device, Modbus::TCP, &settings, false);
int c = 0;
while (1)
{
port->process();
Modbus::msleep(1);
if (c % 1000 == 0)
setValue(0, getValue(0)+1);
++c;
}
}
//...
In this example MyModbusDevice
ModbusInterface class was created.
It implements only single function: readHoldingRegisters
(0x03
).
All other functions will return Modbus::Status_BadIllegalFunction
by default.
This example creates Modbus TCP server that process connections and increment first 4x register by 1 every second. This example uses non blocking mode.
Library has simplified Qt-like signal/slot mechanism that can use callbacks when some signal is occured. User can connect function(s) or class method(s) to the predefined signal. Callbacks will be called in the order in which they were connected.
For example ModbusClientPort
signal/slot mechanism:
#include <ModbusClientPort.h>
class Printable
{
public:
void printTx(const Modbus::Char *source, const uint8_t* buff, uint16_t size)
{
std::cout << source << " Tx: " << Modbus::bytesToString(buff, size) << '\n';
}
};
void printRx(const Modbus::Char *source, const uint8_t* buff, uint16_t size)
{
std::cout << source << " Rx: " << Modbus::bytesToString(buff, size) << '\n';
}
void main()
{
//...
ModbusClientPort *port = Modbus::createClientPort(Modbus::TCP, &settings, false);
Printable print;
port->connect(&ModbusClientPort::signalTx, &print, &Printable::printTx);
port->connect(&ModbusClientPort::signalRx, printRx);
//...
}
To use the library with pure C language user needs to include only one header: cModbus.h
.
This header includes functions that wraps Modbus interface classes and its methods.
#include <cModbus.h>
//...
void printTx(const Char *source, const uint8_t* buff, uint16_t size)
{
Char s[1000];
printf("%s Tx: %s\n", source, sbytes(buff, size, s, sizeof(s)));
}
void printRx(const Char *source, const uint8_t* buff, uint16_t size)
{
Char s[1000];
printf("%s Rx: %s\n", source, sbytes(buff, size, s, sizeof(s)));
}
void main()
{
TcpSettings settings;
settings.host = "someadr.plc";
settings.port = STANDARD_TCP_PORT;
settings.timeout = 3000;
const uint8_t unit = 1;
cModbusClient client = cCliCreate(unit, TCP, &settings, true);
cModbusClientPort cpo = cCliGetPort(client);
StatusCode s;
cCpoConnectTx(cpo, printTx);
cCpoConnectRx(cpo, printRx);
while(1)
{
s = cReadHoldingRegisters(client, 0, 10, values);
//...
msleep(1);
}
}
//...
When including ModbusQt.h
user can use ModbusLib in convinient way in Qt framework.
It has wrapper functions for Qt library to use it together with Qt core objects:
#include <ModbusQt.h>
-
Build Tools
Previously you need to install c++ compiler kit, git and cmake itself (qt tools if needed). Then set PATH env variable to find compliler, cmake, git etc.
-
Create project directory, move to it and clone repository:
$ cd ~ $ mkdir src $ cd src $ git clone /~https://github.com/serhmarch/ModbusLib.git
-
Create and/or move to directory for build output, e.g.
~/bin/ModbusLib
:$ cd ~ $ mkdir -p bin/ModbusLib $ cd bin/ModbusLib
-
Run cmake to generate project (make) files.
$ cmake -S ~/src/ModbusLib -B .
To make Qt-compatibility (switch off by default for cmake build) you can use next command (e.g. for Windows 64):
>cmake -DMB_QT_ENABLED=ON -DCMAKE_PREFIX_PATH:PATH=C:/Qt/5.15.2/msvc2019_64 -S <path\to\src\ModbusLib> -B .
-
Make binaries (+ debug|release config):
$ cmake --build . $ cmake --build . --config Debug $ cmake --build . --config Release
-
Resulting bin files is located in
./bin
directory.