arduino-libraries / ArduinoModbus

244 stars 116 forks source link

Multiple clients to one modbus TCP server #113

Open DriesVandb opened 1 year ago

DriesVandb commented 1 year ago

I have a project where i am trying to connect multiple clients to one Modbus TCP server. However the current library does not support one server having multiple clients, So i tried to set multiple server up one for every clients. This seems to work well. It is not the cleanest code because i have to edit the registers of both servers when the data changes.

Is there a way to connect multiple client to one server?

I am using an STM32F303 on a NUCLEO-BOARD to test.

#include <SPI.h>
#include <Ethernet.h>
#include <ArduinoModbus.h>
#include <Adafruit_MAX31865.h>

// Use software SPI: CS, DI, DO, CLK
Adafruit_MAX31865 thermo = Adafruit_MAX31865(6, A0, A1, A3);
// The value of the Rref resistor. Use 430.0 for PT100 and 4300.0 for PT1000
#define RREF 400.0
// The 'nominal' 0-degrees-C resistance of the sensor
// 100.0 for PT100, 1000.0 for PT1000
#define RNOMINAL 100.0

byte mac[] = {
    0x80, 0x20, 0xE1, 0xEF, 0xFE, 0xED};

// Default to use W5100. Must change to false for W5500, W5100S, for faster SPI clock
#define USE_W5100 false

/**
 * @brief The pins used for the pulse interrupts.
 */
const int interruptPin1 = 0;
const int interruptPin2 = 1;
const int interruptPin3 = 9;
const int interruptPin4 = 4;
const int interruptPin5 = 5;
const int interruptPin6 = A2;
const int interruptPin7 = 7;
const int interruptPin8 = 8;

uint16_t interruptNumber = 0;
unsigned long previousTime = micros();

uint16_t interruptNumber1 = 0;
uint16_t interruptNumber2 = 0;
uint16_t interruptNumber3 = 0;
uint16_t interruptNumber4 = 0;
uint16_t interruptNumber5 = 0;
uint16_t interruptNumber6 = 0;
uint16_t interruptNumber7 = 0;
uint16_t interruptNumber8 = 0;

unsigned long previousTime1 = 0;
unsigned long previousTime2 = 0;
unsigned long previousTime3 = 0;
unsigned long previousTime4 = 0;
unsigned long previousTime5 = 0;
unsigned long previousTime6 = 0;
unsigned long previousTime7 = 0;
unsigned long previousTime8 = 0;

EthernetServer ethServer(502);

ModbusTCPServer modbusTCPServer;

ModbusTCPServer modbusTCPServer1;

EthernetClient client1;
bool client1_connected = false;

EthernetClient client2;
bool client2_connected = false;

/**
 * @brief The maximum number of Modbus TCP clients.
 */
#define MAX_MB_CLIENTS 2

/**
 * @brief This structure represents a Modbus client.
 */
struct DbmaticClient
{
  EthernetClient client = NULL;

  /**
   * @brief The modbus TCP server instance for this client.
   */
  ModbusTCPServer mbServer;

  /**
   * @brief True when the client is connected, false if not.
   */
  bool connected = false;

  int slaveId = 0;
};

void setup()
{
  Serial.begin(115200);

  Ethernet.init(10);
  Serial.println("Initialize Ethernet with DHCP:");
  if (Ethernet.begin(mac) == 0)
  {
    Serial.println("Failed to configure Ethernet using DHCP");
    bool isW5500 = (Ethernet.hardwareStatus() == EthernetW5500);
    Serial.print(F("Ethernet type is "));
    Serial.println(isW5500 ? F("W5500") : F("W5100"));
    if (Ethernet.hardwareStatus() == EthernetNoHardware)
    {
      Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    }
    else if (Ethernet.linkStatus() == LinkOFF)
    {
      Serial.println("Ethernet cable is not connected.");
    }
  }
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());

  ethServer.begin();

  pinMode(interruptPin1, INPUT_PULLUP);
  pinMode(interruptPin2, INPUT_PULLUP);
  pinMode(interruptPin3, INPUT_PULLUP);
  pinMode(interruptPin4, INPUT_PULLUP);
  pinMode(interruptPin5, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  pinMode(interruptPin7, INPUT_PULLUP);
  pinMode(interruptPin8, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(interruptPin1), interrupt1Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin2), interrupt2Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin3), interrupt3Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin4), interrupt4Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin5), interrupt5Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(A2), interrupt6Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin7), interrupt7Callback, FALLING);
  attachInterrupt(digitalPinToInterrupt(interruptPin8), interrupt8Callback, FALLING);

  // start the Modbus TCP server
  if (!modbusTCPServer.begin(1))
  {
    Serial.println("Failed to start Modbus TCP Server!");
    while (1)
      ;
  }

  modbusTCPServer.configureHoldingRegisters(0x00, 100);
  Serial.println("Start Modbus TCP Server!");
  delay(1000);

  if (!modbusTCPServer1.begin(2))
  {
    Serial.println("Failed to start Modbus TCP Server!");
    while (1)
      ;
  }

  modbusTCPServer1.configureHoldingRegisters(0x00, 100);
  Serial.println("Start Modbus TCP Server 1!");

  // for(int i = 0; i < MAX_MB_CLIENTS; ++i) {
  //   setupDbmaticClient(mbClient + i, i);
  // }

  thermo.begin(MAX31865_2WIRE);

  Serial.println("Boot complete");
}

void handle_connect()
{
  EthernetClient client = ethServer.accept();

  if(client) {

    if(!client1_connected) {
        Serial.println("Connecting client 1");
        client1 = client;
        client1_connected = true;
        Serial.print("the connected client1 state: ");
        Serial.println(client1.connected());
        modbusTCPServer.accept(client1);
        Serial.println(client1.connected());
        return;
      } else if (!client2_connected) {
        Serial.println("Connecting client 2");
        client2 = client;
        client2_connected = true;
        Serial.print("the connected client2 state: ");
        Serial.println(client2.connected());
        modbusTCPServer1.accept(client2);
        Serial.println(client2.connected());
        return;
      }

    client.stop();
    Serial.println("Closed new client because al connection slots are in use");
  }
}

void handle_modbus()
{
  if(client1.connected()){
    modbusTCPServer.poll();
  }
  if(client2.connected()){
    Serial.println("Polling for client 2");
    modbusTCPServer1.poll();
  }

  if(!client1.connected() && client1_connected){
    client1_connected = false;
    Serial.println("Client 1 closing the connection");
    client1.stop();
  }
  if(!client2.connected() && client2_connected){
    client2_connected = false;
    Serial.println("Client 2 closing the connection");
    client2.stop();
  }
}

unsigned long last_temp_reading = 0;

void handle_temp()
{
  if (millis() - last_temp_reading <= 1000)
  {
    return;
  }

  uint16_t rtd = thermo.readRTD();
  float ratio = rtd;
  ratio /= 32768;
  // Serial.print("Temperature = ");
  // Serial.println(thermo.temperature(RNOMINAL, RREF));

  // Check and print any faults
  uint8_t fault = thermo.readFault();
  if (fault)
  {
    Serial.print("Fault 0x");
    Serial.println(fault, HEX);
    if (fault & MAX31865_FAULT_HIGHTHRESH)
    {
      Serial.println("RTD High Threshold");
    }
    if (fault & MAX31865_FAULT_LOWTHRESH)
    {
      Serial.println("RTD Low Threshold");
    }
    if (fault & MAX31865_FAULT_REFINLOW)
    {
      Serial.println("REFIN- > 0.85 x Bias");
    }
    if (fault & MAX31865_FAULT_REFINHIGH)
    {
      Serial.println("REFIN- < 0.85 x Bias - FORCE- open");
    }
    if (fault & MAX31865_FAULT_RTDINLOW)
    {
      Serial.println("RTDIN- < 0.85 x Bias - FORCE- open");
    }
    if (fault & MAX31865_FAULT_OVUV)
    {
      Serial.println("Under/Over voltage");
    }
    thermo.clearFault();
  }

  last_temp_reading = millis();
}

void loop()
{
  handle_connect();
  handle_modbus();
  handle_temp();
}

void interrupt1Callback()
{
  interruptNumber1++;
  modbusTCPServer.holdingRegisterWrite(0x00, interruptNumber1);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime1) / 10);
  previousTime1 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x20, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x21, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x20, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x21, pulseTime & 0xFFFF);
}

void interrupt2Callback()
{
  interruptNumber2++;
  modbusTCPServer.holdingRegisterWrite(0x01, interruptNumber2);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime2) / 10);
  previousTime2 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x22, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x23, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x22, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x23, pulseTime & 0xFFFF);
}
void interrupt3Callback()
{
  interruptNumber3++;
  modbusTCPServer.holdingRegisterWrite(0x02, interruptNumber3);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime3) / 10);
  previousTime3 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x24, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x25, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x24, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x25, pulseTime & 0xFFFF);
}
void interrupt4Callback()
{
  interruptNumber4++;
  modbusTCPServer.holdingRegisterWrite(0x03, interruptNumber4);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime4) / 10);
  previousTime4 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x26, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x27, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x26, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x27, pulseTime & 0xFFFF);
}
void interrupt5Callback()
{
  interruptNumber5++;
  modbusTCPServer.holdingRegisterWrite(0x04, interruptNumber5);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime5) / 10);
  previousTime5 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x28, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x29, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x28, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x29, pulseTime & 0xFFFF);
}
void interrupt6Callback()
{
  interruptNumber6++;
  modbusTCPServer.holdingRegisterWrite(0x05, interruptNumber6);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime6) / 10);
  previousTime6 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x2A, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x2B, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x2A, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x2B, pulseTime & 0xFFFF);
}
void interrupt7Callback()
{
  interruptNumber7++;
  modbusTCPServer.holdingRegisterWrite(0x06, interruptNumber7);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime7) / 10);
  previousTime7 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x2C, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x2D, pulseTime & 0xFFFF);
  modbusTCPServer1.holdingRegisterWrite(0x2C, int(pulseTime / 100));
  modbusTCPServer1.holdingRegisterWrite(0x2D, pulseTime & 0xFFFF);
}
void interrupt8Callback()
{
  interruptNumber8++;
  modbusTCPServer.holdingRegisterWrite(0x07, interruptNumber8);
  unsigned long currentTime = micros();
  int pulseTime = int((currentTime - previousTime8) / 10);
  previousTime8 = currentTime;
  modbusTCPServer.holdingRegisterWrite(0x2E, int(pulseTime / 100));
  modbusTCPServer.holdingRegisterWrite(0x2F, pulseTime & 0xFFFF);
}