Modbus TCP between Arduino and S7-1200

Hi everyone, I am using an Arduino R4 WIFI as Modbus Slave and S7-1200 PLC as Modbus Master.
So my problem is not to get the communication working. When starting up the PLC and the Arduino the Modbus communication works fine for some time however then it would just freeze up for some reason. Sometimes it would be good for up to 3 days and then just stop. The only way to restart it is to do a hard reset of the PLC.

Below is my code. Hopefully someone can see something in the code that would cause this as I am at my wits end with this.
Let me know if you need to see the TIA portal code.

#include <Modbus.h>
#include <ModbusIP.h>
#include <Ethernet.h>
#include "arduino_secrets.h"
#include "thingProperties.h"
#include "SPI.h"

int setpoint;
const int setPoint_IR = 100;
const int slider_mb = 101;
const int spare_001 = 102;
const int switch_001 = 103;
const int switch_002 = 104;
const int switch_003 = 105;
const int switch_004 = 106;
const int switch_005 = 107;
const int hold_001 = 108;
const int hold_002 = 109;
const int hold_003 = 110;
const int hold_004 = 111;
const int hold_005 = 112;
const int read_001 = 113;
const int read_002 = 114;
const int read_003 = 115;
const int read_004 = 116;
const int read_005 = 117;
ModbusIP mb;

const int analogInPin = A0;
const int flowSensorPin = 2;
const int flowSensorPin2 = 3;
const int analogOutPin = 9;
const int alarmTriggerPin = 4;  // Digital pin for alarm trigger input
const int alarmSetPin = 5; //Digital pin for alarm se/unset input




int sensorValue = 0;
int outputValue = 0;
bool alertSent = false;
unsigned long lastReadTime = 0;
int alarmsetInt = alarmset ? 1:0;
int lightOnInt = lightOn ? 1:0;
volatile unsigned long pulseCount = 0;
volatile unsigned long pulseCount2 = 0;
unsigned long oldTime = 0;
float calibrationFactor = 5.5;
float flowRate = 0.0;
float flowRate2 = 0.0;
unsigned long lastModbusResetTime = 0;
const unsigned long modbusResetInterval = 3600000; // 60 mins in milliseconds
// Define MAC and IP globally for reuse in reset function
byte mac[] = {0xDE, 0xAD,0xBE,0xEF,0xFE,0xED};
byte ip[] {192,168,3,120};


const unsigned long readInterval = 500; // Read every 500ms
int lastAlarmSetState = HIGH;// Store the last alarm set input state (HIGH because of pull-up)
int lastAlarmState = HIGH;  // Store the last alarm input state (HIGH because of pull-up)

// Variables for the burglar alarm delay
unsigned long alarmTriggerTime = 0;
const unsigned long alarmDelay = 5000; // 5 seconds in milliseconds
bool alarmTriggering = false;

unsigned long highFlowStartTime = 0;
bool highFlowActive = false;
const unsigned long highFlowThresholdTime = 1800000; // 30 minutes in milliseconds
const float highFlowThreshold = 5.0;

unsigned long burglarAlarmBlinkTimer = 0;
const unsigned long burglarAlarmBlinkInterval = 30000;


void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500); 

  // Setup the alarm trigger pin as input with pullup
  pinMode(alarmTriggerPin, INPUT_PULLUP);
  pinMode(alarmSetPin, INPUT_PULLUP);

  pinMode(flowSensorPin, INPUT);
  pinMode(flowSensorPin2, INPUT);

  attachInterrupt(digitalPinToInterrupt(flowSensorPin),pulseCounter, FALLING);
  attachInterrupt(digitalPinToInterrupt(flowSensorPin2),pulseCounter2, FALLING);

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  // Enter mac and ip data hier (moenie vergeet nie!)

  // byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
  // byte ip[] = { 192, 168, 3, 120 };
  mb.config(mac, ip);

  mb.addHreg(setPoint_IR);
  mb.addHreg(slider_mb);
  mb.addHreg(spare_001);
  mb.addHreg(switch_001);
  mb.addHreg(switch_002);
  mb.addHreg(switch_003);
  mb.addHreg(switch_004);
  mb.addHreg(switch_005);
  mb.addHreg(hold_001);
  mb.addHreg(hold_002);
  mb.addHreg(hold_003);
  mb.addHreg(hold_004);
  mb.addHreg(hold_005);
  mb.addHreg(read_001);
  mb.addHreg(read_002);
  mb.addHreg(read_003);
  mb.addHreg(read_004);
  mb.addHreg(read_005);
    
  
  
}

void pulseCounter() {pulseCount++; }
void pulseCounter2() {pulseCount2++;}

void loop() {

alarmsetInt = alarmset ? 1:0;
 
  mb.task();
  setpoint = analogRead(A0);
  mb.Hreg(setPoint_IR, threshold_value);
  mb.Hreg(slider_mb,test_value_001);
  mb.Hreg(switch_001,alarmsetInt);
  mb.Hreg(switch_002,lightOnInt);
  mb.Hreg(hold_002, flowRate);
  mb.Hreg(hold_003, flowRate2);
  battery_voltage = mb.Hreg(read_001);
  total_water_per_day = mb.Hreg(read_002);
  total_water_per_week = mb.Hreg(read_003);
  comms_count_01 = mb.Hreg(read_004);
 
  if ((millis() - oldTime) > 1000) {
    detachInterrupt(digitalPinToInterrupt(flowSensorPin));
    detachInterrupt(digitalPinToInterrupt(flowSensorPin2));

    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;
    water_flow_rate = flowRate;
    flowRate2 = ((1000.0/ (millis() - oldTime)) * pulseCount2) / calibrationFactor;
    oldTime = millis();
    water_flow_02 = flowRate2;
    pulseCount = 0;
    pulseCount2 = 0;

     attachInterrupt(digitalPinToInterrupt(flowSensorPin), pulseCounter, FALLING) ;
    attachInterrupt (digitalPinToInterrupt(flowSensorPin2), pulseCounter2, FALLING);

    // Check for high water flow condition
    if (flowRate > highFlowThreshold) {
      if (!highFlowActive){
        // First time flow exceeds threshold
        highFlowActive = true;
        highFlowStartTime = millis();
      }
      else if (millis() - highFlowStartTime > highFlowThresholdTime){ 
      // Flow has been high for more than 30 minutes
        water_use_high = true;
      
      }
    }
    else {
      // Reset when flow drops below threshold
      highFlowActive = false;
      water_use_high = false;
    }

    // Burglar alarm cycling logic

    if (alarmactivated) {
      unsigned long currentMillis = millis();
      if (currentMillis - burglarAlarmBlinkTimer >= burglarAlarmBlinkInterval){
        burglarAlarmBlinkTimer = currentMillis;
        burglaralarm = !burglaralarm; // Toggle the alarm state
      }
    }
  


  
// DisplaySetPointValues();
delay(200);
  }


  
  ArduinoCloud.update();
  unsigned long currentMillis = millis();
  
  // Read digital alarm input (LOW when triggered because of pull-up)
  int alarmState = digitalRead(alarmTriggerPin);
  int alarmSetState = digitalRead(alarmSetPin);
  
  // Burglar alarm logic - detect when pin is held LOW for more than 5 seconds
  if (alarmState == LOW) { // Input is active (LOW with pull-up)
    if (!alarmTriggering) {
      // First time we detect the alarm is active
      alarmTriggering = true;
      alarmTriggerTime = currentMillis;
      Serial.println("Alarm input activated, waiting for 5 seconds to confirm...");
    } else if (currentMillis - alarmTriggerTime >= alarmDelay) {
      // Input has been active for more than 5 seconds
      if (!burglaralarm) {
        burglaralarm = true;
        alarmactivated = true;
        Serial.println("BURGLAR ALARM ACTIVATED! Input held for 5+ seconds");
      }
    }
  } else {
    // Input is inactive (HIGH), reset the trigger state
    if (alarmTriggering) {
      alarmTriggering = false;
      burglaralarm = false;
      alarmactivated = false;
      Serial.println("Alarm input deactivated before timeout");
    }
  }
  
  if (currentMillis - lastReadTime >= readInterval) {
    lastReadTime = currentMillis;
    
    // Read sensor value
    sensorValue = analogRead(analogInPin);
    variable_Input_001 = sensorValue;
    
    Serial.print("Sensor = ");
    Serial.println(sensorValue);
    
    // Check if value exceeds threshold and email alert hasn't been sent yet
    if (sensorValue >= threshold_value && !alertSent) {
      alert_email = true;  // This will trigger the email notification
      alertSent = true;    // Prevent multiple alerts
      Serial.println("Alert triggered!");
    }
    
    // Reset the alert flag if value goes below threshold
    if (sensorValue < threshold_value && alertSent) {
      alert_email = false;
      alertSent = false;
      Serial.println("Alert reset");
    }
    // If alarm set state changed
    if (alarmSetState != lastAlarmSetState){
      lastAlarmSetState = alarmSetState;

      if (alarmSetState== LOW) { // Input is LOW when button is pressed (with Pull-up)
      alarm_is_set = true;
      Serial.println("Alarm system is now SET");
    } else {
      alarm_is_set = false;
      burglaralarm = false;
      alarmactivated = false;
      Serial.println ("Alarm system is now UNSET");
    }
    }
    
    // If alarm state changed
    if (alarmState != lastAlarmState) {
      lastAlarmState = alarmState;
      
      if (alarmState == LOW) {  // Input is LOW when button is pressed (with pull-up)
        aLARM = true;
        Serial.println("aLARM triggered by digital input!");
      } else {
        aLARM = false;
        Serial.println("aLARM reset by digital input");
      }
      // Check if time to reset Modbus connection
      unsigned long currentMillis = millis(); 
      if (currentMillis - lastModbusResetTime >= modbusResetInterval){
       Serial.println("Resetting Modbusconnection...");
       mb.config(mac, ip); // Reinitialize the connection
       lastModbusResetTime = currentMillis;
       Serial.println("Modbus connection reset complete");
      
    }
  }
}


// void DisplaySetPointValues() {
    // String Printstr;
   //  Printstr = "SetPoint Value = " + String(setpoint);
    // Serial.println(Printstr);
  }



// Existing cloud callback functions
void onTemperature001Change() {
  // Add your code here to act upon Temperature001 change
}

void onVariableInput001Change() {
  // This function will be called when the input value changes
}

void onThresholdValueChange() {
  // This function will be called when the threshold value is updated from the cloud
  Serial.print("New threshold set: ");
  Serial.println(threshold_value);
}

void onAlertEmailChange() {
  // This will be automatically called when the email alert variable changes
  // You don't need to add code here as the cloud handles the email sending
}

void onAlarmIsSetChange() {
  // Reset burglar alarm when alarm is unset
  if (!alarm_is_set){
    burglaralarm = false;
    alarmactivated = false;
  }
}

void onALARMChange() {
  // Add your code here to act upon ALARM change
}

void onAlarmsetChange() {
  alarmsetInt = alarmset ? 1:0;
  // Add your code here to act upon Alarmset change
}

/*
  Since Burglaralarm is READ_WRITE variable, onBurglaralarmChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onBurglaralarmChange() {
  // Add your code here to act upon Burglaralarm change
  Serial.print("Burglar alarm state changed to: ");
  Serial.println(burglaralarm);
}
/*
  Since ResetAll is READ_WRITE variable, onResetAllChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onResetAllChange()  {
  // Add your code here to act upon ResetAll change
  if (reset_all) {
    alarmactivated = false;
    burglaralarm = false;
  Serial.println("Alarm state reset by ResetAll command");
    reset_all = false; // Auto-reset the ResetAll variable
    }
}
/*
  Since Alarmactivated is READ_WRITE variable, onAlarmactivatedChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onAlarmactivatedChange()  {
  // Add your code here to act upon Alarmactivated change
}
/*
  Since TestValue001 is READ_WRITE variable, onTestValue001Change() is
  executed every time a new value is received from IoT Cloud.
*/
void onTestValue001Change()  {
  // Add your code here to act upon TestValue001 change
}
/*
  Since BatteryVoltage is READ_WRITE variable, onBatteryVoltageChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onBatteryVoltageChange()  {
  // Add your code here to act upon BatteryVoltage change
}
/*
  Since LightOn is READ_WRITE variable, onLightOnChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLightOnChange()  {
  lightOnInt = lightOn ? 1:0;
  // Add your code here to act upon LightOn change
}
/*
  Since WaterFlowRate is READ_WRITE variable, onWaterFlowRateChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onWaterFlowRateChange()  {
  // Add your code here to act upon WaterFlowRate change
  Serial.println("Water flow rate updated:" + String(water_flow_rate) + "L/min");
}
/*
  Since WaterFlow02 is READ_WRITE variable, onWaterFlow02Change() is
  executed every time a new value is received from IoT Cloud.
*/
void onWaterFlow02Change()  {
  // Add your code here to act upon WaterFlow02 change
  Serial.println("Water flow rate 2 updated:" + String(water_flow_02) + "L/min");
}
/*
  Since TotalWaterPerDay is READ_WRITE variable, onTotalWaterPerDayChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onTotalWaterPerDayChange()  {
  // Add your code here to act upon TotalWaterPerDay change
}
/*
  Since TotalWaterPerWeek is READ_WRITE variable, onTotalWaterPerWeekChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onTotalWaterPerWeekChange()  {
  // Add your code here to act upon TotalWaterPerWeek change
}
/*
  Since CommsCount01 is READ_WRITE variable, onCommsCount01Change() is
  executed every time a new value is received from IoT Cloud.
*/
void onCommsCount01Change()  {
  // Add your code here to act upon CommsCount01 change
}
/*
  Since WaterUseHigh is READ_WRITE variable, onWaterUseHighChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onWaterUseHighChange()  {
  // Add your code here to act upon WaterUseHigh change
}

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