modbuspp  1.1.40
C++ wrapper for the libmodbus library
server/clock-server/main.cpp
Author
Pascal JEAN, aka epsilonrt
#include <ctime>
#include <iostream>
#include <modbuspp.h>
#include <modbuspp/popl.h>
using namespace std;
using namespace Modbus;
const int SlaveAddress = 10;
Server srv; // instantiates new MODBUS Server
// -----------------------------------------------------------------------------
// Signal trap, triggers during a CTRL+C or if kill is called
void
sighandler (int sig) {
srv.close();
cout << "everything was closed." << endl << "Have a nice day !" << endl;
exit (EXIT_SUCCESS);
}
// -----------------------------------------------------------------------------
int main (int argc, char **argv) {
Net net;
string connection;
string settings;
string mode;
time_t now, before;
tm * t;
// ModBus Data
bool daylight;// daylight saving time, true = summer time
Data<int32_t> gmtoff; // GMT offset, +/- minutes, Big endian order : ABCD
uint16_t mb_time[8]; // date and time
// parsing options from the command line
// watch https://github.com/badaix/popl to understand how we use popl ...
popl::OptionParser CmdLine ("Allowed options");
auto help_option = CmdLine.add<popl::Switch> ("h", "help", "produce help message");
CmdLine.add<popl::Value<string>> ("m", "mode", "mode (rtu or tcp)", "tcp", &mode);
CmdLine.add<popl::Value<string>> ("c", "connection",
"host or serial port when using ModBus protocol\n"
"(e.g. /dev/ttyS1 for RTU, 127.0.0.1 for TCP)",
"127.0.0.1", &connection);
CmdLine.add<popl::Value<string>> ("s", "settings", "connection settings\n"
"(e.g. 38400E1 for RTU, 1502 port for TCP)",
"1502", &settings);
try {
CmdLine.parse (argc, argv);
// print auto-generated help message then exit
if (help_option->count() == 1) {
cout << CmdLine << endl;
exit (EXIT_SUCCESS);
}
if (mode == "rtu") {
net = Rtu;
}
else if (mode == "tcp") {
net = Tcp;
}
else {
cerr << "invalid mode " << mode << ", must be tcp or rtu" << endl;
cerr << CmdLine << endl;
exit (EXIT_FAILURE);
}
cout << "Modbus Time Server" << endl;
// CTRL+C and kill call triggers the trap sighandler()
signal (SIGINT, sighandler);
signal (SIGTERM, sighandler);
cout << "Press CTRL+C to stop... " << endl << endl;
srv.setBackend (net, connection, settings);
srv.setDebug();
BufferedSlave & slv = srv.addSlave (SlaveAddress); // Adding a new slave to the server
cout << "Slave id: " << slv.number() << endl << endl;
if (srv.debug()) {
cout << "Mapping of registers:" << endl
<< "--- Input Registers" << endl
<< "@ Reg. Size Description" << endl
<< "1 16-bit Seconds (0-60), unsigned" << endl
<< "2 16-bit Minutes (0-59), unsigned" << endl
<< "3 16-bit Hours (0-23), unsigned" << endl
<< "4 16-bit Day of the month (1-31), unsigned" << endl
<< "5 16-bit Month (1-12), unsigned" << endl
<< "6 16-bit Year e.g. 2019, unsigned" << endl
<< "7 16-bit Day of the week (0-6, Sunday = 0), unsigned" << endl
<< "8 16-bit Day in the year (1-366, 1 Jan = 1), unsigned" << endl
<< "--- Holding Registers" << endl
<< "@ Reg. Size Description" << endl
<< "1 32-bit number of seconds to add to UTC to get local time, signed" << endl
<< "--- Coils" << endl
<< "@ Reg. Size Description" << endl
<< "1 1-bit Daylight saving time" << endl << endl;
}
/* Mapping of registers :
--- Input Registers
@ Reg. Size Description
1 16-bit Seconds (0-60), unsigned
2 16-bit Minutes (0-59), unsigned
3 16-bit Hours (0-23), unsigned
4 16-bit Day of the month (1-31), unsigned
5 16-bit Month (1-12), unsigned
6 16-bit Year e.g. 2019, unsigned
7 16-bit Day of the week (0-6, Sunday = 0), unsigned
8 16-bit Day in the year (1-366, 1 Jan = 1), unsigned */
slv.setBlock (InputRegister, 8);
/* --- Holding Registers
@ Reg. Size Description
1 32-bit number of seconds to add to UTC to get local time, signed */
slv.setBlock (HoldingRegister, 2);
/* --- Coils
@ Reg. Size Description
1 1-bit Daylight saving time, true = summer time */
slv.setBlock (Coil, 1);
now = before = time (nullptr);
t = localtime (&now);
// init tz_offset and daylight saving time from from localtime
daylight = (t->tm_isdst > 0);
slv.writeCoil (1, daylight);
gmtoff = t->tm_gmtoff;
slv.writeRegister (1, gmtoff);
if (srv.open ()) { // open a connection
cout << "Listening server on " <<
srv.connection() << ":" << srv.settings() << "..." << endl << endl;
do {
now = time (nullptr);
if (now > before) {
before = now;
// update daylight saving time from holding register
slv.readCoil (1, daylight);
// update GMT offset from holding register
slv.readRegister (1, gmtoff);
// calculate the epoch time
now += (daylight ? 3600 : 0) + gmtoff;
t = gmtime (&now);
mb_time[0] = t->tm_sec;
mb_time[1] = t->tm_min;
mb_time[2] = t->tm_hour;
mb_time[3] = t->tm_mday;
mb_time[4] = t->tm_mon + 1;
mb_time[5] = t->tm_year + 1900;
mb_time[6] = t->tm_wday;
mb_time[7] = t->tm_yday + 1;
// update the input registers
slv.writeInputRegisters (1, mb_time, 8);
}
srv.poll (100);
}
while (srv.isOpen());
}
}
catch (std::exception & e) {
cerr << "Error: " << e.what() << endl;
}
catch (...) {
cerr << "Unattended exception !" << endl;
}
return EXIT_FAILURE;
}
/* ========================================================================== */