FieldTalk Modbus® Slave Library
C++ Editions
FOCUS Software Engineering

Main Page | Modules | Class Hierarchy | Class List | Class Members | Related Pages

Examples

A Tiny Slave

The following example tinyslave.cpp shows how to implement a small Modbus RTU slave:

// Platform header
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Include FieldTalk package header
#include "MbusRtuSlaveProtocol.hpp"


/*****************************************************************************
 * Gobal data
 *****************************************************************************/

#if defined(__LINUX__)
   char *portName = "/dev/ttyS0";
#elif defined(__WIN32__) || defined(__CYGWIN__)
   char *portName = "COM1";
#elif defined(__FREEBSD__) || defined(__NETBSD__) || defined(__OPENBSD__)
   char *portName = "/dev/ttyd0";
#elif defined(__QNX__)
   char *portName = "/dev/ser1";
#elif defined(__VXWORKS__)
   char *portName = "/tyCo/0";
#elif defined(__IRIX__)
   char *portName = "/dev/ttyf1";
#elif defined(__SOLARIS__)
   char *portName = "/dev/ttya";
#elif defined(__OSF__)
   char *portName = "/dev/tty00";
#else
#  error Unknown platform, please add an entry for portName
#endif


/*****************************************************************************
 * Modbus data table
 *****************************************************************************/

typedef struct
{
   short actTemp;           // Register 1
   short minTemp;           // Register 2
   long  scanCounter;       // Register 3 and 4
   float setPoint;          // Register 5 and 6
   short statusReg;         // Register 7
   short configType;        // Register 8
} MyDeviceData;

MyDeviceData deviceData;


/*****************************************************************************
 * Data provider
 *****************************************************************************/

class MyDataProvider: public MbusDataTableInterface
{

  public:

   int readHoldingRegistersTable(int startRef, short regArr[], int refCnt)
   {
      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(deviceData) / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(regArr, &((short *) &deviceData)[startRef],
             refCnt * sizeof(short));
      return (1);
   }


   int writeHoldingRegistersTable(int startRef,
                                  const short regArr[],
                                  int refCnt)
   {
      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(deviceData) / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(&((short *) &deviceData)[startRef],
             regArr, refCnt * sizeof(short));
      return (1);
   }

} dataProvider;


/*****************************************************************************
 * Modbus protocol declaration
 *****************************************************************************/

MbusRtuSlaveProtocol mbusServer;


/*****************************************************************************
 * Function implementation
 *****************************************************************************/

void startupServer()
{
   int result;

   result = mbusServer.addDataTable(1, &dataProvider);
   if (result == FTALK_SUCCESS)
      result = mbusServer.startupServer(portName,
                                        9600L, // Baudrate
                                        8,     // Databits
                                        1,     // Stopbits
                                        0);    // Parity
   if (result != FTALK_SUCCESS)
   {
      fprintf(stderr, "Error starting server: %s!\n",
              getBusProtocolErrorText(result));
      exit(EXIT_FAILURE);
   }
}


void shutdownServer()
{
   mbusServer.shutdownServer();
}


void runServer()
{
   int result = FTALK_SUCCESS;

   while (result == FTALK_SUCCESS)
   {
      result = mbusServer.serverLoop();
      if (result != FTALK_SUCCESS)
         fprintf(stderr, "%s!\n", getBusProtocolErrorText(result));
   }
}


int main()
{
   atexit(shutdownServer);
   startupServer();
   runServer();
   return (EXIT_FAILURE);
}

Shared Memory Data Provider example

The following example shows how to implement a Data Provider which serves it's data from shared memory:

class ShmemMbusDataTable: public MbusDataTableInterface
{
  public:

   ShmemMbusDataTable(int table0Size, int table1Size, int table3Size, int table4Size)
   {
      int i;
      int fd;

      for (i = 0; i < 5; i++)
      {
         //
         // Determine table sizes
         //
         switch (i)
         {
           case 0:
              if (table0Size <= 0)
                 break; // No table will be created for this type
              dataTableArr[i].size = table0Size * sizeof(char);
           break;
           case 1:
              if (table1Size <= 0)
                 break; // No table will be created for this type
              dataTableArr[i].size = table1Size * sizeof(char);
           break;
           case 2:
              dataTableArr[i].size = sizeof(SlaveStatusInfo);
           break;
           case 3:
              if (table3Size <= 0)
                 break; // No table will be created for this type
              dataTableArr[i].size = table3Size * sizeof(short);
           break;
           case 4:
              if (table4Size <= 0)
                 break; // No table will be created for this type
              dataTableArr[i].size = table4Size * sizeof(short);
           break;
         }

         //
         // Open shared memory tables
         //
         shm_unlink(dataTableArr[i].name);
         if (dataTableArr[i].size == 0)
            continue;
         fd = shm_open(dataTableArr[i].name, O_CREAT | O_RDWR | O_EXCL, S_IRWXU);
         if (fd < 0)
         {
            perror("Shared memory open failed");
            abort();
         }
         
         //
         // Size it
         //
         if (ftruncate(fd, dataTableArr[i].size) < 0)
         {
            perror("Shared memory ftruncate failed");
            abort();
         }
         
         //
         // Map shared memory into address space
         //
         dataTableArr[i].ptr = (short *) mmap(0, dataTableArr[i].size, 
                                              PROT_READ | PROT_WRITE, 
                                              MAP_SHARED, fd, 0L);
         if (dataTableArr[i].ptr == NULL)
         {
            perror("Shared memory mmap failed");
            abort();
         }
         
         close  (fd);  // No we can close the file descriptor
         memset(dataTableArr[i].ptr, 0, dataTableArr[i].size);
      }

      //
      // Handle special cases where we map table 3 to point to table 4 and
      // table 1 to point to table 0
      //
      if (table3Size == -1) // Map table 3 to table 4
      {
         dataTableArr[3].size = dataTableArr[4].size;
         dataTableArr[3].ptr = dataTableArr[4].ptr;
      }
      if (table1Size == -1) // Map table 1 to table 0
      {
         dataTableArr[1].size = dataTableArr[0].size;
         dataTableArr[1].ptr = dataTableArr[0].ptr;
      }
      
      // Setup pointer to status table
      slaveStatusInfoPtr = (SlaveStatusInfo *) dataTableArr[2].ptr;

   }
   

   ~ShmemMbusDataTable()
   {
      int i;

      for (i = 0; i < 5; i++)
      {
         if (dataTableArr[i].ptr != NULL)
         {
            munmap(dataTableArr[i].ptr, dataTableArr[i].size);
            shm_unlink(dataTableArr[i].name);
         }
      }
   }
   

   void timeOutHandler()
   {
      slaveStatusInfoPtr->timeoutCnt++;
   }


   int readInputDiscretesTable(int startRef,
                               char bitArr[], 
                               int refCnt)
   {
      printf("\nreadInputDiscretesTable: %d, %d\n", startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > (int) (dataTableArr[1].size / sizeof(char)))
         return (0);

      //
      // Copy data
      //
      memcpy(bitArr, &((char *) dataTableArr[1].ptr)[startRef], refCnt * sizeof(char));
      slaveStatusInfoPtr->readDiscretesCnt++;
      return (1);
   }


   int readCoilsTable(int startRef,
                      char bitArr[], 
                      int refCnt)
   {
      printf("\nreadCoilsTable: %d, %d\n", startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > (int) (dataTableArr[0].size / sizeof(char)))
         return (0);

      //
      // Copy data
      //
      memcpy(bitArr, &((char *) dataTableArr[0].ptr)[startRef], refCnt * sizeof(char));
      slaveStatusInfoPtr->readDiscretesCnt++;
      return (1);
   }

   
   int writeCoilsTable(int startRef,
                       const char bitArr[],
                       int refCnt)
   {
      printf("\nriteCoilsTable: %d, %d\n", startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > (int) (dataTableArr[0].size / sizeof(char)))
         return (0);

      //
      // Copy data
      //
      memcpy(&((char *) dataTableArr[0].ptr)[startRef], bitArr, refCnt * sizeof(char));
      slaveStatusInfoPtr->writeDiscretesCnt++;
      return (1);
   }
   

   int readInputRegistersTable(int startRef,
                               short regArr[],
                               int refCnt)
   {
      printf("\nreadInputRegistersTable: %d, %d\n", startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > (int) (dataTableArr[3].size / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(regArr, &((short *) dataTableArr[3].ptr)[startRef], refCnt * sizeof(short));
      slaveStatusInfoPtr->readRegistersCnt++;
      return (1);
   }


   int readHoldingRegistersTable(int startRef,
                                 short regArr[],
                                 int refCnt)
   {
      printf("\nreadHoldingRegistersTable: %d, %d\n", startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > (int) (dataTableArr[4].size / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(regArr, &((short *) dataTableArr[4].ptr)[startRef], refCnt * sizeof(short));
      slaveStatusInfoPtr->readRegistersCnt++;
      return (1);
   }


   int writeHoldingRegistersTable(int startRef,
                                  const short regArr[],
                                  int refCnt)
   {
      printf("\nwriteHoldingRegistersTable: %d, %d\n", startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > (int) (dataTableArr[4].size / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(&((short *) dataTableArr[4].ptr)[startRef], regArr, refCnt * sizeof(short));
      slaveStatusInfoPtr->writeRegistersCnt++;
      return (1);
   }


  private:

   SlaveStatusInfo *slaveStatusInfoPtr;

};

Diagnostic Slave

The following more complex example diagslave.cpp shows how to use the protocol stack in a context where the user can select the protocol type (TCP, RTU and ASCII) and other parameters. Diagslave is a slave simulator and test tool.

// Platform header
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Include FieldTalk package header
#include "MbusRtuSlaveProtocol.hpp"
#include "MbusAsciiSlaveProtocol.hpp"
#include "MbusTcpSlaveProtocol.hpp"
#include "DiagnosticDataTable.hpp"

#ifdef _WIN32
#  include "getopt.h"
#else
#  include <unistd.h>
#endif


/*****************************************************************************
 * String constants
 *****************************************************************************/

const char versionStr[]= "$Revision: 1.14 $";
const char progName[] = "diagslave";
const char bannerStr[] =
"\n"
"%s - FieldTalk(tm) Modbus(R) Diagnostic Slave\n"
"Copyright (c) 2002-2006 FOCUS Software Engineering Pty Ltd\n"
"Visit http://www.modbusdriver.com for Modbus libraries and tools.\n"
"\n";

const char usageStr[] =
"%s [options] [serialport]\n"
"Arguments: \n"
"serialport    Serial port when using Modbus ASCII or Modbus RTU protocol \n"
"              COM1, COM2 ...                on Windows \n"
"              /dev/ttyS0, /dev/ttyS1 ...    on Linux \n"
"              /dev/ser1, /dev/ser2 ...      on QNX \n"
"General options:\n"
"-m ascii      Modbus ASCII protocol\n"
"-m rtu        Modbus RTU protocol (default)\n"
"-m tcp        MODBUS/TCP protocol\n"
"-t #          Master poll time-out in ms (0-100000, 3000 is default)\n"
"-a #          Slave address (1-255 for RTU/ASCII, 0-255 for TCP)\n"
"Options for MODBUS/TCP:\n"
"-p #          TCP port number (502 is default)\n"
"Options for Modbus ASCII and Modbus RTU:\n"
"-b #          Baudrate (e.g. 9600, 19200, ...) (9600 is default)\n"
"-d #          Databits (7 or 8 for ASCII protocol, 8 for RTU)\n"
"-s #          Stopbits (1 or 2, 1 is default)\n"
"-p none       No parity\n"
"-p even       Even parity (default)\n"
"-p odd        Odd parity\n"
"";


/*****************************************************************************
 * Enums
 *****************************************************************************/

enum
{
   RTU,   
   ASCII, 
   TCP    
};


/*****************************************************************************
 * Gobal configuration data
 *****************************************************************************/

int address = -1;
long timeout = 3000;
long baudRate = 9600;
int dataBits = 8;
int stopBits = 1;
int parity = MbusSerialSlaveProtocol::SER_PARITY_EVEN;
int protocol = RTU;
char *portName = NULL;
int port = 502;


/*****************************************************************************
 * Protocol and data table
 *****************************************************************************/

DiagnosticMbusDataTable *dataTablePtrArr[256];
MbusSlaveServer *mbusServerPtr = NULL;


/*****************************************************************************
 * Function implementation
 *****************************************************************************/

void printUsage()
{
   printf("Usage: ");
   printf(usageStr, progName);
   exit(EXIT_SUCCESS);
}


void printVersion()
{
   printf(bannerStr, progName);
   printf("Version: %s using FieldTalk package version %s\n",
          versionStr, MbusSlaveServer::getPackageVersion());
}


void printConfig()
{
   printf(bannerStr, progName);
   printf("Protocol configuration: ");
   switch (protocol)
   {
      case RTU:
         printf("Modbus RTU\n");
      break;
      case ASCII:
         printf("Modbus ASCII\n");
      break;
      case TCP:
         printf("MODBUS/TCP\n");
      break;
      default:
         printf("unknown\n");
      break;
   }
   printf("Slave configuration: ");
   printf("Address = %d, ", address);
   printf("Master Time-out = %ld\n", timeout);
   if (protocol == TCP)
   {
      printf("TCP configuration: ");
      printf("Port = %d\n", port);
   }
   else
   {
      printf("Serial port configuration: ");
      printf("%s, ", portName);
      printf("%ld, ", baudRate);
      printf("%d, ", dataBits);
      printf("%d, ", stopBits);
      switch (parity)
      {
         case MbusSerialSlaveProtocol::SER_PARITY_NONE:
            printf("none\n");
         break;
         case MbusSerialSlaveProtocol::SER_PARITY_EVEN:
            printf("even\n");
         break;
         case MbusSerialSlaveProtocol::SER_PARITY_ODD:
            printf("odd\n");
         break;
         default:
            printf("unknown\n");
         break;
      }
   }
   printf("\n");
}


void exitBadOption(const char *const text)
{
   fprintf(stderr, "%s: %s! Try -h for help.\n", progName, text);
   exit(EXIT_FAILURE);
}


void scanOptions(int argc, char **argv)
{
   int c;

   // Check for --version option
   for (c = 1; c < argc; c++)
   {
      if (strcmp (argv[c], "--version") == 0)
      {
         printVersion();
         exit(EXIT_SUCCESS);
      }
   }

   // Check for --help option
   for (c = 1; c < argc; c++)
   {
      if (strcmp (argv[c], "--help") == 0)
         printUsage();
   }

   opterr = 0; // Disable getopt's error messages
   for(;;)
   {
      c = getopt(argc, argv, "ha:b:d:s:p:m:");
      if (c == -1)
         break;

      switch (c)
      {
         case 'm':
            if (strcmp(optarg, "tcp") == 0)
            {
               protocol = TCP;
            }
            else
               if (strcmp(optarg, "rtu") == 0)
               {
                  protocol = RTU;
               }
               else
                  if (strcmp(optarg, "ascii") == 0)
                  {
                     protocol = ASCII;
                  }
                  else
                  {
                     exitBadOption("Invalid protocol parameter");
                  }
         break;
         case 'a':
            address = strtol(optarg, NULL, 0);
            if ((address < -1) || (address > 255))
               exitBadOption("Invalid address parameter");
         break;
         case 't':
            timeout = strtol(optarg, NULL, 0);
            if ((timeout < 0) || (timeout > 100000))
               exitBadOption("Invalid time-out parameter");
         break;
         case 'b':
            baudRate = strtol(optarg, NULL, 0);
            if (baudRate == 0)
               exitBadOption("Invalid baudrate parameter");
         break;
         case 'd':
            dataBits = (int) strtol(optarg, NULL, 0);
            if ((dataBits != 7) || (dataBits != 8))
               exitBadOption("Invalid databits parameter");
         break;
         case 's':
            stopBits = (int) strtol(optarg, NULL, 0);
            if ((stopBits != 1) || (stopBits != 2))
               exitBadOption("Invalid stopbits parameter");
         break;
         case 'p':
            if (strcmp(optarg, "none") == 0)
            {
               parity = MbusSerialSlaveProtocol::SER_PARITY_NONE;
            }
            else
               if (strcmp(optarg, "odd") == 0)
               {
                  parity = MbusSerialSlaveProtocol::SER_PARITY_ODD;
               }
               else
                  if (strcmp(optarg, "even") == 0)
                  {
                     parity = MbusSerialSlaveProtocol::SER_PARITY_EVEN;
                  }
                  else
                  {
                     port = strtol(optarg, NULL, 0);
                     if ((port <= 0) || (port > 0xFFFF))
                        exitBadOption("Invalid parity or port parameter");
                  }
         break;
         case 'h':
            printUsage();
         break;
         default:
            exitBadOption("Unrecognized option or missing option parameter");
         break;
      }
   }

   if (protocol == TCP)
   {
      if ((argc - optind) != 0)
         exitBadOption("Invalid number of parameters");
   }
   else
   {
      if ((argc - optind) != 1)
         exitBadOption("Invalid number of parameters");
      else
         portName = argv[optind];
   }
}




int validateMasterIpAddr(char* masterIpAddrSz)
{
   printf("\nvalidateMasterIpAddr: accepting connection from %s\n",
          masterIpAddrSz);
   return (1);
}


void startupServer()
{
   int i;
   int result = -1;

   switch (protocol)
   {
      case RTU:
         mbusServerPtr = new MbusRtuSlaveProtocol();
         if (address == -1)
         {
            for (i = 1; i < 255; i++)
            mbusServerPtr->addDataTable(i, dataTablePtrArr[i]);
         }
         else
            mbusServerPtr->addDataTable(address, dataTablePtrArr[address]);
         mbusServerPtr->setTimeout(timeout);
         result = ((MbusRtuSlaveProtocol *) mbusServerPtr)->startupServer(
                    portName, baudRate, dataBits, stopBits, parity);
      break;
      case ASCII:
         mbusServerPtr = new MbusAsciiSlaveProtocol();
         if (address == -1)
         {
            for (i = 1; i < 255; i++)
            mbusServerPtr->addDataTable(i, dataTablePtrArr[i]);
         }
         else
            mbusServerPtr->addDataTable(address, dataTablePtrArr[address]);
         mbusServerPtr->setTimeout(timeout);
         result = ((MbusAsciiSlaveProtocol *) mbusServerPtr)->startupServer(
                   portName, baudRate, dataBits, stopBits, parity);
      break;
      case TCP:
         mbusServerPtr = new MbusTcpSlaveProtocol();
         if (address == -1)
         {
            for (i = 0; i < 255; i++) // Note: TCP support slave addres of 0
               mbusServerPtr->addDataTable(i, dataTablePtrArr[i]);
         }
         else
            mbusServerPtr->addDataTable(address, dataTablePtrArr[address]);
         mbusServerPtr->setTimeout(timeout);
         ((MbusTcpSlaveProtocol *) mbusServerPtr)->installIpAddrValidationCallBack(validateMasterIpAddr);
         ((MbusTcpSlaveProtocol *) mbusServerPtr)->setPort((unsigned short) port);
         result = ((MbusTcpSlaveProtocol *) mbusServerPtr)->startupServer();
      break;
   }
   switch (result)
   {
      case FTALK_SUCCESS:
         printf("Server started up successfully.\n");
      break;
      case FTALK_ILLEGAL_ARGUMENT_ERROR:
         fprintf(stderr, "Configuration setting not supported!\n");
         exit(EXIT_FAILURE);
      break;
      default:
         fprintf(stderr, "%s!\n", getBusProtocolErrorText(result));
         exit(EXIT_FAILURE);
      break;
   }
}


void shutdownServer()
{
   printf("Shutting down server.\n");
   delete mbusServerPtr;
}


void runServer()
{
   int result = FTALK_SUCCESS;

   printf("Listening to network (Ctrl-C to stop)\n");
   while (result == FTALK_SUCCESS)
   {
      result = mbusServerPtr->serverLoop();
      if (result != FTALK_SUCCESS)
         fprintf(stderr, "%s!\n", getBusProtocolErrorText(result));\
      else
      {
         printf(".");
         fflush(stdout);
      }
   }
}


int main(int argc, char **argv)
{
   int i;

   // Construct data tables
   for (i = 0; i < 255; i++)
   {
      dataTablePtrArr[i] = new DiagnosticMbusDataTable(i);
   }

   scanOptions(argc, argv);
   printConfig();
   atexit(shutdownServer);
   startupServer();
   runServer();
   return (EXIT_FAILURE);
}

Diagslave uses the following diagnostic data table as Data Provider:

#ifndef _DIAGNOSTICDATATABLE_H_INCLUDED
#define _DIAGNOSTICDATATABLE_H_INCLUDED


// Platform header
#include <stdio.h>
#include <string.h>

// Package header
#include "MbusDataTableInterface.hpp"


/*****************************************************************************
 * DiagnosticMbusDataTable class declaration
 *****************************************************************************/

class DiagnosticMbusDataTable: public MbusDataTableInterface
{

public:

   DiagnosticMbusDataTable(int slaveAddr)
   {
      this->slaveAddr = slaveAddr;
      memset(regData, 0, sizeof(regData));
      memset(bitData, 0, sizeof(bitData));
   }


   ~DiagnosticMbusDataTable()
   {
   }


   char readExceptionStatus()
   {
      printf("\rSlave %3d: readExceptionStatus\n", slaveAddr);
      return (0x55);
   }


   int readInputDiscretesTable(int startRef,
                               char bitArr[],
                               int refCnt)
   {
      printf("\rSlave %3d: readInputDiscretes from %d, %d references\n",
             slaveAddr, startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(bitData) / sizeof(char)))
         return (0);

      //
      // Copy data
      //
      memcpy(bitArr, &bitData[startRef], refCnt * sizeof(char));
      return (1);
   }


   int readCoilsTable(int startRef,
                      char bitArr[],
                      int refCnt)
   {
      printf("\rSlave %3d: readCoils from %d, %d references\n",
             slaveAddr, startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(bitData) / sizeof(char)))
         return (0);

      //
      // Copy data
      //
      memcpy(bitArr, &bitData[startRef], refCnt * sizeof(char));
      return (1);
   }


   int writeCoilsTable(int startRef,
                       const char bitArr[],
                       int refCnt)
   {
      printf("\rSlave %3d: writeCoils from %d, %d references\n",
             slaveAddr, startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(bitData) / sizeof(char)))
         return (0);

      //
      // Copy data
      //
      memcpy(&bitData[startRef], bitArr, refCnt * sizeof(char));
      return (1);
   }


   int readInputRegistersTable(int startRef,
                               short regArr[],
                               int refCnt)
   {
      printf("\rSlave %3d: readInputRegisters from %d, %d references\n",
             slaveAddr, startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(regData) / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(regArr, &regData[startRef], refCnt * sizeof(short));
      return (1);
   }


   int readHoldingRegistersTable(int startRef,
                                 short regArr[],
                                 int refCnt)
   {
      printf("\rSlave %3d: readHoldingRegisters from %d, %d references\n",
             slaveAddr, startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(regData) / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(regArr, &regData[startRef], refCnt * sizeof(short));
      return (1);
   }


   int writeHoldingRegistersTable(int startRef,
                                  const short regArr[],
                                  int refCnt)
   {
      printf("\rSlave %3d: writeHoldingRegisters from %d, %d references\n",
             slaveAddr, startRef, refCnt);

      // Adjust Modbus reference counting
      startRef--;

      //
      // Validate range
      //
      if (startRef + refCnt > int(sizeof(regData) / sizeof(short)))
         return (0);

      //
      // Copy data
      //
      memcpy(&regData[startRef], regArr, refCnt * sizeof(short));
      return (1);
   }


   int validateMasterIpAddr(char* masterIpAddrSz)
   {
      printf("\nvalidateMasterIpAddr: accepting connection from %s\n",
             masterIpAddrSz);
      return (1);
   }


  private:

   int slaveAddr;
   short regData[0x10000];
   char bitData[2000];

};


#endif // ifdef ..._H_INCLUDED