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