Help Needed: Integrating Arduino Modbus TCP Server with C# Modbus TCP Client

Hello everyone,

I'm currently working on a project where I have an Arduino Modbus TCP server and a C# Modbus TCP client. I've managed to get both the server and client code working separately, but I'm having trouble integrating them to establish communication between the two.

Here's a brief overview of what I'm working with:

Arduino Modbus TCP Server:

#include <ArduinoModbus.h>
#include <SPI.h>
#include <Controllino.h>
#include <Ethernet.h>
#include <ArduinoRS485.h>

 byte mac[] = {0xFC, 0x8F, 0xC4, 0x0C, 0x2D, 0x07};
 IPAddress ip(169,254,70,133);
 IPAddress myDns(169,254,70,1);
 IPAddress gateway(169,254,70,1);
 IPAddress subnet(255,255,0,0);
 ModbusTCPServer modbusTCPServer; 
 EthernetServer server(502);

const int baudRate=9600;
const int Inputs=16;
const int Outputs=8;
const uint16_t Input_Pins[Inputs]={A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, 10, 11};    //16 Input-Signale
const uint16_t Output_Pins[Outputs]={CONTROLLINO_D0,CONTROLLINO_D1,CONTROLLINO_D2,CONTROLLINO_D3,CONTROLLINO_D4,CONTROLLINO_D5,CONTROLLINO_D6,CONTROLLINO_D7};   //8 Output-Signale
uint16_t  Inputstate[Inputs];
uint16_t Data[Outputs];


void setup() {
  Ethernet.begin(mac, ip, myDns, gateway, subnet);
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  
  
  server.begin();

  Serial.print("Modbus server address:");
  Serial.println(Ethernet.localIP());
  // start the Modbus TCP server
  if (!modbusTCPServer.begin()) {
    Serial.println("Failed to start Modbus TCP Server!");
    while (1);
  }  
  
  for (int i = 0; i < Inputs; i++) {
    pinMode(Input_Pins[i], INPUT_PULLUP);     //Controllino Inputs set as Input Pullup   
    modbusTCPServer.configureInputRegisters(0x00, Inputs);   
  }

   for (int i = 0; i < Outputs; i++) {
    pinMode(Output_Pins[i], OUTPUT);          //Controllino Outputs set as Output       
    Data[i] = false;
    modbusTCPServer.configureCoils(0x00, Outputs);
  } 

}

void loop() {  
  // put your main code here, to run repeatedly:
  if (Ethernet.linkStatus() == LinkON) {
    Serial.println("Ethernet cable is not connected.");
  }
  EthernetClient client = server.available();
  if (client) {
  Serial.println("new client");
  modbusTCPServer.accept(client);
  while (client.connected()) {
     modbusTCPServer.poll();
     for (int i = 0; i < Inputs; i++) {
      Inputstate[i] = digitalRead(Input_Pins[i]);
      }
      client.write((uint8_t *)Inputstate, sizeof(Inputstate));

    // Empfangen Sie Coils-Daten vom Client und verarbeiten Sie sie
    if (client.available() >= Outputs) {
      client.readBytes((char *)Data, sizeof(Data));
    }

  
 if(Inputstate[9] == LOW && Inputstate[11] == LOW && Inputstate[15] == HIGH)              //Sofortabschaltung && keine Störung(Frequenzumrichter) && Sicherungsautomat(eingesichert)
 {  

    //Ausgabe der Signale

    if(Inputstate[0] == HIGH)         //Signal Fehler Quittieren        
   {
    digitalWrite(Output_Pins[4], HIGH);      //Fehler quittieren
   }
   else
   {
    digitalWrite(Output_Pins[4], LOW); 
   }
   
  }
  delay(1000);
  client.stop();
}

C# Modbus TCP Client:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;



namespace ModbusTCPClient
{
    public partial class Form1 : Form
    {
        
        private int port = 502;
        private EasyModbus.ModbusClient  modbusTCP;
        private string ipAddress = "169.254.70.133";
        
       
        


        public Form1()
        {
            InitializeComponent();
           

            modbusTCP = new EasyModbus.ModbusClient();
            modbusTCP.IPAddress = ipAddress;

            modbusTCP.ConnectedChanged += ModbusTCP_ConnectedChanged;
        }

       

        private void ModbusTCP_ConnectedChanged(object sender)
        {
            buttonReadDiscreteInputs.Enabled = modbusTCP.Connected;
            buttonReadDiscreteCoils.Enabled = modbusTCP.Connected; 
            checkBoxC1.Enabled = modbusTCP.Connected;
            checkBoxC2.Enabled = modbusTCP.Connected;
            checkBoxC3.Enabled = modbusTCP.Connected;
            checkBoxC4.Enabled = modbusTCP.Connected;
            checkBoxC5.Enabled = modbusTCP.Connected;
            checkBoxC6.Enabled = modbusTCP.Connected;
            checkBoxC7.Enabled = modbusTCP.Connected;
            checkBoxC8.Enabled = modbusTCP.Connected;
            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (!modbusTCP.Connected)
            {
                modbusTCP.Connect(textBoxIpAddress.Text, (int)numericUpDownPort.Value);
            }

            var data = modbusTCP.ReadDiscreteInputs(0, 16);

            UpdateDiscreteInputs(data);
        }

        private void UpdateDiscreteInputs(bool[] data)
        {
            for (int i = 1; i < data.Length + 1; i++)
            {
                var name = string.Concat("CheckboxDI", i);


                var control = this.Controls.Find(name, true).First();

                if (control is CheckBox myCheckbox)
                {
                    myCheckbox.Checked = data[i - 1];
                }


            }
        }

        private void UpdateCoils(bool[] data)
        {
            for (int i = 1; i < data.Length + 1; i++)
            {
                var name = string.Concat("CheckboxC", i);


                var control = this.Controls.Find(name, true).First();

                if (control is CheckBox myCheckbox)
                {
                    myCheckbox.Checked = data[i - 1];
                }


            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (!modbusTCP.Connected)
            {
                modbusTCP.Connect(textBoxIpAddress.Text, (int)numericUpDownPort.Value);
            }

            var data = modbusTCP.ReadCoils(0, 8);

            UpdateCoils(data);
        }

        private void checkBoxC_Click(object sender, EventArgs e)
        {
            if (sender is CheckBox chb)
            {
                int number = Convert.ToInt32(chb.Tag);
                
                if (!modbusTCP.Connected)
                {
                    modbusTCP.Connect(textBoxIpAddress.Text, (int)numericUpDownPort.Value);
                }

                modbusTCP.WriteSingleCoil(number - 1, chb.Checked);

            }
        }

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (!IPAddress.TryParse(textBoxIpAddress.Text, out _))
            {
                toolStripStatusLabelMessages.Text = "ip address not valid";
                return;
                
            }

            try
            {
                modbusTCP.Connect(textBoxIpAddress.Text, (int)numericUpDownPort.Value);
            }
            catch (Exception ex)
            {
                toolStripStatusLabelMessages.Text = ex.Message;
            }
        }

       
    }
}

Network Information:

Ethernet-Adapter Ethernet 4:

Verbindungsspezifisches DNS-Suffix: Verbindungslokale IPv6-Adresse . : fe80::4919:e73c:2add:8203%6 IPv4-Adresse (Auto. Konfiguration): 169.254.70.133 Subnetzmaske . . . . . . . . . . : 255.255.0.0 Standardgateway . . . . . . . . . :

Ping Results:
Ping wird ausgeführt für 169.254.70.133 mit 32 Bytes Daten:
Antwort von 169.254.70.133: Bytes=32 Zeit<1ms TTL=128
Antwort von 169.254.70.133: Bytes=32 Zeit<1ms TTL=128
Antwort von 169.254.70.133: Bytes=32 Zeit<1ms TTL=128
Antwort von 169.254.70.133: Bytes=32 Zeit<1ms TTL=128

Ping-Statistik für 169.254.70.133:
Pakete: Gesendet = 4, Empfangen = 4, Verloren = 0
(0% Verlust),
Ca. Zeitangaben in Millisek.:
Minimum = 0ms, Maximum = 0ms, Mittelwert = 0ms

As you can see i can ping the Controllino Maxi Automation, where my Arduino program runs on, but if i test my C# program i keep getting connection timeouts.
I'm not sure if the configuration of my C# Modbus TCP client is correct to communicate with the Arduino Modbus TCP server. Specifically, I'm unsure about how to properly configure the client to read/write data from/to the server. I suspect that the issue might also be related to the network setup or a potential firewall on the company network.

If anyone has experience with integrating Arduino Modbus TCP servers with C# Modbus TCP clients or can offer guidance on how to correctly set up the communication between the two, I would greatly appreciate your help. Any insights, code snippets, suggestions, or resources you could provide would be incredibly valuable.

Thank you in advance for your assistance!

Best regards,
Xiltor

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.