Documentation

Examples

This topic contains the following sections:

Serial Example

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

// 
// @file tinyslave.cs
// 
// A simple Modbus RTU slave program.
// 
// @if NOTICE
// 
// Copyright (c) proconX Pty Ltd. All rights reserved.
// 
// The following source file constitutes example program code and is
// intended merely to illustrate useful programming techniques.  The user
// is responsible for applying the code correctly.
// 
// THIS SOFTWARE IS PROVIDED BY PROCONX AND CONTRIBUTORS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PROCONX OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// @endif
// 


using System;
using FieldTalk.Modbus.Slave;


class MyDatatable: MbusDataTableInterface
{
   private short[] localRegisters = new short[1000];
   private bool[] localCoils = new bool[1000];


   protected override bool readHoldingRegistersTable(Int32 startRef, Int16[] regArr)
   {
      Console.WriteLine("readHoldingRegisters from " + startRef + ", " + regArr.Length + " references");
      // Adjust Modbus reference counting from 1-based to 0-based
      startRef--;
      // Validate range
      if (startRef + regArr.Length > localRegisters.Length)
      {
         return false;
      }
      // Copy registers from local data array to Modbus
      for (int i = 0; i < regArr.Length; i++)
      {
         regArr[i] = localRegisters[startRef + i];
      }
      return true;
   }


   protected override bool writeHoldingRegistersTable(Int32 startRef, Int16[] regArr)
   {
      Console.WriteLine("writeHoldingRegisters from " + startRef + ", " + regArr.Length + " references");
      // Adjust Modbus reference counting from 1-based to 0-based
      startRef--;
      // Validate range
      if (startRef + regArr.Length > localRegisters.Length)
      {
         return false;
      }
      // Copy registers from Modbus to local data block
      for (int i = 0; i < regArr.Length; i++)
      {
         localRegisters[startRef + i] = regArr[i];
      }
      return true;
   }

}


class TcpSlaveApp
{
   private MbusRtuSlaveProtocol mbusServer = new MbusRtuSlaveProtocol();
   private MyDatatable dataTable = new MyDatatable();


   private void startupServer()
   {
      int result;

      result = mbusServer.addDataTable(1, dataTable); // Unit ID is 1
      if (result == BusProtocolErrors.FTALK_SUCCESS)
      {
         result = mbusServer.startupServer("COM1",
                                           19200, // Baudrate
                                           MbusSerialServerBase.SER_DATABITS_8,
                                           MbusSerialServerBase.SER_STOPBITS_1,
                                           MbusSerialServerBase.SER_PARITY_EVEN);
      }
      if (result != BusProtocolErrors.FTALK_SUCCESS)
      {
         Console.WriteLine(BusProtocolErrors.getBusProtocolErrorText(result));
         Environment.Exit(result);
      }
      Console.WriteLine("Modbus server started on serial interface " + mbusServer.portName);
   }


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


   private void runServer()
   {
      int result;

      do
      {
         result = mbusServer.serverLoop();
      } while (!(result != BusProtocolErrors.FTALK_SUCCESS));
      if (result != BusProtocolErrors.FTALK_SUCCESS)
      {
         Console.WriteLine(BusProtocolErrors.getBusProtocolErrorText(result));
      }
   }


   public static void Main()
   {
      TcpSlaveApp app = new TcpSlaveApp();
      app.startupServer();
      app.runServer();
      app.shutdownServer();
   }

}

MODBUS/TCP Example

The following example tcpslave.cpp shows how to implement a small Modbus/TCP slave:

// 
// @file tcpslave.cs
// 
// A simple console based example using FieldTalk in Modbus/TCP slave mode

// @if NOTICE
// 
// Copyright (c) proconX Pty Ltd. All rights reserved.
// 
// The following source file constitutes example program code and is
// intended merely to illustrate useful programming techniques.  The user
// is responsible for applying the code correctly.
// 
// THIS SOFTWARE IS PROVIDED BY PROCONX AND CONTRIBUTORS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PROCONX OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// @endif
// 


using System;
using FieldTalk.Modbus.Slave;

class MyDatatable: MbusDataTableInterface
{
    private short[] localRegisters = new short[1000];
    private bool[] localCoils = new bool[1000];

    protected override bool readHoldingRegistersTable(Int32 startRef, Int16[] regArr)
    {
        MasterInfo? masterInfo = getMasterInfo(); // Retrieve optionally master meta data
        Console.WriteLine("readHoldingRegisters from {0}, {1} references using {2} by slave {3} via {4}", startRef, regArr.Length, masterInfo?.protocol, masterInfo?.slaveAddr, masterInfo?.connection);

        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;
        // Validate range
        if (startRef + regArr.Length > localRegisters.Length)
        {
            return false;
        }
        // Copy registers from local data array to Modbus
        for (int i = 0; i < regArr.Length; i++)
        {
            regArr[i] = localRegisters[startRef + i];
        }
        return true;
    }


#if ENABLE_INPUT_REGS // Set to true if input registers shall be supported
    protected override bool readInputRegistersTable(Int32 startRef, Int16[] regArr)
    {
        Console.WriteLine("readInputRegistersTable from " + startRef + ", " + regArr.Length + " references");
        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;
        // Validate range
        if (startRef + regArr.Length > localRegisters.Length)
        {
            return false;
        }
        // Copy registers from local data array to Modbus
        for (int i = 0; i < regArr.Length; i++)
        {
            regArr[i] = localRegisters[startRef + i];
        }
        return true;
    }
#endif


    protected override bool writeHoldingRegistersTable(Int32 startRef, Int16[] regArr)
    {
        Console.WriteLine("writeHoldingRegisters from " + startRef + ", " + regArr.Length + " references");
        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;
        // Validate range
        if (startRef + regArr.Length > localRegisters.Length)
        {
            return false;
        }
        // Copy registers from Modbus to local data block
        for (int i = 0; i < regArr.Length; i++)
        {
            localRegisters[startRef + i] = regArr[i];
        }
        return true;
    }


    protected override bool readCoilsTable(Int32 startRef, bool[] bitArr)
    {
        Console.WriteLine("readCoilsTable from " + startRef + ", " + bitArr.Length + " references");
        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;
        // Validate range
        if (startRef + bitArr.Length > localCoils.Length)
        {
            return false;
        }
        // Copy registers from local data array to Modbus
        for (int i = 0; i < bitArr.Length; i++)
        {
            bitArr[i] = localCoils[startRef + i];
        }
        return true;
    }


    protected override bool writeCoilsTable(Int32 startRef, bool[] bitArr)
    {
        Console.WriteLine("writeCoilsTable from " + startRef + ", " + bitArr.Length + " references");
        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;
        // Validate range
        if (startRef + bitArr.Length > localCoils.Length)
        {
            return false;
        }
        // Copy registers from Modbus to local data block
        for (int i = 0; i < bitArr.Length; i++)
        {
            localCoils[startRef + i] = bitArr[i];
        }
        return true;
    }


#if ENABLE_ENRON_REGS // Set to true if Enron mode is required   
    private Int32[] enronData = new Int32[4000];

    protected override bool readEnronRegistersTable(int startRef, Int32[] regArr)
    {
        Console.WriteLine("readEnronRegistersTable from " + startRef + ", " + regArr.Length + " references");
        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;

        // 
        // Validate start address range
        // Note: In this example, we have 2000 references at 5000 and 2000 references at 7000.
        // 
        if ((startRef < 5000) || (startRef >= 9000))
            return false;
        else
            startRef -=5000;
        if (startRef + regArr.Length > enronData.Length)
        {
            return false;
        }
        // Copy registers from local data array to Modbus
        for (int i = 0; i < regArr.Length; i++)
        {
            regArr[i] = enronData[startRef + i];
        }
        return true;
    }


    protected override bool writeEnronRegistersTable(Int32 startRef, Int32[] regArr)
    {
        Console.WriteLine("writeEnronRegistersTable from " + startRef + ", " + regArr.Length + " references");
        // Adjust Modbus reference counting from 1-based to 0-based
        startRef--;

        // 
        // Validate start address range
        // Note: In this example, we have 2000 references at 5000 and 2000 references at 7000.
        // 
        if ((startRef < 5000) || (startRef >= 9000))
            return false;
        else
            startRef -= 5000;
        if (startRef + regArr.Length > enronData.Length)
        {
            return false;
        }
        // Copy registers from Modbus to local data block
        for (int i = 0; i < regArr.Length; i++)
        {
            enronData[startRef + i] = regArr[i];
        }
        return true;
    }
#endif



}


class TcpSlaveApp
{
    private MbusTcpSlaveProtocol mbusServer = new MbusTcpSlaveProtocol();
    private MyDatatable dataTable = new MyDatatable();


    private void startupServer()
    {
       int result;

       result = mbusServer.addDataTable(1, dataTable); // Unit ID is 1
       if (result == BusProtocolErrors.FTALK_SUCCESS)
       {
           result = mbusServer.startupServer();
       }
       if (result != BusProtocolErrors.FTALK_SUCCESS)
       {
           Console.WriteLine(BusProtocolErrors.getBusProtocolErrorText(result));
           Environment.Exit(result);
       }
       Console.WriteLine("Modbus server started on TCP interface.");
    }


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


    private void runServer()
    {
       int result;

       do
       {
           result = mbusServer.serverLoop();
       } while (!(result != BusProtocolErrors.FTALK_SUCCESS));
       if (result != BusProtocolErrors.FTALK_SUCCESS)
       {
           Console.WriteLine(BusProtocolErrors.getBusProtocolErrorText(result));
       }
    }


    public static void Main()
    {
        TcpSlaveApp app = new TcpSlaveApp();
        app.startupServer();
        app.runServer();
        app.shutdownServer();
    }

}